Security Headers on HTTP requests

and how to test your site

Modern web browsers have a number of settings to help protect your site from attack. You turn them on by use of various header lines in your HTTP responses. Now when I first read about them I thought they were not useful; a basic rule for client-server computing is “don’t trust the client”. An attacker can bypass any rules you try to enforce client side.

But then I read what they do and realised that they are primary to help protect the client and, as a consequence, protect your site from being hijacked. For example, if you can define exactly where javascript can be run from then an attacker will find it harder to inject rogue scripts and steal credentials. We can help stop XSS attacks.

What these headers do is stop the legitimate normal user of your site being used as an attack vector. This makes them an import part of the controls surface of your web presence. You still need to make sure your app isn’t vulnerable to SQL injection, buffer overflow and everything else; these headers (mostly) protect against a third-party attack.

What header values are available?

Fortunately, Scott Helme has done an excellent series of posts that describe the headers. I won’t repeat all of his work, but I will summarise and link to the relevant pages. They’re well worth reading.


This one is simple to understand; if you report the version of the software you’re using then attackers can easily determine what vulnerabilities may be present. Knowing you run Apache 1.2.3 gives a lot more information to attackers then if you reported “FooBarBaz”.

This tells the client to always use https for your site. It can cache this value so even if the user types in a non-https address the browser will automatically switch to the TLS version.


This one really shouldn’t be needed, but is due to Microsoft being too clever. When you send a file via a https connection you include a content-type header. This should be used to help the browser decide what to do with it. But Internet Explorer ignores this and looks at the data itself; if it looks like a word document then it’ll open it in word, even if the content-type says differently. This header allows you to turn off that feature.


Modern web browsers have a level of built in protection to stop cross-site scripting. This header turns it on.


When I first came across this it annoyed me. It stopped me from using my own ‘bookmarks’ frame. For the past 20 years my home page was a framed site; on the left were my normal bookmarks and on the right was where the content loaded. Very very old school. X-Frame-Options stopped this working on sites like Dilbert or GPF. Grrr. But it has a good rationale; it stops a site from being embedded in a frame and so can protect against click-jacking. Grump.


This allows you to restrict what SSL certificates can claim to be you. For example, if you know you will always get a cert from Lets Encrypt then you can flag this. In this way you can help protect against a rogue CA also releasing a cert under your name. We’ve seen this happen before.


This is a biggie and important protection and is easily misconfigured and can break your site. And, indeed, first time I hit Scott’s site it wasn’t working properly because site changes weren’t being properly reflected in the policy.

It can also stop you from doing stuff; e.g. I can’t embed YouTube videos on this site because the policy I wrote locks it down very tightly.

You will spend most of your time on this entry and will have to do the most testing.

My configuration

Because I use Apache I can’t turn off the whole Server header, but I can remove the version number. But I can do pretty well for the rest:

ServerSignature Off

Header set Strict-Transport-Security "max-age=31536000; includeSubdomains"
Header set X-Content-Type-Options "nosniff"
Header set X-XSS-Protection "1; mode=block"
Header set X-Frame-Options "SAMEORIGIN"
Header set Content-Security-Policy "default-src 'self' 'unsafe-inline' 'unsafe-eval' *; style-src 'self' * * 'unsafe-inline'; font-src 'self' *"


Scott has also created a site for you to be able to test your setup. Just go to, enter your site name and get an almost immediate response.


I don’t have my cert pinned at the moment. You can also note that I have a warning for the CSP; I allow ‘unsafe-inline’. So my configuration isn’t necessarily safe, but because I’m forcing SSL then the chances of an attacker injecting rogue scripts isn’t really any different to trusting self and having the code loaded from an external page.


Most of these headers can be turned on without any impact on your app; forcing TLS, turning off content-type sniffing, turning on XSS protections and frame clickjacking protectings are mostly safe (not 100% because a site may trip things and need workarounds). CSP is the hard one, but definitely worth spending time on.