Custom headers in netlify.toml not working

Hi, I’m having trouble getting custom headers working, I’m setting them in the netlify.toml file, but no matter what I try they don’t seem to come through. I’m currently using the example from the custom headers docs.

Any help would be massively appreciated, thanks!

[[headers]]
  for = "/*"
  [headers.values]
    X-Frame-Options = "DENY"
    X-XSS-Protection = "1; mode=block"

darrenjansson.netlify.app
deployId: 61b3bd5ea5a0cf000741b0c5

1 Like

Hi @darrenjansson

Welcome to the Netlify forums :netliconfetti:

Using curl I see:

% curl -IL https://darrenjansson.netlify.app
HTTP/2 200
cache-control: no-cache
content-type: text/html
etag: "1pbadzh"
permissions-policy: interest-cohort=()
age: 0
server: Netlify
x-nf-request-id: 01FPK300TG8QFSX246WCRT0P08
date: Fri, 10 Dec 2021 21:11:44 GMT
content-length: 0

So I agree, these headers are not present. If I use http:// not https:// I see the headers, however when it does the redirect to https:// they are missing.

% curl -IL http://darrenjansson.netlify.app
HTTP/1.1 301 Moved Permanently
cache-control: public, max-age=0, must-revalidate
content-length: 49
content-type: text/plain
date: Fri, 10 Dec 2021 22:25:54 GMT
x-xss-protection: 1; mode=block                  # <-- here
location: https://darrenjansson.netlify.app/
x-nf-request-id: 01FPK77T88TCZN5QNRC2NASCP9
x-frame-options: DENY                            # <-- here
age: 1
server: Netlify

HTTP/2 200
cache-control: no-cache
content-type: text/html
etag: "1xobezu"
permissions-policy: interest-cohort=()
age: 1
server: Netlify
x-nf-request-id: 01FPK77V0XGT1N9YYKQXKXB6Z8
date: Fri, 10 Dec 2021 22:25:55 GMT
content-length: 0

In an attempt to reproduce this, I deployed a basic site (using drag and drop, and deploy from git) with the same headers in a netlify.toml and also in _headers (instead of the toml) and in both cases I see the headers present regardless of accessing http:// (including on the redirect) or https:// (these headers don’t appear by default on Netlify as far as I know.)

% curl -IL http://61b3c5281a2fcc9faf7b6a56--sad-tesla-31c18c.netlify.app/
HTTP/1.1 301 Moved Permanently
cache-control: public, max-age=0, must-revalidate
content-length: 78
content-security-policy: style-src 'self' 'nonce-1baab3f43d'
content-type: text/plain
date: Fri, 10 Dec 2021 22:35:54 GMT
location: https://61b3c5281a2fcc9faf7b6a56--sad-tesla-31c18c.netlify.app/
x-xss-protection: 1; mode=block                  # <-- here
x-frame-options: DENY                            # <-- here
x-nf-request-id: 01FPK7T51Z2G14WHA5P31NFE7A
age: 2
server: Netlify
x-robots-tag: noindex

HTTP/2 200
cache-control: public, max-age=0, must-revalidate
content-length: 747
content-security-policy: style-src 'self' 'nonce-1baab3f43d'
content-type: text/html; charset=UTF-8
date: Fri, 10 Dec 2021 22:35:55 GMT
etag: "c0b056774c161a0b59e8720781654245-ssl"
strict-transport-security: max-age=31536000; includeSubDomains; preload
age: 0
x-robots-tag: noindex
server: Netlify
x-nf-request-id: 01FPK7T5CVA92MECG22D845YDR
x-frame-options: DENY                            # <-- here
x-xss-protection: 1; mode=block                  # <-- here

Do you have other headers present?
Is the netlify.toml in the root of the git repository or deploy directory (if using drag and drop)?
If you put them in the _headers file instead of netlify.toml do you see the same behaviour?
If you deploy to a new site, do you see the same behaviour?

So I’ve tried switching to a _headers file instead of using the netlify.toml and I get the same result. the headers only appear on the 301 redirect. I tried creating a new site and deploying to that, but get the same result as the other site. Headers are not shown on https.

% curl -IL https://elated-yonath-6f3b14.netlify.app/
HTTP/2 200 
cache-control: no-cache
content-type: text/html
etag: "f6sik6"
permissions-policy: interest-cohort=()
age: 0
server: Netlify
x-nf-request-id: 01FPSNKKT344BCDWWJ0MP6X047
date: Mon, 13 Dec 2021 10:32:27 GMT
content-length: 0
% curl -IL http://elated-yonath-6f3b14.netlify.app/
HTTP/1.1 301 Moved Permanently
cache-control: public, max-age=0, must-revalidate
content-length: 56
content-type: text/plain
date: Mon, 13 Dec 2021 10:32:07 GMT
x-xss-protection: 1; mode=block                   # <-- here but not the 200 response below
location: https://elated-yonath-6f3b14.netlify.app/
x-nf-request-id: 01FPSNK0VM2EVQ0RC1JBSBQ3Z6
x-frame-options: DENY                             # <-- here but not the 200 response below
age: 0
server: Netlify

HTTP/2 200 
cache-control: no-cache
content-type: text/html
etag: "16fz8pc"
permissions-policy: interest-cohort=()
age: 0
server: Netlify
x-nf-request-id: 01FPSNK1652AR29DMFMVG7VPS9
date: Mon, 13 Dec 2021 10:32:08 GMT
content-length: 0

These are the only headers I’m trying to use at the moment.

Are you able to share the repository you deployed this test site from?

Sure! GitHub - frontendbeast/darrenjansson.com

Thanks @darrenjansson.

As per headers documentation

Save a plain text file called _headers to the publish directory of your site

The _headers file is in the root of the project so does not make it into the publish directory (build in this case) so the headers never get processed. if you move the _headers file into the static directory, it is copied to the output (build) directory during build, and Netlify will the process the headers.

If you wanted to use the netlify.toml (as you have this file) to specify [[headers]] this remains in the root of the project.

The build command is setup to do that, and is working locally to copy it across. In netlify.toml I have

[build]
  command = "npm run build && cp _headers build/_headers"
  publish = "build"

Using the netfliy.toml to set the headers wasn’t much more successful so I’m not sure what’s going on.

Hi, @darrenjansson. I can solve the mystery for why the headers were not working for the deploy 61b3bd5ea5a0cf000741b0c5. The explanation is the first limitation listed here:

https://docs.netlify.com/routing/headers/#limitations

Quoting:

  • Custom headers apply only to files Netlify serves from our own backing store. If you are proxying content to your site or dealing with a URL handled by a function, custom headers won’t be applied to that content. In those cases, the site being proxied to or the serverless function should return any required headers instead.

The root path / was being handled by a function for that specific deploy. Because the function was replying, the function itself must be the source of any custom headers. The header rules in netlify.toml or _headers will never apply to a function response.

If there are other questions about this, please let us know.

2 Likes