Why to add Security HTTP Headers ?

Security HTTP Headers helps your website from attacks such as Clickjacking, code injection, MIME types, Cross Site Scripting (XSS), etc. A typical attack can take place when you include Javascript from a Third-Party Site. If somehow the Javascript from the domain/site you are including in your website is compromized, the script may try to send data/load data from attackersite.com.

These scripts may request access to hardware components available via web browser such as Camera, Microphone, Location, Motion Sensors, etc , voiding the security of users using the site.


How to add to Security Headers to Zeit ?

Zeit/Vercel supports adding Custom Headers to deployments by adding it as a list of header definitions in now.json

Header object definition:

  • source: A pattern that matches each incoming pathname (excluding querystring).
  • headers: An array of key/value pairs representing each response header.

example now.json

{
    "headers": {
        "cache-control": "s-maxage=604800",
        "X-Frame-Options": "sameorigin",
        "X-Content-Type-Options": "nosniff",
        "Feature-Policy": "microphone 'none'; camera 'none'; vibrate 'none'; payment 'none'; gyroscope 'none'; push 'none'; geolocation 'none'",
        "x-xss-protection": "1; mode=block",
        "Referrer-Policy": "no-referrer-when-downgrade",
        "Content-Security-Policy": "default-src 'self'; child-src 'self'; font-src 'self'; form-action 'self'; frame-src 'self'; img-src 'self'; object-src 'none'; style-src 'self' 'unsafe-inline'; script-src 'self' 'sha256-blablah=' "
    }
}

If you are using external domains to include CSS / JS into,

{
  "headers": {
    ...
    "Content-Security-Policy": "... style-src 'self' 'unsafe-inline' cdn.domain.com; ... "
    ...
  }
}
{
  "headers": {
    ...
    "Content-Security-Policy": "...  script-src 'self' cdn.domain.com  ... "
    ...
  }
}

Adding hashes for static files such as .css and .js

After applying the above now.json you may notice that scripts are blocked with a bare bones CSP blocked scripts in console! You can see the error message in Web-Dev tools -> Console.

Copy SHA-256 hash in that error message and change the CSP to do this, make sure these hashed are in between single quotes 'sha256-blablah='

{
  "headers": {
    ...
    "Content-Security-Policy": "...  script-src 'self' cdn.domain.com 'sha256-blablah=' ... "
    ...
  }
}

How to add Custom 404

Here we rely on the filesystem, providing a custom error page if there are no matches, precedence is given to the filesystem, routing only to /404 if there is no match.

In now.json add the following

{
    "version": 2,
    "builds": [{ "src": "package.json", "use": "@now/static-build" }],
    "routes": [
        {
            "src": "/(.*)",
            "dest": "/$1"
        },
        { "handle": "filesystem" },
        {
            "src": "/.*",
            "status": 404,
            "dest": "/404.html"
        }
    ]
}

Checking Your Security HTTP Headers

Visit https://securityheaders.com/ to analyze your site.


Final now.json may look like

{
    "version": 2,
    "builds": [{ "src": "package.json", "use": "@now/static-build" }],
    "routes": [
        {
            "src": "/(.*)",
            "headers": {
                "cache-control": "s-maxage=604800",
                "X-Frame-Options": "sameorigin",
                "X-Content-Type-Options": "nosniff",
                "Feature-Policy": "microphone 'none'; camera 'none'; vibrate 'none'; payment 'none'; gyroscope 'none'; push 'none'",
                "x-xss-protection": "1; mode=block",
                "Referrer-Policy": "no-referrer-when-downgrade",
                "Content-Security-Policy": "default-src 'self'; child-src 'self'; font-src 'self'; form-action 'self'; frame-src 'self'; img-src 'self' cdn.domain.com; object-src 'none'; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-eval' 'sha256-blablah=' "
            },
            "dest": "/$1"
        },
        { "handle": "filesystem" },
        {
            "src": "/.*",
            "status": 404,
            "dest": "/404.html"
        }
    ]
}

Refrerences: