What all Developers need to know about: Security Headers

Posted by Martijn van Lambalgen on August 1, 2018

— This post is part of a series of monthly blog posts about all kinds of Security topics for Developers —

Adding security headers is usually a quick win when improving the security of your web application, but nowadays there’s so many to choose from. Or shouldn’t you choose at all? Why not add all of them? How does this work? We tried to make an overview of which headers improve your security, and what they actually try to achieve for you.

Let’s start at the beginning. The HTTP protocol tells you to specify the HTTP protocol version, add a bunch of headers and optionally include a body for your request/response. The response headers can be thought of as meta data to the response, or as additional instructions for the browser. E.g. it tells the browser which content type the reponse page has, what server handled the request, or how long the requested resource can be cached. Below is an example of an HTTP response. The relevant security headers are marked in red.

HTTP/1.1 200 OK
Server: nginx
Date: Tue, 17 Jul 2018 12:22:44 GMT
Content-Type: text/html;charset=utf-8
Content-Length: 13
Connection: keep-alive
Last-Modified: Mon, 02 Jul 2018 19:41:10 GMT
ETag: W/”gGSCXKpii+IgGSDOPbh0PM”
Cache-Control: max-age=0 must-revalidate
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
X-Frame-Options: SAMEORIGIN
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
Referrer-Policy: no-referrer

Hello World!

HTTP Headers can be dynamically generated, but especially when they are static, hackers cannot easily tamper with them and they will make the user experience a lot more secure. The browser just follows the directions of the headers, and if the content of the site doesn’t adhere to these directions (e.g. because of injected data), the browser blocks any attack and prevents damage for the user. We tried to make a list of the most important headers regarding the security of your web application.



First a fairly standard header that we think everyone should use. There’s only one directive for this header: nosniff.

X-Content-Type-Options: nosniff

It tells the browser that it should always follow the Content-Type as specified by the server, and not try to ‘sniff’ and guess the MIME type. The risk of content type sniffing is that a non-executable resource is misinterpreted and becomes executable. E.g. a text file might be misinterpreted as HTML and be executed by the browser. Hackers could upload images that the browser would sniff as HTML containing JavaScript, thereby executing all kinds of nastiness. Simply adding this header prevents this.

See the specification for more options and browser compatibility: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Content-Type-Options



The Strict-Transport-Security header tells the browser to be very strict about the use of HTTPS. Whenever you send this header with your responses, the browser will not allow the user to make simple http requests to your site in the future anymore. Even if the user explicitly types in ‘http://…’, the browser will internally redirect to ‘https://…’. This prevents unencrypted traffic from going over the wire, and makes it a lot harder for hackers to do a man-in-the-middle attack.

A handy little detail to know: If your site was initially accessed through http, the browser will ignore the header. It only starts working after it was loaded over HTTPS once. This means that you can use the header in your development environment, even if you don’t use HTTPS there.

Strict-Transport-Security: max-age=31536000; includeSubDomains;

