NextJS build ignoring headers set in netlify.toml

Exact same problem on my end ! Would be great to have a fix soon !

1 Like

Hey there, folks! Thanks for writing in and sharing this feedback. I am taking these questions to a member of our team so that they can dig in further. I appreciate your patience.

2 Likes

Thanks @hillary, do you know what the eta might be on this?

Hey there, @justincavery :wave:

I have a meeting about this today, actually! Stay tuned! Thanks for following up :slight_smile:

1 Like

A visual demonstration for your meeting :slight_smile:

Homepage :sob:

Any other page :grinning:

1 Like

Hey there, folks! Thanks for your patience here.

I have brought this issue to the Director of Support as well as some NextJS pros at Netlify. Below I have outlined information relevant to the questions above:

  1. Though when running locally _headers could cover such routes, it is intended at Netlify that custom _headers in that file or in netlify.toml do not cover routes we proxy to another service, we route traffic to functions via a reverse proxy. This limitation is described in our documentation. I encourage you to take a look!

  2. Much of the less-static content in next.js sites is served via functions.

  3. If you want all of those functions to use your headers, you’d need to return all of their headers and other HTTP response from the lambda itself. Essentially, the headers would need to be added to the response from those functions directly, at run time.

  4. We currently don’t support tailoring responses using next.config.js . There is an open issue on the topic in our codebase to implement this, here: Support experimental next.config.js redirects and rewrites · Issue #150 · netlify/netlify-plugin-nextjs · GitHub. As this is open source, feel free to chime in on the issue.

  5. Lastly, I don’t have any great suggestions about “how to add those headers to those lambdas’ code”. I wish I had better news! However, I can tell you that brainstorming in that issue will have the eyes of the dev team for our next implementation, in case any method today can be found. But, our Support team, after consulting with the lead of that dev team about this thread, do not know of one.

Thanks again for your patience on this. I will keep my eyes peeled for responses to this thread!

It’s a shame this feature isn’t available. It basically prevent HSTS support on Netlify, which is the new security standard for modern websites.
No Netlify website can be on Google’s HSTS preload list (which is also used by Firefox & Safari) then. For reference, i’m talking about https://hstspreload.org/.

This also make this whole section of the documentation false, which mean that this was written without being tested at any point (at least for NextJS).

Finally, it makes it impossible to implement tons of security rules on our websites. This gives terrible scores on security-related checking tools, like https://observatory.mozilla.org. To name a few security rules that we can’t define:

  • Content Security Policy. Makes us vulnerable on cross-site scripting (XSS) and clickjacking attacks.
  • X-Content-Type-Options. Makes us vulnerable on XSS attacks.
  • X-Frame-Options. Makes us vulnerable on clickjacking attacks, allowing malicious sites to trick users into clicking links on our site (even though they may appear to not be on our site at all). To cite MDN:

As such, the use of the X-Frame-Options header is mandatory for all new websites, and all existing websites are expected to add support for X-Frame-Options as soon as possible.

  • X-XSS-Protection. Stops pages from loading when they detect reflected cross-site scripting (XSS) attacks. Mostly uneccessary when Content-Security-Policy is defined, but we can’t define it :slight_smile:

On the other end, never had the same problem with Vercel. The security implications are huge for any NextJS website hosted on Netlify. But you want a few weeks to brainstorm the issue? There is no issue to brainstorm, it’s a security problem that requires immediate fix.

This isn’t up to my client’s security standards, so I will move on to Vercel for the time being. Thanks for the quick answer though.

I agree with the comments from @TheMrZZ , however I’m not convinced this is a widespread issue that can’t be solved.

I’ve used this repo GitHub - cassidoo/next-netlify-starter: A one-click starter project for Next and Netlify, moved to https://github.com/netlify-templates/next-netlify-starter to get started with a next.js site and it is hosted here https://stupefied-boyd-1f4090.netlify.app/

By default, the netlify.toml file has the following

[build]
  command = "npm run build"
  publish = "out"

however I updated that with the same details that I have in my other toml file for the site headers I’m having issues with, and the new file now looks like this

