jagaapple / next-secure-headers

Sets secure response headers for Next.js.
MIT License
310 stars 13 forks source link

Using i18n in next.config.js will cause secure headers to disappear #55

Open websaid opened 2 years ago

websaid commented 2 years ago

💩 Bug Report

A summary of the bug

Using i18n internationalization will cause secure headers to disappear

Current behavior

If you have this in your next.config.js:

i18n: {
  defaultLocale: "de",
  locales: ["de"]
},

Your headers will not be used.

To Reproduce

Steps to reproduce the behavior, please provide code snippets or a repository.

  1. add this to your next.config.js alongside your headers configuration i18n: { defaultLocale: "de", locales: ["de"] },
  2. npm run build & npm run start
  3. Check network tab, the CSP header wont be displayed
  4. Remove i18n, recompile & restart, CSP Headers are back there

Expected behavior

CSP Headers to work when i18n locales are set.

Environment

Additional context

The headers config i used:

  async headers() {
    return [{
      source: "/(.*)",
      headers: createSecureHeaders({
        contentSecurityPolicy: {
          directives: {
            defaultSrc: "'self'",
            imgSrc: ["https://*", "'self'", "data:"],
            styleSrc: ["'self'", "https://use.typekit.net/", "'unsafe-inline'"],
            fontSrc: "https://use.typekit.net/",
            scriptSrc: ["'self'", process.env.NODE_ENV === "development" ? "'unsafe-eval'" : ""],
            scriptSrcElem: "'self'"
          }
        }
      })
    }]
  }

mauritsl commented 2 years ago

I had the same issue. When i18n is enabled, the path for the homepage is "/en", but the language prefix is stripped before matching the path but doesn't contain a trailing slash anymore. Hence an empty string is matched against /(.*).

The solution is to add locale: false to prevent NextJS from stripping the language prefix. I would suggest adding this to the code in the readme. Setting this option has no effect when i18n is not in use.

Config example with this fix:

module.exports = {
  i18n: {
    locales: ['en'],
    defaultLocale: 'en',
  },
  async headers() {
    return [
      {
        locale: false,
        source: '/(.*)',
        headers: createSecureHeaders({
          frameGuard: 'deny',
          nosniff: 'nosniff',
          referrerPolicy: 'same-origin',
        })
      },
    ];
  }
};

Note that we cannot omit the slash from the pattern as an alternative solution because NextJS fails to start in that case.

mauritsl commented 2 years ago

It would be good if this could be reviewed and updated in the readme soon. When enabling i18n later on, the security headers' absence may go unnoticed, hence putting the site at risk.