HTTP Security Headers

The Goal

HTTP Security Headers are a collection of security features built into modern web browsers that can be enabled by a web application to harden the application against common web attacks. These can be enabled by setting specific server response headers and can protect against attacks such as Cross-site Scripting, ClickJacking, SSL Stripping, and information leakage issues.

Key Takeaways

  • Understand which headers are available and how to configure them to gain the best level of protection available.
  • Understand why some headers are now deprecated and the alternative features that can be implemented to offer a similar level of protection.

The Headers


Transport Layer Security (TLS), and its deprecated predecessor Secure Sockets Layer (SSL), are protocols that can be used to protect information in transit – and are the underlying protocols used by HTTPS to protect web traffic. They are designed to prevent a threat actor who is able to intercept messages from being able to read or modify the contents of those messages.

Many web servers operate by redirecting users who request a HTTP (unencrypted) page to the HTTPS equivalent, however the initial plaintext connection is therefore at risk of interception and modification.

HTTP Strict Transport Security (HSTS) enforces the use of HTTPS in the web browser, ensuring that no information is sent to the domain (and optionally subdomains too), even if the user attempts to navigate to a HTTP page. This additionally mitigates the risk of cookies without the “secure” flag set, by enforcing all traffic is HTTPS only.

HSTS can be enabled by supplying the Strict-Transport-Security HTTP response header. The max age directive must also be supplied, which specifies for how many seconds the header should be enforced. It is recommended that the max-age is set to at least one year. For example:

Strict-Transport-Security: max-age=31536000

It is also recommended that the header is configured to include sub-domains, so that all subdomains are protected, by using the includeSubDomains directive.

Additionally, it is recommended that preloading is enabled using the preload directive and that the site is submitted to the browser preload lists. This ensures that the first connection to the server uses HTTPS even if the user has never visited the site before. The following example includes these recommended directives:

Strict-Transport-Security: max-age=63072000; includeSubDomains; preload

Note: to enable preloading, the max-age directive must be set to at least 3153600 and the includeSubDomains directives must be present.


Content Security Policy (CSP) allows the application to restrict the location of resources to an allow-list of approved locations, including where scripts can be loaded from and when the application may be framed. This can therefore mitigate reflected and stored Cross-Site Scripting Attacks as well as issues such as Clickjacking.

ClickJacking may also be prevented using the X-Frame-Options security, however this is not fully implemented in some browsers, for example in Chrome the only available options are DENY and SAMEORIGIN.

The CSP header is made up of two parts, a list of directives and a list of approved locations. It is recommended that at a minimum the following directives are supplied: script-src, object-src-, style-src, and frame-ancestors. It is also recommended that no directives marked as unsafe are included, for example, unsafe-inline and unsafe-eval.

The following are examples of available directives:

default-srcA default allow-list for where more specific directives have not been supplied.
script-srcSpecifies where JavaScript can be loaded from.
img-srcSpecifies where images can be loaded from.
style-srcSpecifies where CSS can be loaded from.
connect-srcSpecifies where features such as XMLHttpRequest may connect to.
font-srcSpecifies where fonts may be loaded from.
object-srcSpecifies where object, embed, and applet elements may be loaded from.
media-srcSpecifies where audio, video and track elements may be loaded from.
frame-srcFrame-src is for where frames can be loaded from, which was deprecated in CSP Level 2 and then undeprecated in CSP Level 3.
frame-ancestorsSpecifies where this page may be framed from. Setting frame-ancestors to ‘none’ is the equivalent of setting the X-Frame-Options header to DENY.

The following are examples of source locations that can be given to directives:

*A wildcard to allow any URL except data: and filesystem: schemes.
‘none’Disables this resource type
‘self’Allows resources from the same-origin
data:Allows the loading of resources via the data scheme, such as base64 encoded images
domain.example.comAllows loading resources from the specified domain
*.example.comAllows loading resources from any subdomain of the specified domain
https://example.comAllow loading resources from the specified domain, if supplied over HTTPS
https:Allow loading resources from any domain, if supplied over HTTPS
‘unsafe-inline’With script-src, allows the use of inline JavaScript; considered unsafe as it may allow for Cross-site Scripting attacks. With style-src, allows the use of inline CSS; considered unsafe as it may allow for virtual defacement.
‘unsafe-eval’Allows the use of JavaScript eval(), a function, which may allow for DOM-XSS attacks. The eval() function is described by MDN as “an enormous security risk”.
‘sha256-‘Allow scripts to execute if they match the supplied hash. sha256-, sha384-, or sha512- may be used.
‘nonce-Allow scripts to be used if they include a nonce= attribute which matches the supplied none entry in the header. These numbers should not be used for more than one script, instead additional nonce- entries should be supplied.

The source list can be used, with the directives to build a policy. For example:

Content-Security-Policy: default-src: 'none'; script-src:; img-src:; font-src:; object-src: 'none'; frame-ancestors: 'self';


The X-Frame-Options header can be used to specify whether a web browser should be allowed to render the target page in a frame (such as a frame, iframe, embed, or an object tag). This can be used to prevent attacks such as ClickJacking.

ClickJacking is an issue where an attacker causes an application to be rendered as a transparent layer in the web browser and crafts the page to entice the user to interact. However, the clicks or keys submitted by the user are sent to the transparent page not the visible page, allowing for these clicks to be hijacked.

This would require the frameable application to have functionality that can be abused through simple actions such as a single click, or small number of clicks. For example, where a user is trying to illegitimately boost their social media page they could use ClickJacking to trick users into clicking the “Like” button, or similar.

