Multiline CSP headers are joined incorrectly


I am seeing an unexpected (for me at least) behaviour when setting my CSP headers in my _headers file.
Since my CSP list is quite long I’d like to break it down to multiple line like so (this is a stripped down version, just an example):

  X-XSS-Protection: 1; mode=block
  Content-Security-Policy: default-src 'self'; 
  Content-Security-Policy: script-src 'self' 'unsafe-inline';

This validates on like so:

for = "/*"
Content-Security-Policy = [
"default-src 'self';",
"script-src 'self' 'unsafe-inline';"
X-XSS-Protection = "1; mode=block"

And I would expect to see the values of the CSP fold to a single value like so:

Content-Security-Policy:  default-src 'self'; script-src 'self' 'unsafe-inline';

Which is a valid CSP directive[]=default-src+‘self’%3B+script-src+‘self’+‘unsafe-inline’

If I try to deploy that though, here’s the result I see in my headers:

default-src 'self';, script-src 'self' 'unsafe-inline';

(x-nf-request-id: 5f73cde1-aac6-4bd8-b0a9-ee2edf06b76b-487459)

Notice the comma after the first semicolon. That makes the policy invalid:[]=default-src+‘self’%3B%2C+script-src+‘self’+‘unsafe-inline’

Even removing my semicolons will still make it invalid, because the result would be:

default-src 'self', script-src 'self' 'unsafe-inline'

And the separator should be ;.

This is not a blocker for me, I solved this by declaring everything on one line.
Still it would be nice to have a way to spread the directive on multiple lines to make both reading and diffs easier to scan.

Let me know if I’m doing anything wrong or if I can provide more details.
Many thanks!

Hmm, why not remove the comma in your spec? I’m not sure if that feature is intended to work without the commas, since the example in our docs is one where the commas are syntactically required and desired (from You could also just try porting to the below quoted format as it is the one our tests use:

  for = "/*"
	cache-control = '''

why not remove the comma in your spec?

I am not adding commas (they are invalid in csp directives), it’s Netlify that’s adding them.
I think that by default Netlify should just join without any separator, so a user would be able to use - or not use - the correct separator that goes along with that header.

Just taking your example as a base, one would have to write a _headers file such as:

Cache-control: max-age=0,
Cache-control: no-cache,
Cache-control: no-store,
# so on..

Note that the commas are added explicitly by the user. That would be the ideal scenario in my view.

I realise this would be a breaking change, so it’s alright, but it’s still worth logging this as an issue someone else might encounter.

For the record I ended up using a .toml file because it played better with multi lines + just happened to be a better option in my particular case.

Let me know if I need to expand on any of the above, other than that thanks for the reply.

netlify doesn’t change your netlify.toml, which is where the comments were:

Content-Security-Policy = [
"default-src 'self';",
"script-src 'self' 'unsafe-inline';"

That is where I was suggesting removing them :slight_smile:

Also, I suggested converting to the “```” style quoting instead of your style with “[” and “]” to contain the multi-line syntax - how did that go?

I see what you mean. To clarify, my original issue was not related to the .toml file, but to the _header file.
I didn’t have a toml file at that time.

if I want to use just a headers file and write it like so:

  Content-Security-Policy: default-src 'self'; 
  Content-Security-Policy: script-src 'self' 'unsafe-inline';

Netlify will add the commas to the HTTP headers that are delivered to live site.

This is a screenshot with a _headers file with the configuration laid out as above:

I then resolved by deleting my headers file and using a toml, where I could successfully use multilines and - as you noted - there’s no comma insertion.

If you paste the code above here you will see that the generated output is with added commas.
Again, I resolved by using a .toml but I think the original issue still stands.

Yes, that playground is not so great - sorry it led you astray! When you do it instead as the docs describe, I think that things work as intended. (The docs here do indicate that the commas are added because that is how the RFC suggests merging multiple values: I understand that in your case it may have been a single value that you split up, but our implementation is intended to fit the “multiple values” intention.

I’ll work with the documentation team to see if we can make things clearer in the docs!