Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions next.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,36 @@ import createNextIntlPlugin from "next-intl/plugin";

const withNextIntl = createNextIntlPlugin();

// Baseline security headers applied to every response. Deliberately no
// Content-Security-Policy here: a correct CSP for this app (inline theme
// script, Google Fonts, Analytics, Turnstile, map tiles) needs to be built
// and tested against the running app, so it is left as a dedicated follow-up
// rather than shipped as a guess that could break rendering.
const securityHeaders = [
// Force HTTPS for two years, including subdomains, and allow preload-list
// submission.
{
key: "Strict-Transport-Security",
value: "max-age=63072000; includeSubDomains; preload",
},
// Stop browsers from MIME-sniffing responses away from the declared type.
{ key: "X-Content-Type-Options", value: "nosniff" },
// Disallow framing by other origins (clickjacking protection).
{ key: "X-Frame-Options", value: "SAMEORIGIN" },
// Send only the origin on cross-origin navigations; full URL same-origin.
{ key: "Referrer-Policy", value: "strict-origin-when-cross-origin" },
// Explicitly deny powerful features the site does not use.
{
key: "Permissions-Policy",
value: "camera=(), microphone=(), geolocation=(), browsing-topics=()",
},
];

const nextConfig: NextConfig = {
output: "standalone",
async headers() {
return [{ source: "/:path*", headers: securityHeaders }];
},
};

export default withNextIntl(nextConfig);
Loading