CSP Headers for Gatsby project on Netlify
So I want CSP to be implemented.
In fact, there is already one official plugin that can help you do this:
Well done, it even generates the CSP sha-256 CSP hashes for you. But that’s not perfect, since it only injects the http-equiv <meta/> tag for you. In case you are a fan of CSP, you must know that Observatory does not support CSP in meta tags for now. Anyway, setting CSP in HTTP headers is better since there are some tags (e.g. frame-ancestors could not be set in <meta/>.
On the other hand, (oh by the way this site is hosted with Netlify), there is a Gatsby plugin for generating some Netlify-specific “server-side” config scripts, namely _headers and _redirects :
Then I found that the transformHeaders: (headers, path) option seems to be configurable for my use case.
So what does my hack do?
- the
gatsby-plugin-cspgenerates the CSP hashes, build the CSP string and inject it as<meta/>in each generated HTML file, in the./publicdirectory by default. - the
gatsby-plugin-netlifyloops over each path, and thetransformHeadersmethod is called upon processing of each page. You can manually update theheadersargument according to thepathargument. This is where the hack happens:- read each generated HTML file
- extract the
contentvalue of the tag<meta name="Content-Security-policy" /> - update the variable
headerswith the CSP header - remove the CSP meta tag in the original HTML file
That’s all! No need to hack the original two plugins at all while you don’t have to struggle writing a new Gatsby plugin from scratch.
/* gatsby-config.js */
module.exports = {
plugins: [
{
resolve: `gatsby-plugin-csp`,
options: {
disableOnDev: false,
reportOnly: false,
mergeScriptHashes: true,
mergeStyleHashes: true,
mergeDefaultDirectives: true,
},
},
{
resolve: `gatsby-plugin-netlify`,
options: {
transformHeaders: (headers, path) => {
if (path.endsWith('/')) {
const filePath = `./public${path}index.html`;
const rawHtml = readFileSync(filePath).toString();
const csp = /<meta http-equiv="Content-Security-Policy" content="(.*?)"\/>/.exec(rawHtml)[1].replace(/'/g, `'`);
headers.push(`Content-Security-Policy: ${csp}`);
writeFileSync(filePath, rawHtml.replace(/<meta http-equiv="Content-Security-Policy" content=".*?"\/>/g, ''));
}
return headers;
},
mergeSecurityHeaders: true,
mergeLinkHeaders: true,
mergeCachingHeaders: true,
generateMatchPathRewrites: true,
},
},
],
};