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

Modern browsers and web servers support many HTTP headers that can greatly improve web application security to protect against clickjacking, cross-site scripting, and other common types of attacks. This post provides an overview of best-practice HTTP security headers that you should be setting in your websites and applications and shows how to use DAST to make sure you’re doing it right.

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

What are HTTP security headers?

HTTP security headers are those HTTP headers that are related specifically to security, exchanged between a client (like a web browser) and a server to define the security of HTTP communication. These include dedicated security headers and several others that can indirectly affect privacy and security.

Setting the right security headers in your web application, API, and web server settings can greatly improve the resilience of your applications against entire classes of attacks, including cross-site scripting (XSS) and clickjacking attacks. This post highlights the most important headers and shows how to use tools such as DAST to automatically check for their presence and correctness. For an in-depth discussion of available security headers, see our white paper on HTTP security headers.

How HTTP security headers improve your web application security posture

In the realm of web application security testing, vulnerabilities are often understood to be exploitable security flaws that originate in application code and need to be fixed there. That usually means you’re fixing one vulnerability in one app, often affecting just in one place in that app.

HTTP security headers operate at the runtime level and provide a much broader layer of security. By restricting behaviors permitted by the browser and server once the web application is running, security headers can block entire classes of attacks, which makes them extremely powerful. Implementing the right headers in the right way is a crucial aspect of any best-practice application setup—but first you need to choose the ones that make the biggest difference, and then you need to implement and test them all across your application environment to balance security and functionality.

Keeping your HTTP security headers healthy with DAST

As with other web technologies, HTTP protocol headers come and go depending on current specifications and browser vendor support. Security research, in particular, moves much faster than official tech standards, so de facto standards can arise and fall out of favor quite independently of the official specs. Headers that were widely supported a few years ago are deprecated today and replaced by something else. That’s a lot to keep up with.

On top of that, security headers can be set in server config but also in the application itself. In a large app environment with hundreds of servers running thousands of sites, applications, and APIs, manually checking and maintaining security headers everywhere they’re being set is completely unrealistic. Fortunately, that’s a natural job for automated vulnerability scanners. Leading tools such as Invicti’s DAST solutions will automatically check for the presence and correctness of HTTP security headers, providing clear recommendations according to current security best practices.

The most important HTTP security headers

First up are the two best-known HTTP response headers that any modern web application will be setting. Apart from ruling out entire classes of web attacks, both are now also a practical necessity.

Strict-Transport-Security

The HTTP Strict Transport Security header (HSTS) is set on the server and 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 visiting web browsers that the site along with all its subdomains only communicates over SSL/TLS and the browser should only access 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 also eliminate the risk of man-in-the-middle (MITM) attacks when a site is visited for the first time without encryption.

Invicti’s DAST scanner checks if HSTS is enabled and correctly configured.

Content-Security-Policy

The Content Security Policy header (CSP) is the Swiss Army knife of HTTP security headers. It lets you precisely control permitted content sources and many other content parameters. Because you can also limit script sources, it is the recommended way to protect your sites and applications against XSS attacks. Here’s a basic CSP header that only allows assets from the local origin:

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

Some of the other directives include script-src, style-src, object-src, and img-src to specify permitted sources for scripts, CSS stylesheets, objects, and images, respectively. For example, if you specify script-src 'self', you are restricting scripts to the local origin but can still load other content from external origins.

Invicti’s DAST scanner checks if the CSP header is present.

Other 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 (at least compared to getting the same effect purely in application code).

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, without attempting any content type detection if the Content-Type header is missing. This is 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 to block sniffing:

 X-Content-Type-Options: nosniff

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

Headers related to cross-origin resource sharing (CORS)

Many web apps need to work with some external resources that require exceptions to the default same-origin policy (SOP) settings applied by modern browsers. Several headers exist that let you selectively relax SOP restrictions without compromising overall security:

  • Access-Control-Allow-Origin: Specifies a list of permitted domains for cross-origin access. The value can be one or more domains and subdomains, or * to explicitly disable CORS restrictions for all sites.
  • Cross-Origin-Opener-Policy (COOP): Specifies whether a top-level document can share browsing context with cross-origin documents. Use same-origin to disallow such access.
  • Cross-Origin-Resource-Policy (CORP): Specifies domains that are permitted to include the current resource. Use same-site to disallow all external origins.
  • Cross-Origin-Embedder-Policy (COEP): As for CORP but specifically related to embedding resources on the current page. Use require-corp to only embed resources from origins permitted by the CORP header.