If you’re really serious about HTTPS (like TOPdesk), you can add your domain to the preload list (https://hstspreload.org/), and let browser vendors that support this, hard-code it in the browser. This means that users can never again make the mistake to connect insecurely to your web application the first time.

Leaving this header out means that every time the user types ‘http://…’ into his browser, hackers have an opportunity to intercept the request and send back a fake version of the requested site.

See the specification for more options and browser compatibility: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security



X-Frame-Options allows you to tell the browser if framing your web application is allowed. This is to prevent clickjacking, which we explained extensively in a previous blog post. We think this header is a bit dated and inflexible and should in the future be exchanged for the frame-ancestors directive of CSP, but since not all browsers support that one (yet), we’ll stick with X-Frame-Options a little while longer.

To completely block all framing:

X-Frame-Options: DENY

To only allow framing from the same domain as your web application:

X-Frame-Options: SAMEORIGIN

To only allow framing from a specific domain:

X-Frame-Options: ALLOW-FROM https://example.com/

By default TOPdesk will only allow framing from the same domain. It is currently possible for customers to explicitly disable this, for example because they want to put our software in an iframe on multiple domains. However, we discourage this and might stop supporting the funtionality in the future.

See the specification for more options and browser compatibility: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options



Expect-CT stands for Expect Certificate Transparency. By setting this header, the server asks the browser to check that any certificate from that site appears in public CT logs. If the server has publicly registered his certificates, it’s much harder for a hacker to do a man-in-the-middle attack and serve a fake certificate. The browser would immediately notice the certificate is fake, because it is missing from the CT logs. Optionally, any violations can be reported to a specified URI.

Expect-CT: max-age=86400, enforce, report-uri="https://foo.example/report"

See the specification for more options and browser compatibility: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Expect-CT



Although maybe not strictly a security header, adding cookies correctly is definitely a security concern. Don’t forget to set the Secure flag for cookies that only should be transferred over HTTPS connections, and the HTTPOnly flag when the cookies shouldn’t be read by JavaScript. Also, consciously choose whether to make the cookie a session cookie or a persistent cookie. We explained all of this in detail in a previous blog post: https://techblog.topdesk.com/security/cookie-security/

Set-Cookie: JSESSIONID_ROOT=abcdefghijklmnopqr12345678;Path=/;Secure;HttpOnly

See the specification for more options and browser compatibility: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie



This is a relatively new header that sets a policy for the browser to govern the referrer information. Why is this needed anyway you might think? Well, with every link a user clicks on, the browser sends the referrer along to wherever the link points. This means that the receiving end knows where the user was coming from. Good stuff you’d say, but sometimes developers put sensitive information in URLs, including internal id’s, personal information or even passwords. Although this is all bad practice, it does happen, especially in legacy code. If a link points to an external domain, the other party may learn all of the sensitive information through the referer header. To avoid this risk, we tell the browser to not send the referrer along with a link. Optionally you can tell the browser to send it only under specific circumstances.

To let the browser never send the referrer in links:

Referrer-Policy: no-referrer

To only let the browser use the referrer if the link points to the same domain:

Referrer-Policy: same-origin

See the specification for more options and browser compatibility: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy



Perhaps one of the easiest headers to just add to your web application with very little risk of breaking legacy functionality. X-XSS-Protection will tell the browser to activate its built-in XSS protection (e.g. Chrome’s XSS Auditor, or IE’s XSS Filter). Although XSS cannot simply be mitigated by setting a header, browsers are able to detect some of the ‘easier’ reflected XSS attacks. Just enabling the protection will already catch most of the low-hanging fruit. By default, browsers already enable this protection, but it is possible to deactivate it. By setting this header, we tell the browser to activate it anyway.

You can do so with:

X-XSS-Protection: 1;

Or you can set it to ‘strict’ mode, which means that on any encounter of XSS, the browser will block all rendering of the site:

X-XSS-Protection: 1; mode=block

This is the option we use at TOPdesk.

Note: Just remember that this header is just a safety-net. It’s never going to catch all XSS attacks, and should certainly not be used as an alternative to real XSS prevention.

See the specification for more options and browser compatibility: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-XSS-Protection



This is a big one and could be explained easily in a whole book. It’s an important one however that gives us a … lot … of … control over what the browser can do. In short: with this header we tell the browser what types of resources it can load from which sources.  Resources can for example be scripts, stylesheets, images, fonts, iframes or objects, and sources can be specific domains, inline  specified, evaluated content or mixed content. By being very specific about what the web application actually uses, you can limit the hacker drastically in his options to start an attack or to steal data. For example, a hacker cannot load his favorite malicious script from his own server if CSP doesn’t allow scripts from any source. Furthermore, CSP also prevents ISPs or rogue libraries from injecting ads to your site.

One final interesting option to mention is that you can let the browser report any violations automatically to a specified URI. This allows you to keep an eye on attacks, so you know what hackers are up to. It also shows you when you set your policy too strict, and some functionality is broken.

Content-Security-Policy default-src 'none' ; style-src 'self' https://fonts.googleapis.com; font-src 'self' data: https://fonts.gstatic.com ; script-src 'self'; img-src 'self' https://resources.topdesk.com; connect-src 'self' ; object-src 'none' ; frame-src 'self' https://www.youtube.com ;

TOPdesk is currently in the process of adding a strict version of CSP to all new functionality.

See the specification for more options and browser compatibility: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy



This is the latest addition to the set of security headers, and at this moment only Chrome and Safari support it. The goal is to enable or disable certain browser features or APIs that have security implications. Basically, you just need to list the feature restrictions you like. Note that restrictions also apply to any child frames you put in your site. Some examples of features that are currently supported for this header are: vibrate, geolocation, microphone or fullscreen.

Feature-Policy: vibrate 'self'; usermedia '*'; sync-xhr 'self' example.com

For more information see: https://scotthelme.co.uk/a-new-security-header-feature-policy/


If you quickly want to know how well your site is doing on security headers, you can check out https://securityheaders.io.  Scott Helme is keeping a list of all important security headers and he has a tool that automatically grades your website and gives you advice on how to improve. For example, check out the rating of our security blog

Want to learn more about security? Find all our security blogs here: https://techblog.topdesk.com/tag/security/ or check out https://careers.topdesk.com/ for job vacancies and join our security team.


About the author: Martijn van Lambalgen

Software engineer at TOPdesk interested in Software Architecture, Continuous Delivery and Web Security. Also wine drinker, good food lover, amateur baker and pleasure-seeker in general

More Posts