Ctrl K
ComunidadGuías…

next.config.js

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
/** @type {import('next').NextConfig} */
const nextConfig = {
  output: "standalone",
  cacheComponents: true,
  partialPrefetching: true,
  // Off, or Next sends `X-Powered-By: Next.js` on every response by default.
  // It names the framework and nothing else, which is free reconnaissance for
  // anyone scanning a self-hosted instance for a version worth an exploit, and
  // it is bought with nothing: no code here, in the API, or in the browser ever
  // reads it.
  poweredByHeader: false,
  // No `images.remotePatterns` on purpose. Nothing in the app uses
  // `next/image` - private card images go through `useAuthedBlobUrl` and a
  // plain `<img>` (see `certification-card-image.tsx`), because they need an
  // `Authorization` header the image optimizer can't send. An entry here
  // wouldn't just be dead config: `/_next/image?url=...` makes the *server*
  // fetch whatever it allows, and a host with no `port`/`pathname` constraint
  // matches every port and path on that host, turning the optimizer into an
  // SSRF probe of whatever else is listening next to the app.
  experimental: {
    // Next's instant-navigation testing API, which `e2e/` drives through
    // `@next/playwright`'s `instant()`. It is a *build-time* define - the
    // compiler substitutes `process.env.__NEXT_EXPOSE_TESTING_API` and the
    // client bundle branches on it - so a build made without this flag cannot
    // be tested by starting it with an environment variable set, and the e2e
    // job needs a build of its own rather than the one CI uploads.
    //
    // `playwright.config.ts` sets `NEXT_EXPOSE_TESTING_API` for the build it
    // starts; nothing else does, and the Dockerfile has no `ARG` for it, so a
    // published image cannot carry the API however the container is run.
    exposeTestingApiInProductionBuild:
      process.env.NEXT_EXPOSE_TESTING_API === "1",
  },
  async headers() {
    // Content-Security-Policy and Strict-Transport-Security are set per-request
    // by src/proxy.ts instead of here - the first needs a fresh, unpredictable
    // nonce on every response, and the second depends on whether the request
    // arrived over HTTPS and on a variable read at runtime. What is left below
    // is static: the same value for every request of every deployment, which is
    // what this build-time hook can honestly express.
    return [
      {
        source: "/:path*",
        headers: [
          { key: "X-Content-Type-Options", value: "nosniff" },
          // Legacy fallback for browsers that don't support the CSP
          // `frame-ancestors` directive set in proxy.ts.
          { key: "X-Frame-Options", value: "DENY" },
          { key: "Referrer-Policy", value: "strict-origin-when-cross-origin" },
          {
            // The camera and microphone off, and geolocation to this origin
            // alone: the site finder's *Use my location* asks on a tap, and no
            // frame of anyone else's may. Notably absent:
            // `publickey-credentials-get`/`-create`, which stay at their default
            // `self` so passkey ceremonies work (see `hooks/usePasskeySignIn.ts`).
            // Tightening this header later has to name them explicitly, or it
            // kills passkey sign-in with a browser-side error and no clue why.
            key: "Permissions-Policy",
            value: "camera=(), microphone=(), geolocation=(self)",
          },
        ],
      },
      // The two pages an emailed link lands on with a live token in its query.
      // `strict-origin-when-cross-origin` still sends the full URL to this origin,
      // so every chunk and API request the page makes before it strips the token
      // would carry it in `Referer`. A header rather than route metadata so it
      // governs from the first byte, ahead of the `<meta>`. Later entries win, so
      // these stay after the catch-all.
      ...["/auth/verify", "/settings/confirm-email"].map((source) => ({
        source,
        headers: [{ key: "Referrer-Policy", value: "no-referrer" }],
      })),
    ];
  },
};

module.exports = nextConfig;