[build]
  command = "npm run build"
  publish = "out"


[[headers]]
  for = "/*"
  [headers.values]
    X-Frame-Options = "DENY"
    X-XSS-Protection = "1; mode=block"
    Content-Security-Policy = "default-src 'self'; script-src 'self' 'unsafe-inline' www.googletagmanager.com www.google-analytics.com; connect-src 'self' nftx.ethereumdb.com api.opensea.io api.covalenthq.com www.google-analytics.com; img-src 'self' cms.nftx.xyz res.cloudinary.com lh3.googleusercontent.com storage.opensea.io www.google-analytics.com; style-src 'self' 'unsafe-inline' ;base-uri 'self';form-action 'self'; font-src 'self'; prefetch-src 'self' 'unsafe-inline'; manifest-src 'self'"

With this change and a refresh on that site I now have the right headers set for optimal security on the landing page, however, on my original site the first view doesn’t have the security headers but the subsequent pages viewed do contain them.

The difference between the two implementations seems to be only the [build] command that comes shipped with the project.

Hey there @TheMrZZ :wave:

Thanks for your patience with this!

To clarify, this only affects next.js customers, not every site on Netlify! You are correct in your assertion that as of today, we cannot support HSTS on next-functions-at-the-root sites.

As I shared before, there is an open issue for redirects and rewrites that is open source. Please feel free to chime in or follow along: Support next.config.js redirects, rewrites, headers · Issue #150 · netlify/next-runtime · GitHub

so is there an actual solve for the homepage route + security headers on a nextjs site?

Did you check the above link? It’s already supported at this point.

which above link? that netlify-nexjs link?

we see the same issues that were originally posted, any security headers in next.config or netlify.toml are not present on the index (home page) when hitting ‘/’ if we hit ‘/index.html’ or any other route they are.

The link to this issue: Support next.config.js redirects, rewrites, headers · Issue #150 · netlify/next-runtime · GitHub.

If it’s still not working, we would need to know your site as a bare minimum.

Hey @enginedigital,

When I visited the URL you shared, I can see the headers:

That’s on the HTML file. Are you sure this is not working?

I have received your DM and from what I can see, this seems to be a problem with the 304 header. A 200 response correctly includes the headers in your netlify.toml, but as soon as it’s a cached (304) response, it is missing those headers.

Sounds like a bug to me. We will file an escalation for the devs to investigate this.

Ah thanks yes i do see the headers in incognito browser. Thanks for escalating.

Hi, @enginedigital. It is intentional that the 304 response does not have the other headers. The 304 is saying “use your locally cached version of this response”. The local system will then use the headers of the original 200 response that was cached.

This is covered in the RFCs for the HTTP protocol. The section about 304 responses can be found here:

https://www.rfc-editor.org/rfc/rfc2616#section-10.3.5

That says that only three types of headers are mandatory (MUST) for 304 responses:

  • Date, unless its omission is required by section 14.18.1
  • ETag and/or Content-Location, if the header would have been sent in a 200 response to the same request
  • Expires, Cache-Control, and/or Vary, if the field-value might differ from that sent in any previous response for the same variant

For other headers it says:

If the conditional GET used a strong cache validator (see section 13.3.3), the response SHOULD NOT include other entity-headers.

I hope this explains why the headers are missing on the 304 response.

1 Like

Excellent thanks for the explanation!

1 Like

I cannot for the life of me get a CSP header set on the root page of my nextjs/netlify app.

next.config.js:

    async headers() {
      return [
        {
          source: '/',
          headers: [
            {
              key: 'Content-Security-Policy',
              value: "default-src 'self';",
            },
          ]
        }
      ];
    },

netlify.toml

[[headers]]
  for = "/*"
  [headers.values]
    Content-Security-Policy = "default-src 'self';"

_headers

/*
  Content-Security-Policy: default-src 'self';

Any guidance? thx

Hey @wildabeast , I’m wondering if you can try this with a absolute URL instead of just “self” and let us know if that works? The example in the file-based configuration section of our Help Docs shows this [File-based configuration | Netlify Docs].