Note that in practice, there will be overlap between these and other security headers and, in many cases, there will be more than one way to get the result you need.

Fetch metadata headers

This relatively young 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 give the server additional context on intended application behaviors and business logic 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 do provide additional control over data security and privacy.

Referrer-Policy

Controls how much referrer information the browser should reveal to the web server (if any). Typical usage is:

Referrer-Policy: origin-when-cross-origin

With this setting, the browser will only reveal full referrer information (including the URL) for same-origin requests. For all other requests, only the origin will be shared.

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

Cache-Control

Lets you control caching for specific web pages. Many directives are available, but the most common 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 fine-tune the desired caching behavior, including expiration time.

Clear-Site-Data

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 value 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. Note this header is not universally supported.

Permissions-Policy (previously Feature-Policy)

Allows you to define permissions for specific browser features and APIs on the current page. It can be used to control application functionality, but the main use case is to restrict access to privacy-related features like microphone, camera, or geolocation APIs. To disallow access to all three of these, specify:

Permissions-Policy: microphone=(), camera=(), geolocation=()

Several dozen directives are available—see the Permissions-Policy documentation on MDN for a full list.

Examples of deprecated HTTP security headers

As already mentioned, it was common in the past for dominant browsers to introduce new headers as temporary fixes for specific security issues. As web technologies became more standardized and organized, many of these were deprecated, often after only a few years. While they shouldn’t be used in modern applications, these deprecated headers give a fascinating insight into the history and relentless pace of changes in web technology.

(Deprecated) X-Frame-Options

The X-Frame-Options header was introduced way back in 2008 in Microsoft Internet Explorer to provide protection against cross-site scripting attacks involving HTML iframes before more standardized headers were adopted. To completely prevent the current page from being loaded into iframes, you would specify:

 X-Frame-Options: deny

Another useful value was x-frame-options: sameorigin to only allow loading into iframes with the same origin. You could also specify allow-from to list specific permitted URLs. This header has been deprecated since the adoption of the frame-ancestors CSP directive to control iframe security.

(Deprecated) X-XSS-Protection

As the name suggests, the X-XSS-Protection header was introduced to protect against JavaScript injection attacks, i.e. 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 that filtering functionality. Modern browsers no longer use XSS filtering due to the many possibilities of XSS filter evasion, so this header is now deprecated, making CSP directives your main XSS defense.

(Deprecated) Public-Key-Pins

HTTP Public Key Pinning (HPKP) was introduced in Google Chrome and Firefox to counteract 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. Worse, if configured incorrectly, the header could completely disable website access for the time specified in the max‑age parameter (two months, in the example above). The header was deprecated in favor of certificate transparency logs and the Expect-CT header—but that one didn’t last long, either…

(Deprecated) Expect-CT

With HPKP gone, the recommended way to prevent website certificate spoofing was to use the Expect-CT header to indicate that only new certificates added to Certificate Transparency logs should be accepted. This proved another dead end, and Mozilla now recommends avoiding the header and removing it wherever possible. A typical header looked something like this:

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

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

Security headers in action with Sven Morgenroth

It’s one thing to read about security headers, but seeing them in action is gives you a whole new appreciation of how they work (and when they don’t work). Invicti Staff Security Engineer 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 and other misconfigurations. 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.

Start testing for security misconfigurations today

 


Frequently asked questions

What are the main HTTP security headers for improving website security?

The two most important security headers are Content-Security-Policy (CSP) to define permitted content types and Strict-Transport-Security (HSTS) to enforce HTTPS connections. It’s also common to set X-Content-Type-Options to prevent MIME type sniffing by browsers.
 
Read our detailed white paper on HTTP security headers.

How do HTTP security headers improve web application resilience?

HTTP security headers play a crucial role in web security by mitigating risks associated with common attacks such as cross-site scripting (XSS), frame injection, and clickjacking. When set up correctly, they allow website owners to prevent entire classes of web application attacks already at the configuration level.
 
Learn more about cross-site scripting vulnerabilities and attacks.

How can website owners implement and configure HTTP security headers to improve their web security posture?

To effectively implement and configure HTTP security headers, both website owners and developers need to understand the place and purpose of each header and apply configurations tailored to their specific security requirements. This involves setting response headers on the web server (though some can also be set in application code) and then regularly testing for header misconfigurations using an automated scanner.
 
Learn how to set up Content-Security-Policy (CSP) headers and how they work.

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.