HTTP security headers: An easy way to harden your web applications

Modern browsers support a wide array of HTTP headers that can improve web application security to protect against clickjacking, cross-site scripting, and other common attacks. This post provides an overview of best-practice HTTP security headers that you should be setting in your websites and applications.

HTTP security headers: An easy way to harden your web applications

What are HTTP security headers?

HTTP security headers are a subset of HTTP headers that is related specifically to security. They are exchanged between a client (usually a web browser) and a server to specify the security details of HTTP communication. There are also other HTTP headers that, although not directly related to privacy and security, can also be considered HTTP security headers.

Setting suitable headers in your web applications and web server settings is an easy way to greatly improve the resilience of your web application against many common attacks, including cross-site scripting (XSS) and clickjacking attacks. This post only lists the most important headers – see our white paper on HTTP security headers for a more detailed discussion of available security headers.

How HTTP security headers can improve web application security

When we talk about web application security on this blog, we often mean finding exploitable vulnerabilities and fixing them in application code. HTTP security headers operate on a different level, providing an extra layer of security by restricting behaviors permitted by the browser and server once the web application is running. Implementing the right headers in the right way is a crucial aspect of any best-practice application setup – but how do you choose the ones that make the biggest difference?

As with other web technologies, HTTP protocol headers come and go depending on current protocol specifications and support from browser vendors. Especially in security, where de facto standards can arise and fall out of favor quite independently of official specs, it’s not unusual to find headers that were widely supported a few years ago but are deprecated today. At the same time, completely new proposals can gain universal support in a matter of months. Keeping up with the latest developments is not easy, but leading application security solutions such as Invicti can help by automatically checking for the presence and correctness of HTTP security headers and providing clear recommendations.

The most important HTTP security headers

First up are the three best-known and probably most important HTTP response headers that any modern web application should be setting to immediately rule out entire classes of web attacks.

Strict-Transport-Security

When enabled on the server, the HTTP Strict Transport Security header (HSTS) enforces the use of encrypted HTTPS connections instead of plain-text HTTP communication. A typical HSTS header might look like this:

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

This informs any visiting web browser that the site and all its subdomains use only SSL/TLS communication, and that the browser should default to accessing it over HTTPS for the next two years (the max-age value in seconds). The preload directive indicates that the site is present on a global list of HTTPS-only sites. The purpose of preloading is to speed up page loads and eliminate the risk of man-in-the-middle (MITM) attacks when a site is visited for the first time.

Invicti checks if HSTS is enabled and correctly configured.

Content-Security-Policy

The Content Security Policy header (CSP) is something of a Swiss Army knife among HTTP security headers. It lets you precisely control permitted content sources and many other content parameters and is recommended way to protect your websites and applications against XSS attacks. A basic CSP header to allow only assets from the local origin is:

Content-Security-Policy: default-src 'self'

Other directives include script-src, style-src, and img-src to specify permitted sources for scripts, CSS stylesheets, and images. For example, if you specify script-src 'self', you are restricting scripts (but not other content) to the local origin. Among other things, you can also restrict browser plugin sources using plugin-types (unsupported in Firefox) or object-src.

Invicti checks if the CSP header is present.

X-Frame-Options

This header was introduced way back in 2008 in Microsoft Internet Explorer to provide protection against cross-site scripting attacks involving HTML iframes. To completely prevent the current page from being loaded into iframes, you can specify:

X-Frame-Options: deny

Other supported values are sameorigin to only allow loading into iframes with the same origin and allow-from to indicate specific permitted URLs. Note that nowadays, this header can usually be replaced by suitable CSP directives.

Invicti checks if the X-Frame-Options header is present.

Examples of deprecated HTTP security headers

As already mentioned, some headers get introduced as temporary fixes for specific security issues. As web technology moves on or standards catch up, these become deprecated, often after only a few years. Here are two examples of deprecated headers that were intended to address specific vulnerabilities.

X-XSS-Protection

As the name suggests, the X-XSS-Protection header was introduced to protect against JavaScript injection attacks in the form of cross-site scripting. The usual syntax was:

X-XSS-Protection: 1; mode=block

Created for browsers equipped with XSS filters, this non-standard header was intended as a way to control the filtering functionality. In practice, it was relatively easy to bypass or abuse. Since modern browsers no longer use XSS filtering, this header is now deprecated.

Invicti checks if you have set X-XSS-Protection for your websites.

Public-Key-Pins

HTTP Public Key Pinning (HPKP) was introduced in Google Chrome and Firefox to solve the problem of certificate spoofing. HPKP was a complicated mechanism that involved the server presenting clients with cryptographic hashes of valid certificate public keys for future communication. A typical header would be something like:

Public-Key-Pins:
    pin-sha256="cUPcTAZWKaASuYWhhneDttWpY3oBAkE3h2+soZS7sWs="; 
    max-age=5184000

In practice, public key pinning proved too complicated to use. If configured incorrectly, the header could completely disable website access for the time specified in the max‑age parameter (in the example above, this would be two months). The header was deprecated in favor of certificate transparency logs â€“ see the Expect-CT header below.

Other useful HTTP security headers

While not as critical to implement as CSP and HSTS, the additional headers below can also help you harden your web applications with relatively little effort.

Expect-CT

The recommended way to prevent website certificate spoofing is to use the Expect-CT header to indicate that only new certificates added to Certificate Transparency logs should be accepted. A typical header would be:

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

The enforce directive instructs clients to refuse connections that violate the Certificate Transparency policy. The optional report-uri directive indicates a location for reporting connection failures.

Invicti reports missing Expect-CT headers with a Best Practice severity level.

X-Content-Type-Options

When included in server responses, this header forces web browsers to strictly follow the MIME types specified in Content-Type headers. This is specifically intended to protect websites from cross-site scripting attacks that abuse MIME sniffing to supply malicious code masquerading as a non-executable MIME type. The header has just one directive:

X-Content-Type-Options: nosniff

Invicti checks if Content-Type headers are set and X-Content-Type-Options: nosniff is present.

Fetch metadata headers

This relatively new set of client-side headers allows the browser to inform the server about application-specific HTTP request attributes. Four headers currently exist:

  • Sec-Fetch-Site: Specifies the intended relationship between the initiator and target origin
  • Sec-Fetch-Mode: Specifies the intended request mode
  • Sec-Fetch-User: Specifies if the request was triggered by the user
  • Sec-Fetch-Dest: Specifies the intended request destination

When supported by both the server and the browser, these headers provide the server with additional information about intended application behaviors to help identify and block suspicious requests.

Related HTTP headers to improve privacy and security

These final items are not strictly HTTP security headers but can serve to improve both security and privacy.

Referrer-Policy

This controls how much (if any) referrer information the browser should reveal to the web server. Typical usage would be:

Referrer-Policy: origin-when-cross-origin

With this header value, the browser will only reveal its full referrer information (including the URL) for same-origin requests. For all other requests, only information about the origin is sent.

Invicti reports missing Referrer-Policy headers with a Best Practice severity level.

Cache-Control

This header allows you to control the caching of specific web pages. Several directives are available, but the typical usage is simply:

Cache-Control: no-store

This prevents any caching of the server response, which can be useful for ensuring that confidential data is not retained in any caches. You can use other available directives to get more precise control over caching behavior.

Clear-Site-Data

If you want to ensure that confidential information from your application is not stored by the browser after a user logs out, you can set the Clear-Site-Data header:

Clear-Site-Data: "*"

This directive will clear all browsing data related to the site. The cache, cookies, and storage directives are also available to give you more fine-grained control over what is cleared.

Feature-Policy

This is an experimental header that allows you to deny access to specific browser features and APIs on the current page. It can be used to control application functionality but also to improve privacy and security. For example, if you want to deny an application permission to access the microphone and camera APIs, you can send the following header:

Feature-Policy: microphone 'none'; camera 'none'

Many more directives are available – see the Feature-Policy documentation on MDN for a full list.

Security headers in action with Sven Morgenroth

Invicti security researcher Sven Morgenroth joined Paul Asadoorian on Paul’s Security Weekly #652 to describe and demonstrate various HTTP headers related to security. Watch the full video interview and demo:

Keep track of your HTTP security headers with Invicti

HTTP security headers can be an easy way to improve web security and often don’t require changes to the application itself, so it’s always a good idea to use the most current headers. However, because browser vendor support for HTTP headers can change so quickly, it’s hard to keep everything up-to-date, especially if you’re working with hundreds of websites. 

To help you keep up and stay secure, Invicti provides vulnerability checks that include testing for recommended HTTP security headers. Invicti checks if a header is present and correctly configured, and provides clear recommendations to ensure that your web applications always have the best protection.

Zbigniew Banach

About the Author

Zbigniew Banach - Technical Content Lead & Managing Editor

Cybersecurity writer and blog managing editor at Invicti Security. Drawing on years of experience with security, software development, content creation, journalism, and technical translation, he does his best to bring web application security and cybersecurity in general to a wider audience.