Files
zitadel/apps/login/next.config.mjs
Nils 5c88576c9e fix(loginV2): Disable image optimization (#10508)
<!--
Please inform yourself about the contribution guidelines on submitting a
PR here:
https://github.com/zitadel/zitadel/blob/main/CONTRIBUTING.md#submit-a-pull-request-pr.
Take note of how PR/commit titles should be written and replace the
template texts in the sections below. Don't remove any of the sections.
It is important that the commit history clearly shows what is changed
and why.
Important: By submitting a contribution you agree to the terms from our
Licensing Policy as described here:
https://github.com/zitadel/zitadel/blob/main/LICENSING.md#community-contributions.
-->

# Which Problems Are Solved

Next.js's Image Optimization feature requires that hostnames for remote
images be explicitly defined in the `next.config.js` file via
`remotePatterns`. This configuration is static and evaluated at **build
time**.

However, the `ZITADEL_API_URL`, which is supposed to be used for
additional whitelisted hostnames, is a dynamic environment variable only
known at **run time**. This creates a fundamental conflict, making it
impossible to add the user-provided URL to the configuration when
building the public Docker image. Consequently, images like instance
logos fail to load.

The existing workaround uses a permissive wildcard pattern
(`*.zitadel.*`). This is a significant security risk, as it could allow
malicious actors to abuse the server as an open image-resizing proxy,
leading to potential denial-of-service (DDoS) attacks or excessive
costs.

# How the Problems Are Solved

This change disables the Next.js Image Optimization feature entirely by
setting `unoptimized: true` in the `images` configuration.

By doing this, Next.js will no longer attempt to optimize, cache, or
validate remote image sources. Instead, it will pass the original image
URL directly to the client. This approach resolves the issue by:

1. **Eliminating the need for `remotePatterns`**, which bypasses the
build-time vs. run-time configuration conflict.
2. **Improving security** by removing the overly permissive wildcard
pattern.
3.  **Ensuring functionality**, as remote images now load correctly.

The trade-off is the loss of performance benefits from Next.js image
optimization, but I see this as an acceptable compromise to restore
essential functionality and secure the application.

Fixes #10456

Co-authored-by: Max Peintner <max@caos.ch>
(cherry picked from commit 7a9cc5c456)
2025-08-25 16:04:57 +02:00

60 lines
1.2 KiB
JavaScript
Executable File

import createNextIntlPlugin from "next-intl/plugin";
import { DEFAULT_CSP } from "./constants/csp.js";
const withNextIntl = createNextIntlPlugin();
/** @type {import('next').NextConfig} */
const secureHeaders = [
{
key: "Strict-Transport-Security",
value: "max-age=63072000; includeSubDomains; preload",
},
{
key: "Referrer-Policy",
value: "origin-when-cross-origin",
},
{
key: "X-Frame-Options",
value: "SAMEORIGIN",
},
{
key: "X-Content-Type-Options",
value: "nosniff",
},
{
key: "X-XSS-Protection",
value: "1; mode=block",
},
{
key: "Content-Security-Policy",
value: `${DEFAULT_CSP} frame-ancestors 'none'`,
},
{ key: "X-Frame-Options", value: "deny" },
];
const nextConfig = {
basePath: process.env.NEXT_PUBLIC_BASE_PATH,
output: process.env.NEXT_OUTPUT_MODE || undefined,
reactStrictMode: true, // Recommended for the `pages` directory, default in `app`.
experimental: {
dynamicIO: true,
},
images: {
unoptimized: true
},
eslint: {
ignoreDuringBuilds: true,
},
async headers() {
return [
{
source: "/:path*",
headers: secureHeaders,
},
];
},
};
export default withNextIntl(nextConfig);