Although this header is effectively made obsolete by the Content-Security-Policy (CSP) feature “frame-ancestors”, it can still be a benefit for older browsers that do not support CSP – such as Internet Explorer, Chrome prior to version 40 (Released in 2015), and Firefox prior to version 33 (Released in 2014).

It is recommended that an X-Frame-Options header is configured for the application if framing should not be allowed, or if it should only the target application should be able to frame itself. The following header example would disable framing:

X-Frame-Options: DENY

Referrer Policy

The HTTP response header “Referer” supplies the web address of the page that linked to the resource being requested. Where sensitive information is supplied within the URL this may be disclosed within the Referer header.

The Referrer-Policy allows a web application to reduce when the Referer header is set. Thereby preventing sensitive data in the URL from being transmitted to third-parties or over insecure connections.

Note: The header name “Referer” is a misspelling of the word “referrer”. However, it was misspelled in the original RFC and is therefore used in its misspelled form for this header. Although, the mistake was not continued for the “Referrer-Policy” header.

It is recommended that a Referrer Policy is specified in order to prevent information leakage via the referrer header. The most secure option is to disable the Referer header entirely, The which can be achieved with the following:

Referrer-Policy: no-referrer

Alternatively, the following will send the full URL when performing a same-origin request with the same protocol, but will only disclose the origin when the request is sent to a different origin:

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

The following will send the full URL when performing a same-origin request but will disable the Referer header when the request is sent to a different origin.

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


The “X-XSS-Protection” header could be used by a web application to control the behaviour of browser that support built-in XSS protection, for example Chrome’s “XSS Auditor”. However, this protection was found to be insufficient, for example Chrome states: “The XSS Auditor can introduce cross-site information leaks and mechanisms to bypass the Auditor are widely known.”

This feature has been removed from all modern browsers. It was permanently disabled in Chrome on 5th August 2019 and removed in Chrome 78 (Released October 2019). Microsoft Edge’s protection, known as “XSS Filter” was removed in July 2018. Firefox never implemented a protection of this nature.

Whilst no modern browsers support this protection, if you would like to enable this protection in older browsers, it is recommended that it is explicitly set to blocking mode; this can be achieved with the following:

X-XSS-Protection: 1; mode=block

Alternatively, if you want to explicitly disable this feature, you can use:

X-XSS-Protection: 0


The Cache-Control HTTP server response header specifies whether the server response can be cached by the web browser and any interim devices such as web proxies. Generally, if the content of the page includes confidential information, then it should not be cached, as if confidential information is cached on user’s device, and that device is a public device, or shared with other users then the information may be compromised by another user with access to the device.

Note: the Cache-Control header option no-cache does not instruct the browser not to cache the response, but instead instructs it that the cache must be revalidated before reuse, to prevent the browser from storing the response in the cache the no-store option should be used.

It is recommended that where confidential information is being transmitted, Cache-Control is enabled with the no-store option, for example:

Cache-Control: no-cache, no-store

Permissions Policy & Feature Policy

A Permissions Policy can be used to disable certain device features within an application and pages that the application embeds. It is implemented as a HTTP Server response header and can control features such as the device camera, microphone, accelerometers and device payment features. This header was previously named “Feature-Policy” although within the specification has now been renamed to “Permissions-Policy”.

As it is up to the browsers to implement this change it is feasible that a browser may eventually disable support for Feature-Policy and begin supporting only Permissions-Policy, thereby removing a protection in new browsers.

For example, Chrome currently supports both headers and will enable protection for any features specified in either header. Where both headers specify a feature, Chrome will use the configuration given in the Permissions-Policy header.

Therefore, it is recommended that you consider implementing both a Feature-Policy and Permissions-Policy header to include only the features used by the application, and to ensure long-term browser support.

The following example restricts some common browser features:

Permissions-Policy: camera=(), geolocation=(), microphone=()
Feature-Policy: microphone 'none'; camera 'none'; geolocation 'none'


The “X-Content-Type-Options” header can be used to disable a default feature of some browsers known as “MIME Type Sniffing” which may aim to detect the MIME type of server response and may overwrite the explicit settings given in the Content-Type server response header.

MIME Type Sniffing may be abused by attackers to smuggle executable content within server responses which should not be executable. Therefore, it is recommended that this feature is disabled, with the following:

X-Content-Type-Options: no-sniff

Public Key Pins

HTTP Public Key Pinning (HPKP) was a security header which allowed the application to specify a list of public keys that should be trusted by the browser, and if the server provided a certificate that used any other key, it should not be trusted – even if it is a valid CA-issued certificate.

The header was intended to decrease the risk of interception attacks with forged or mis-issued certificates. However, this header proved troublesome and when incorrectly implemented could block access to websites for long periods of time, therefore its use is discouraged. Furthermore, all browsers have removed support for his header. Chrome removed supported in version 72 (Released January 2019) and Firefox removed support in January 2020.

We do not recommend that you configure HPKP.

Here Be Dragons

There are two common issues that we see with security headers configured on websites. The first are simple syntax errors when entering the header value, for example we often see quote marks where they’re not supposed to be! It’s important to double check how your headers are presented to the web browsers and double check your quote marks!

The second mistake that we often see is sites that set a header twice. Often this isn’t a problem, but can lead to unpredictable behaviour. For example, if you set the header twice which one does the browser pick? The browser could pick the first one, it could pick the most secure option, or it might try to parse them both and combine them in some way.

As an example, according to RFC 6797 for Strict Transport Security “If a UA receives more than one STS header field in an HTTP response message over secure transport, then the UA MUST process only the first such header field.” That said, just because the standard tells the browser to do that, doesn’t mean the browser developers agreed and do it that way – in short, it can lead to unpredictable behaviour between browsers and so we recommend avoiding this behaviour where possible.

Read More

Play Cover Track Title
Track Authors