Skip to main content

By Rachel Opperman

An Overview of Security Headers

It goes without saying that we want the websites we build to be secure.

If you want to strengthen the security of your websites, adding some security headers is a great step to take, especially since it can be easy and inexpensive to do.

These HTTP headers get exchanged between the browser and the server and dictate how the two can communicate with each other. This can help mitigate common attacks, like cross-site scripting (XSS) and clickjacking.

If you're not overly familiar with security headers, worry not. There are some helpful tools out there such as and Mozilla Observatory. These are free and fast ways to spot check your site's headers and provide recommendations to strengthen them.

In this post, we'll dive into six security headers, their purpose, and how to apply them. As a bonus, we'll provide an example of how to add them to a site hosted on Vercel.

Site content restriction headers

X-Frame-Options header

The X-Frame-Options header is used to indicate whether or not a browser should be allowed to render a page in the following HTML elements:

  • <frame>
  • <iframe>
  • <embed>
  • <object>

It can be used to prevent clickjacking attacks by ensuring that site content is not embedded into other sites.

X-Frame-Options syntax
X-Frame-Options: DENY

When the header is given a value of DENY, the page cannot be displayed in a frame.

X-Frame-Options: SAMEORIGIN

When it's given a value of SAMEORIGIN, the page can be displayed in a frame, but only on the same origin as the page itself.

X-Content-Type-Options header

The X-Content-Type-Options header is used to indicate that the MIME types in the Content-Type headers should be followed and not be changed. This helps to prevent MIME sniffing because it specifies that MIME types are deliberately configured.

X-Content-Type-Options syntax
X-Content-Type-Options: nosniff

This configuration will block a request if the request destination is of type:

  • style and MIME type is not text/css
  • script and MIME type is not a JavaScript MIME type

Domain resolution control headers

X-DNS-Prefetch-Control header

The X-DNS-Prefetch-Control header controls, as its name suggests, DNS prefetching.

DNS prefetching is a feature by which browsers proactively perform domain name resolution on both links that the user may choose to follow and URLs for items referenced by the document (images, CSS, JavaScript, etc.). It’s performed in the background and reduces latency when a user clicks a link.

X-DNS-Prefetch-Control syntax
X-DNS-Prefetch-Control: on

A value of on will enable DNS prefetching, which is the default browser behavior if the header isn't present.

X-DNS-Prefetch-Control: off

A value of off will disable DNS prefetching. This configuration is useful if you don't control the links on the page or you don't want to leak information to those domains.

Strict-Transport-Security header

The Strict-Transport-Security header informs browsers that the site should only be accessed using HTTPS and that any future attempts to access it using HTTP should be automatically converted to HTTPS.

Using this header is more secure than configuring an HTTP to HTTPS 301 redirect on the server, since in that case, the initial HTTP connection would still be vulnerable to a man-in-the-middle attack.

When using a redirect, visitors may initially communicate with the non-encrypted version of the site before being redirected. The redirect could be exploited to direct users to a malicious site instead of the secure version of the original site.

Strict-Transport-Security syntax
Strict-Transport-Security: max-age=<expire_time>

<expire_time> is the time in seconds that the browser should remember that a site is only to be accessed using HTTPS.

Strict-Transport-Security: max-age=<expire_time>; includeSubdomains

Specifying the optional includeSubdomains parameter will apply the rule to all of the site's subdomains.

User privacy protection headers

Referrer-Policy header

The Referrer-Policy header controls how much referrer information should be included with requests. Referrer information is sent with the Referer header (this may look like a typo, but it's actually a misspelling that was set in stone back in 1996).

Some examples of referrer information are:

  • The address of the previous web page that a user was on when they clicked a link for the current page
  • The address of a page that's loading an image or some other resource

Sharing such information can be a security problem, so the Referrer-Policy header can be used to prevent it.

Referrer-Policy syntax

There are several different values that can be provided for this header. Let's examine each one.

Referrer-Policy: no-referrer

Providing the no-referrer value will result in the Referer header being omitted, so the sent request will not include any referrer information.

Referrer-Policy: no-referrer-when-downgrade

This configuration will prevent the Referer header from being sent when navigating from HTTPS to HTTP. When navigating from HTTP to any origin, the Referer header will be sent and will contain the full URL.

Referrer-Policy: origin

With a value of origin, the Referer header will include the origin but not the path.

Referrer-Policy: origin-when-cross-origin

In this case, the Referer header will include the URL for requests to the same origin, but will include only the origin when requests are cross-origin.

Referrer-Policy: same-origin

Specifying same-origin will result in the Referer header only being sent for requests to the same origin. It's important to note that HTTPS to HTTP is considered a different origin.

Referrer-Policy: strict-origin

With strict-origin, the Referer header will include the origin but not the path, and it will only be sent for HTTPS requests.

Referrer-Policy: strict-origin-when-cross-origin

Like origin-when-cross-origin, the strict-origin-when-cross-origin value will result in the Referer header including the full URL for requests to the same origin and only the origin when requests are cross-origin. The difference is that no information will be sent when navigating from HTTPS to HTTP.

Referrer-Policy: unsafe-url

Finally, with unsafe-url, the Referer header will always include the full URL with any request to any origin. The name is apt, because this configuration will result in leaking potentially private information from HTTPS URLs to insecure origins, so it should generally be avoided.

You can view examples of each Referrer-Policy value on MDN.

Permissions-Policy header

The Permissions-Policy header provides a mechanism for allowing or denying the use of browser features in its own frame and in content within any <iframe> elements.

Permissions-Policy syntax

There are quite a few Permissions-Policy header directives, so we won't go over all of them in this post. But here's the general syntax and an example:

// General syntax
Permissions-Policy: <directive> <allowlist>
// Example
Permissions-Policy: microphone 'none'; geolocation 'none'

In the example, the microphone and geolocation features will be disabled for all browsing contexts, including <iframe> elements, regardless of their origin.

Bonus: Configuring security headers for Vercel

If your site is hosted on Vercel, adding security headers is as simple as updating the vercel.json file. Add any headers you want to the headers array, each element of which is an object that contains a source property and a headers property.

The source property indicates the routes to which you want the headers to apply, and the headers property is an array that contains the headers themselves.

Here's an example using the security headers we've discussed in this post. A source of /(.*) will result in the headers being applied to every page.

  "headers": [
      "source": "/(.*)",
      "headers": [
          "key": "X-Content-Type-Options",
          "value": "nosniff"
          "key": "X-Frame-Options",
          "value": "DENY"
          "key": "Referrer-Policy",
          "value": "no-referrer"
          "key": "Permissions-Policy",
          "value": "geolocation=(), microphone=(), camera=()"
          "key": "X-DNS-Prefetch-Control",
          "value": "on"
          "key": "Strict-Transport-Security",
          "value": "max-age=15552000; includeSubdomains"

Note: A Permissions-Policy value of 'none' becomes a set of empty parentheses when configuring for Vercel.

Wrapping up

As we examined above, adding security headers can improve the security of your site in a few ways, and it's often as simple as updating a configuration file. We looked at Vercel here, but the principle applies to other hosting environments. For example, with Netlify, just update your netlify.toml file.

We hope you found this overview of security headers to be helpful. You can find us on Twitter if you'd like to share your thoughts with us. We'd love to hear from you.

Want to read more tips and insights on working with a website development team that wants to help your organization grow for good? Sign up for our bimonthly newsletter.

By Rachel Opperman


What happens when you cross years of study in biology and medicine with a degree in computer science? You get someone like Rachel.