HTTP Security Headers

Software Engineering Sep 17, 2020

There are a ton of things we have to consider when making our website or web-application as secure as possible. A good starting point might be looking into HTTP Security headers.

Why? Because they are a one-off effort and they are easy to implement since the only thing we need to do are little changes to our preferred web servers configuration.

In this article we'll explore the most important ones and give advice on how to test out our security header configurations.


Starting off with the Strict-Transport-Security header, this header basically tells the browser that our website can only be reached via https instead of http.

Strict-Transport-Security: max-age=<expire-time>
Strict-Transport-Security: max-age=<expire-time>; includeSubDomains
Strict-Transport-Security: max-age=<expire-time>; preload

The max-age directive defines how long (in seconds) the browser should remember that the website is only reachable via https.

The optional  includeSubDomains  directive defines what it's name implies, namely that the browser should apply this knowledge to all subdomains of the domain as well.

The optional preload directive is in fact nothing that is officially part of the header specification. Google is maintaining an HSTS preload service with which we can register our website, telling each browser about our https preference even before the first request hits our web servers.

Content-Security-Policy (CSP)

The Content-Security-Policy (CSP) Header is another added security layer that helps to detect and mitigate certain types of attacks such as Cross Site Scripting (XSS) and data injection attacks.

It works by providing the browser with a list of approved ressources and ressource origins that it is allowed to load or act upon.

TDLR: To easily generate the CSP header, you can use the generator from, which makes the process really easy.

Content-Security-Policy: default-src 'self'
Content-Security-Policy: default-src 'self' *
Content-Security-Policy: default-src 'self'; img-src *; script-src data: 

As we can see the Content Security Header can be composed of a variety of different directives like default-src , img-src , script-src , media-src to name a few. For a complete list we can check out the MDN docs.

The default-src obviously tells the browser from which origins it should be allowed to load ressources by default.

Via the other directives like img-src we are able to override our configuration for the default-src and specify our own custom set of rules only for images.

Additionally to the classic origins or origin patterns called host-sources we are also able to define scheme-sources for scheme based restrictions such as http:, https: , data: or blob: .

Additionally you might have already noticed 'self' doesn't fall into either of those two source categories, that is because it is a special source type, referring to the current origin of the requested page.

Other important special source categories are:

  • 'unsafe-eval'  which enables the use of JavaScripts eval() method
  • 'unsafe-inline' which allows inlined content within the HTML markup to be acted upon or displayed.
  • 'none' which specifically sets the rule to not allow anything for the specific directive.

For a complete list we can check out the MDN docs again.


The X-Frame-Options header allows us to tell the browser if it should be allowed to render our website or webpage within an <iframe>, <frame>, <embed> or <object>.

X-Frame-Options: DENY
X-Frame-Options: SAMEORIGIN

Allowed values for this header are DENY which signals browsers that it should not be possible to embed the current site into another one, and SAMEORIGIN which signals that the site can only be embedded in another site of the same origin.


The X-Content-Type-Options header prevents browsers from guesssing the content type of a ressource instead of listening to the Content-Type header value.

X-Content-Type-Options: nosniff

The only possible value is nosniff which basically enables the functionality and signals the browser to strictly listen the Content-Type header sent within the response.


The Referrer-Policy header is a way to control how much referrer information that is sent via the Referrer header should be included with requests.

Referrer-Policy: no-referrer
Referrer-Policy: no-referrer-when-downgrade
Referrer-Policy: origin
Referrer-Policy: origin-when-cross-origin
Referrer-Policy: same-origin
Referrer-Policy: strict-origin
Referrer-Policy: strict-origin-when-cross-origin
Referrer-Policy: unsafe-url

Possible directives for this header are:

  • no-referrer omits the header altogether.
  • no-referrer-when-downgrade (default) Origin, Path and Query string are sent as long as the security protocol level stays the same or improves
  • origin only sends the origin in the header
  • origin-when-cross-origin Origin, Path and Query string will be sent for same origin request, for cross-origin requests only the origin will be sent.
  • same-origin referrer information will only be sent to same origin sites.
  • strict-origin only sends the origin information when the security protocol level stays the same or improves.
  • strict-origin-when-cross-origin sends the Origin, Path and Query string to same origin requests and sends only the origin information if the security protocol level stays the same or improves.
  • unsafe-url sends Origin, Path and Query all the time without any constraints.


The Expect-CT header prevent misissued certificates from being used. This happens by allowing website to report and optionally enforce so called Certificate Transparency requirements.

As soon as we are enabling this header for our website, we are requesting the browser to verify whether or not the certificate appear in the public Certifcate Transparency logs.

Expect-CT: max-age=86400, report-uri=""

Via the max-age directive we are able to define how long the browser should be allowed to remember the last lookup.

Via the report-uri directive we are explicitly telling the browser to report any issues to the specified uri.


The Permissions-Policy header, previously known as Feature-Policy header, provides a mechanism to allow or deny the use of certain browser API features.

Permissions-Policy: <directive>=(<allowlist>)

Permissions-Policy: geolocation=(), microphone=('none'), battery=('self'), payment=()

The header consists of multiple "directive to allowlist" definitions, just as we can see in the example.

There are various directives we can use, spanning over all browser based API features currently available, some of the more common are camera, geolocation, microphone and fullscreen. For a more detailed list check out the MDN docs.

Allowed values for our <allowlist> would be:

  • 'self' meaning that the feature will be allowed in this and all nested contexts, with the restriction that they must have the same origin
  • 'src' meaning that the feature will be allowed in iframes, as long as the documented loaded into the iframe has the origin
  • 'none' meaning the feature is disabled in all browsing contexts. The same goes for an empty list.
  • <origin(s)> meaning that the feature is allowed only for ressources matching the specified orgins (separated by space)

How to test our security headers?

Having all those headers implemented in our servers configuration is one part, the other part would be to verify if our efforts were any good.

This is where comes into play, they provide us with an easy way to test our setup for all of the described headers above and give our website a ranking of A+ for superb to F for "there is some more work to be done".


Hey cool, you made it! We hope our article was helpful for you and you managed to find all the information regarding security headers you were looking for!


Nico Filzmoser

Hi! I'm Nico 😊 I'm a technology enthusiast, passionate software engineer with a strong focus on standards, best practices and architecture… I'm also very much into Machine Learning 🤖