# CSP Headers for Gatsby project on Netlify

> Content Security Policy (CSP) is really important, but troublesome too...

- Author: Gabriel Koo (AWS Community Builder)
- Published: 2019-09-28
- Topics: content security policy, CSP, gatsby, gatsby-plugin-csp, gatsby-plugin-netlify, mozilla observatory
- HTML: https://gabrielkoo.com/blog/gatsby-netlify-csp-headers/
- Original canonical URL: 

So I want CSP to be implemented.

In fact, there is already one official plugin that can help you do this:

**[gatsby-plugin-csp](https://www.gatsbyjs.org/packages/gatsby-plugin-csp/)**

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](https://observatory.mozilla.org) 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` :

**[gatsby-plugin-netlify](https://www.gatsbyjs.org/packages/gatsby-plugin-netlify/)**

Then I found that the `transformHeaders: (headers, path)` option seems to be configurable for my use case.

So what does my hack do?

1. the `gatsby-plugin-csp` generates the CSP hashes, build the CSP string and inject it as `<meta/>` in *each* generated HTML file, in the `./public` directory by default.
2. the `gatsby-plugin-netlify` loops over each path, and the `transformHeaders` method is called upon processing of each page. You can manually update the `headers` argument according to the `path` argument. This is where the hack happens:
   - read each generated HTML file
   - extract the `content` value of the tag `<meta name="Content-Security-policy" />`
   - update the variable `headers` with 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.

```javascript
/* 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(/&#x27;/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,
      },
    },
  ],
};
```
