Generate
Content-Security-Policy
headers from inline script and style hashes
When running things like Gatsby or Gridsome, the initial state is stored inside a <script>
tag.
Modern browser content security policies don't like inline scripts or styles, so to get around it you need to add either a cryptographic nonce or a cryptographic hash of each script.
A nonce is out of the question, because you can't update it for each load.
This package generates a crypographic hash (SHA-256) of all inline scripts and styles in each HTML file, and adds it to the _headers
file along with other policies of your choice.
Note
Netlify lets you add aContent-Security-Policy
header in yournetlify.toml
. This will overwrite values inside_headers
, so don't do that.
If you have an existing _headers
file, this will append to the existing file. Just make sure the file ends on a newline, and it should work fine.
Install netlify-plugin-csp-generator
with your favourite package manager:
yarn add netlify-plugin-csp-generator
npm install netlify-plugin-csp-generator
In your netlify.toml
file, add an additional plugin:
[[plugins]]
package = "netlify-plugin-csp-generator"
[plugins.inputs]
buildDir = "dist"
[plugins.inputs.policies]
defaultSrc = "'self'"
buildDir
is the path for the publish directory in Netlify:exclude
is an array of paths you don't want to include. It defaults to an empty array. See Excluding files and folders for more information.disablePolicies
is an array of policies to never include. Files that need these rules will probably be taken fromdefaultSrc
instead by your browser.disableGeneratedPolicies
is an array of policies never to generate. Use this to turn off default policies but still allow the key innetlify.toml
.reportOnly
generates headers withContent-Security-Policy-Report-Only
instead, which is useful for testing.reportURI
/reportTo
sends violations to a given endpoint. See Reporting violations for more information.generateForAllFiles
lets you generate headers for non-HTML files. See Non-index.html files for more information.debug
is a boolean that logs the file paths if set totrue
. Use this if you are struggling to match paths in your app.
You can use the following policies:
childSrc
defaultSrc
connectSrc
fontSrc
frameSrc
imgSrc
manifestSrc
mediaSrc
objectSrc
prefetchSrc
scriptSrc
scriptSrcElem
scriptSrcAttr
styleSrc
styleSrcElem
styleSrcAttr
workerSrc
baseUri
formAction
frameAncestors
Add them under the [plugins.inputs.policies]
object in your netlify.toml
file, with your specified value in quotes.
You can use CSP headers not in this list too - simply use the name in camel case and it will be added.
When using Vue and derivatives (like Gridsome), you may want to use v-show
on things. This adds an inline style of display: none;
, which is forbidden by CSP Level 3. To prevent this throwing an error, you need to add 'unsafe-hashes'
to your styleSrc
policy. The sha-256
hash is generated automatically.
[[plugins]]
package = "netlify-plugin-csp-generator"
[plugins.inputs]
buildDir = "dist"
[plugins.inputs.policies]
defaultSrc = "'self'"
styleSrc = "'unsafe-hashes'"
If you have defined a policy in your netlify.toml
file, this will be added to all files.
[plugins.inputs.policies]
defaultSrc = "'self'"
scriptSrc = "'self' https://www.google-analytics.com https://ssl.google-analytics.com https://www.googletagmanager.com"
/each-file-path/
Content-Security-Policy: default-src 'self'; script-src 'self' *.google-analytics.com;
If a file includes a <script>
or <style>
tag with content, this file path will have the hash added:
/file-with-no-script/
Content-Security-Policy: default-src 'self';
/file-with-script/
Content-Security-Policy: default-src 'self'; script-src 'sha256-RFWPLDbv2BY+rCkDzsE+0fr8ylGr2R2faWMhq4lfEQc=';
If a file has any inline styles, these will be hashed:
<div style="display:none;"></div>
/file-with-inline-style/
Content-Security-Policy: style-src 'unsafe-hashes' 'sha256-0EZqoz+oBhx7gF4nvY2bSqoGyy4zLjNF+SDQXGp/ZrY='
If you want to exclude any files or folders from being included, add them to the exclude
array. Wildcard matching is provided by globby
, which enables advanced pattern matching.
[[plugins]]
package = "netlify-plugin-csp-generator"
[plugins.inputs]
buildDir = "dist"
exclude = ["/exclude-file.html", "/exclude-folder/**"]
Generally, routes are generated with an index.html
file, like /some/file/path/index.html
. However, sometimes you need to handle HTML files that aren't called 'index', for example 404.html
in Nuxt.
These are generated as wildcard links and are placed above the non-wildcard paths in your _headers
file (for specificity):
/*.html
Content-Security-Policy: default-src 'self'; script-src 'sha256-Qb2XxXiF09k6xbk2vTgHvWRed+mgYYGzFqZ6dShQVA0=';
/specific-path/
Content-Security-Policy: default-src 'self';
Any matching wildcard URL has the hashes joined together - for example, if you have a 404.html
and a 500.html
with scripts/styles, all the hashes will be merged together under /*.html
.
In general, it is better to generate
/path/index.html
rather than/path.html
.
Using the generateForAllFiles
setting, you can generate route keys that use /*
instead of /*.html
. Be careful, this will send Content-Security-Policy headers for every file type (i.e. .js
, .css
, etc) which is redundant as per the spec.
The Content-Security-Policy specification allows for reporting violations to a URL - you can read more about it on MDN.
This is useful for testing and checking directives.
To set the header to report only, set reportOnly = true
in your netlify.toml
alongside your policies.
[plugins.inputs]
reportOnly = true
reportURI = "/report-csp-violations-to-this-uri"
Important
- Setting
reportOnly
to true will NOT enforce your policy- You need to add
reportURI
too
The reportURI
is deprecated in CSP Level 3 in favour of report-to
. To use the report-to directive, set the reportTo
value to the group name as defined in the Reporting-Endpoints
header that you also need to set.
[plugins.inputs]
reportOnly = true
reportTo = "csp-violations-group"
- You can include
reportURI
andreportTo
without settingreportOnly = true
, and the policy WILL be enforced and errors will also be reported- You can set both the
reportTo
andreportURI
directives - this is recommended to ensure maximum compatibility
Oh, you. Chances are your browser console is screaming at you, and the network tab is showing a lot of (blocked:csp)
errors.
See our list of example policies to get started.
Don't
unsafe-inline
everything, because that will make CSP redundant. If in doubt, ask Google, Stackoverflow, or create a Github issue (in that order).
If you found this plugin useful, or are just feeling nice, feel free to donate!