Application Security 101 – HTTP headers


20th January 2022

17 min read

This article is the first in a series that will discuss some of the most common issues with HTTP security headers, which are often relatively easy to implement and can have a significant impact on the overall security posture of your application. We’ve previously talked about the proactive and reactive styles of application development and security testing.

In this article, we discuss HTTP headers which are a common misconfiguration. With recent changes to some frequently used headers, we’re going to cover what each header is, does and how it offers you protection when using a web application.

Let’s take a look at each of the currently recommended security headers, their purpose and how to configure them as per security best practices. At the end of this blog, we’ll look at how popular web servers can be configured to include or modify HTTP response headers.

Before you read on:

  • Ideally, all the suggested security best practices found below should be tested, preferably in a test or development environment. If you can’t perform changes within separate environments, you should make backups of the current working configurations to ensure that you can revert to a working configuration should any changes break site functionality.
  • Not all browsers support every header. There is a handy tool that can be used to verify compatibility before HTTP headers are implemented. We recommend that you check this before implementing each of the headers discussed.

1. Strict-Transport-Security

The HTTP Strict Transport Security (HSTS) header forces browsers and other agents to interact with web servers over the encrypted HTTPS protocol, which secures users from Man in the Middle (MitM) attacks by protecting against protocol downgrade attacks. This header has been specified in RFC-6797.

You should ensure that your site is accessible over HTTPS before this header is implemented.

An example configuration of the HSTS header is shown below.

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


max-ageDefines the time in seconds that the browser or agent should be accessed only via encrypted HTTPS.
includeSubDomainsHSTS security controls are applied to all subdomains.
preloadCan be used to include the domain within Google Chrome’s HSTS preload list (Also used by other modern browsers).


2. X-Frame-Options

The X-Frame-Options HTTP header is used to define whether an application can be loaded in an iFrame within a third-party application. HTML iFrames are often used by malicious actors to create a fraudulent website, which mimics a legitimate application in an attempt to harvest user credentials. This is known as a “clickjacking” attack. This header is specified in RFC-7034

X-Frame-Options is easily implemented to deny a third-party application from loading your web application within iFrames. This can be achieved with the Content-Security-Policy (CSP) header, which we will discuss later. When this CSP directive has been implemented, the X-Frame-Options header becomes obsolete.

Deny ensures no rendering within a frame.

X-Frame-Options: deny


denyDeny rendering within frames.
sameoriginWill deny rendering where there is an origin mismatch.
allow-from:Allow-from directive allows administrators to specify any domains that allow the site to be rendered from.


3. X-Content-Type-Options

The X-Content-Type-Options header was introduced to mitigate the threat of MIME sniffing attacks, preventing the browser from interpreting files as a different MIME type than what is specified in the Content-Type HTTP header. This can be used to inject client-side code into a user’s browser to steal sensitive information such as session cookies. You can read more about mitigating MIME attacks in this insightful article from Mozilla.


nosniffwill prevent the browser from MIME-sniffing a response away from the declared content-type.


You can easily configure this header by setting the ‘nosniff’ directive, as demonstrated below.

X-Content-Type-Options: nosniff


4. Content-Security-Policy

The Content-Security-Policy (CSP) is a security standard that is used to mitigate XSS and clickjacking attacks and offers additional protections. It achieves this by allowing application developers to define where external resources (such as client-side JavaScript files) can be loaded from.

The CSP header is possibly the most difficult to configure correctly to achieve best security practice and maintain functionality. There are many directives that can be set, which without knowing their purpose, can lead to bypassing of intended protections if incorrectly configured.

There is a varying degree of support for each directive across browsers which adds to the complexity. Additional information on the CSP header and compatibility can be found on the Mozilla developer website. Configuring a secure but usable CSP can take some fine-tuning and will often take some experimenting to ensure security whilst maintaining the site’s desired functionality.

With that in mind, we will discuss the most commonly found misconfigured directive, the script-src directive. The script-src directive allows administrators to specify valid sources for JavaScript.

Take a look at the following examples.

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


This CSP on its own will only allow scripts to be loaded from the origin in which the document is served. This allows the browser to load JavaScript files from the same origin, where they have been included inside the web page. For example, where they have been specified in HTMLtag;

This is fine until you reach inline script tags; JavaScript that is loaded directly in the tag of an HTML document.

With the CSP header configured as above, this script won’t be executed as it’s in violation of the header configuration.

This is where the configuration of the CSP header can become complex, which can open a user to attack. Consider the next example.

Content-Security-Policy: script-src 'self' 'unsafe-inline' 'unsafe-eval'


Here we’ve added two additional sources, ‘unsafe-inline’ ‘unsafe-eval’, which allows the use of inline resources, such as inline script elements, javascript: URLs, inline event handlers, inline style elements, the use of eval(), and similar methods for creating code from strings respectively.

Now, our alert script from before will execute the below, as this no longer violates the CSP.

These sources are often implemented to loosen the restrictiveness of the CSP where there may be a considerable number of web pages that rely on inline scripts, event handlers and style elements. However, this allows malicious actors to perform Cross-Site Scripting (XSS) attacks. (Please note that we will discuss the (now deprecated) X-XSS-Protection header further down).

Can inline scripts be used safely with the CSP header?

Yes, there are two ways that the CSP can be configured to allow inline scripts. The first is to use a base64 encoded nonce value, the second is to use a SHA hash. CSP supports the use of SHA256/384 and 512 hashes. Examples of how to set this up can be found here.

As already mentioned, this is just a small example of the most common misconfiguration we see within the CSP header. However, with time, the CSP should be configured appropriately so that it provides maximum security benefits as well as maintains site functionality.

What about X-Frame-Options?

As touched on in the X-Frames-Options section, the CSP can also be configured to block the loading of sites within iFrames. This can be achieved by setting the following ‘frame-ancestors’ parameter, where ‘’ refers to external domains that are permitted to load the site within frames. Additional domains can be added in the same manner.

Content-Security-Policy: "frame-ancestors 'self' ''"


5. X-Permitted-Cross-Domain-Policies

This header is used to limit which data external resources, such as Adobe Flash and PDF documents, can access on the domain. Failure to set the X-Permitted- Cross-Domain-Policies header to “none” value allows other domains to embed the application’s data in their content.

If there is no requirement to load application data within web clients such as Adobe Flash Player or Adobe Acrobat (not limited to these), then the header should be configured as follows.

X-Permitted-Cross-Domain-Policies: none


noneWill prevent the browser from MIME-sniffing a response away from the declared content-type.
master-onlyOnly this master policy file is allowed.
by-content-type[HTTP/HTTPS only] Only policy files served with Content-Type: text/x-cross-domain-policy are allowed.
by-ftp-filename[FTP only] Only policy files whose filenames are crossdomain.xml (i.e. URLs ending in /crossdomain.xml) are allowed.
allAll policy files on this target domain are allowed.


6. Referrer-Policy

The Referrer-Policy header is used to determine what information is returned in the “Referrer” header. Failure to set an appropriate Referrer-Policy response header can lead to the disclosure of sensitive information to third-party web applications.

no-referrerThe Referrer header will be omitted entirely. No referrer information is sent along with requests.
no-referrer-when-downgradeThis is the user agent’s default behaviour if no policy is specified. The origin is sent as referrer to a-priori as-much-secure destination (HTTPS → HTTPS), but isn’t sent to a less secure destination (HTTPS → HTTP).
originOnly send the origin of the document as the referrer in all cases. (e.g. the document will send the referrer
origin-when-cross-originSend a full URL when performing a same-origin request, but only send the origin of the document in other cases.
same-originA referrer will be sent for same-site origins, but cross-origin requests will contain no referrer information.
strict-originOnly send the origin of the document as the referrer to a-priori as-much-secure destination (HTTPS → HTTPS), but don’t send it to a less secure destination (HTTPS → HTTP).
strict-origin-when-cross-originSend a full URL when performing a same-origin request, only send the origin of the document to a-priori as-much-secure destination (HTTPS → HTTPS), and send no header to a less secure destination (HTTPS → HTTP).

Send a full URL (stripped of parameters) when performing a same-origin or cross-origin request.

The following example ensures that the Referred header is ignored and no information is passed within requests.

Referrer-Policy: no-referrer


7. Clear-Site-Data

The Clear-Site-Data header provides functionality for application developers to control which data is persistently stored within the browser’s various storage locations (cookies, storage APIs, cache). This can be useful for web developers, as they can instruct the browser to remove sensitive information stored on the client-side, such as cookies, caches, and storage, during a logout process. Note: This is only supported in secure contexts (HTTPS).

The following example sets the header to clear all types of data.

Clear-Site-Data: "*"


“cache”Indicates that the server wishes to remove locally cached data for the origin of the response URL.
“cookies”Indicates that the server wishes to remove all cookies for the origin of the response URL. HTTP authentication credentials are also cleared out. This affects the entire registered domain, including subdomains.
“storage”Indicates that the server wishes to remove all DOM storage for the origin of the response URL.
“executionContexts”Indicates that the server wishes to reload all browsing contexts for the origin of the response. Currently, this value is only supported by a small subset of browsers.
Indicates that the server wishes to clear all types of data for the origin of the response. If more data types are added in future versions of this header, they will also be covered by it.


8. Cross-Origin-Embedder-Policy

The Cross-Origin-Embedder-Policy (COEP) header can be utilised to prevent a web browser from loading resources from third-party domains that don’t explicitly grant permission using Cross-Origin-Resource-Policy headers.

The following example header configuration can be used so that a document can only load resources from the same origin or those specifically identified as loadable from additional origins.

Cross-Origin-Embedder-Policy: require-corp


unsafe-noneAllows the document to fetch cross-origin resources without giving explicit permission through the CORS protocol or the Cross-Origin-Resource-Policy header (it is the default value).
require-corpA document can only load resources from the same origin, or resources explicitly marked as loadable from another origin.

It should be noted that the Cross-Origin-Opener-Policy will need to be set as well. Additional information on the COEP header can be found here.


9. Cross-Origin-Opener-Policy

The Cross-Origin-Opener-Policy (COOP) header is used to segregate cross-origin browsing contexts within a web browser. A browsing context is an environment in which a browser displays a document object to the user, for example, a tab, window or iframe. This header is used to prevent a specific set of cross-origin attacks known as XS-Leaks.

The following example header configuration can be used to isolate the browsing context exclusively to same-origin documents. Cross-origin documents are not loaded in the same browsing context.

Cross-Origin-Opener-Policy: same-origin


unsafe-noneAllows the document to be added to its opener’s browsing context group unless the opener itself has a COOP of same-origin or same-origin-allow-popups (it is the default value).
same-origin-allow-popupsRetains references to newly opened windows or tabs that either doesn’t set COOP or opt-outs of isolation by setting a COOP of unsafe-none.
same-originIsolates the browsing context exclusively to same-origin documents. Cross-origin documents are not loaded in the same browsing context.

Additional information on the implementation of COEP and COOP headers can be found here.


10. Cross-Origin-Resource-Policy

The Cross-Origin-Resource-Policy (CORP) header is used by developers to define whether a user’s browser is able to make requests to an application or web service from a different origin. The header is used to prevent side-channel and Cross-Site Script Inclusion attacks.

The following example header configuration can be used so that only requests from the same origin can read the resource.

Cross-Origin-Resource-Policy: same-origin


same-siteOnly requests from the same Site can read the resource.
same-originOnly requests from the same Origin (i.e. scheme + host + port) can read the resource.
cross-originRequests from any Origin (both same-site and cross-site) can read the resource. Browsers are using this policy when a CORP header is not specified.


11. Cache-Control

The Cache-Control header is used to instruct browsers and shared caches on how they should cache information. Sensitive information may be cached to local storage where it can be viewed by unauthorised malicious actors on devices such as shared computers in internet cafes.

This header is often misconfigured by still allowing caching even when set or is missing entirely, which can be a problem when users start to access sensitive data on shared devices as described above.

The following example configuration instructs the browser that no caching is allowed, any previously cached resources are cleared and provide support for HTTP/1.0 caches.

Cache-Control: no-store, max-age=0
Pragma: no-cache


must-revalidateIndicates that once a resource becomes stale, caches do not use their stale copy without successful validation on the origin server.
no-cacheThe response may be stored in any cache, even if the response is normally non-cacheable. However, the stored response MUST always go through validation with the origin server first before using it.
no-storeThe response may not be stored in any cache.
no-transformAn intermediate cache or proxy cannot edit the response body, Content-Encoding, Content-Range, or Content-Type.
publicThe response may be stored in any cache, even if the response is normally non-cacheable.
privateThe response may be stored only in a browser’s cache, even if the response is normally non-cacheable.
proxy-revalidateLike must-revalidate, but only for shared caches (e.g., proxies). Ignored by private caches.
max-age=The maximum amount of time a resource is considered fresh. Unlike Expires, this directive is relative to the time of the request.
s-maxage=Overrides max-age or the Expires header, but only for shared caches (e.g. proxies). Ignored by private caches.

For more information on HTTP caching, check the Mozilla Developer website here.


12. X-XSS-Protection

This header is now deprecated. We see many applications where this is still configured incorrectly. However, there’s a catch. The Content-Security-Policy can be utilised to protect users from XSS attacks, as long as it’s configured appropriately. It’s recommended that if support for legacy browsers isn’t required, this header should be disabled (shown below) and the CSP header used instead.

X-XSS-Protection: 0


Where this isn’t possible, the X-XSS-Protection header should be enabled.

Web Server Configuration

In the following section, we will look at how to implement the headers described above in several popular web servers, such as Apache, nginx and IIS. However, the configuration of most web servers will be similar and can likely be implemented in a similar way. You should check vendor-specific documentation for more information.

Some software packages, such as WordPress, provide plugins that can be used to easily configure HTTP response headers. However, such plugins can often introduce their own security risks and care should always be taken when downloading and installing third-party plugins.

Apache Server

The Apache extension ‘headers_module’ must be enabled in the Apache configuration to enable the configuration and modification of HTTP response headers within the Apache webserver.

It’s possible to load the module with the following command.

sudo a2enmod headers
// Restart the service
sudo service apache2 restart


Once enabled, you can now add configuration lines to your Apache2 config file. The location of this file may vary depending on the environment and your specific setup. For this demonstration, we will use the 000-default.conf file. However, you should use your own configuration file.

Our configuration file looks like the below.

ServerName localhost
DocumentRoot /var/www/html
DirectoryIndex test.html


From here, we can now continue to configure the HTTP response headers. For example, should we want to add the X-Frame-Options header to test that the site cannot be loaded into an iframe, we can add this in the format ‘Header Set HEADER-NAME VALUE’.

ServerName localhost
DocumentRoot /var/www/html
DirectoryIndex test.html
Header Set X-Frame-Options deny


Now, when we access our site, we can see that the X-Frame-Options header is set, and we are blocked from loading this site within an iframe. We can continue in the same manner to add headers and test the application.

ServerName localhost
DocumentRoot /var/www/html
DirectoryIndex test.html
Header Set X-Frame-Options deny
Header Set Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
Header Set X-Content-Type-Options "nosniff"
Header Set Content-Security-Policy "default-src 'self';"
Header Set Referrer-Policy "no-referrer"
Header Set X-Permitted-Cross-Domain-Policies "none"
Header Set Cross-Origin-Embedder-Policy "require-corp"
Header Set Cross-Origin-Opener-Policy "same-origin"
Header Set Cross-Origin-Resource-Policy "same-origin"
Header Set Cache-Control "no-store, max-age=0"
Header Set Pragma "no-cache"
Header Set X-XSS-Protection "0"


Please note

All changes to the configuration file require the Apache2 service to be restarted before the changes take effect. When we access our test site with a browser and check the Network tab inside the developer tools, we can see that the response headers set above are now returned by the webserver.


Nginx Server

Like the Apache server configuration, nginx requires modification of local config files (nginx.conf) to implement HTTP response headers. For nginx, we can add these in the format ‘add_header HEADER-NAME VALUE’.

After adding the above headers, our configuration file looks like the following:

user www-data;
worker_processes auto;
pid /run/;
include /etc/nginx/modules-enabled/*.conf;events {
worker_connections 768;
# multi_accept on;
}http {##
# Basic Settings
# Headers
Header Set X-Frame-Options deny
Header Set Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
Header Set X-Content-Type-Options "nosniff"
Header Set Content-Security-Policy "default-src 'self';"
Header Set Referrer-Policy "no-referrer"
Header Set X-Permitted-Cross-Domain-Policies "none"
Header Set Cross-Origin-Embedder-Policy "require-corp"
Header Set Cross-Origin-Opener-Policy "same-origin"
Header Set Cross-Origin-Resource-Policy "same-origin"
Header Set Cache-Control "no-store, max-age=0"
Header Set Pragma "no-cache"
Header Set X-XSS-Protection "0"


Again, after stopping the Apache web server and starting the nginx web server, we can see that the headers are still applied when accessing the test site.

Internet Information Services (IIS)

IIS can be configured with the IIS Manager. Within the Site, configure the ‘HTTP Response Headers’ setting for the required site. In this demo, we’re using the “Default Web Site”.


Once in the HTTP Response Headers setting, configure response headers by right-clicking and choosing “Add…”. Add each header and test as required, once complete you may have something similar to the below.

Restart the IIS service and access your site, you should now see the new HTTP response headers returned in HTTP responses.

HTTP headers are a relatively easy way to improve your application security. Keeping up to date with the most current headers will help you implement security best practices and protect your web application. Stay tuned as we’ll be revisiting HTTP security headers in this blog series to ensure you’re equipped with the knowledge you need to stay secure.


  • Insights
  • Labs

OWASP Top 10 2021 Released

The Open Web Application Security Project (OWASP) is a not-for-profit organisation that aims, through community-led open-source projects, to improve the security of web-based software. OWASP develop…

What is penetration testing and why is it important to use a CREST-approved provider?

Trusting the effectiveness of your IT security controls is crucial to mitigate risks and malicious access to your systems and the information they store. Penetration…

How secure use of the cloud can digitally transform your business

Companies that move towards digital transformation can innovate more quickly, scale efficiently and reduce risk to company assets. Businesses must keep up with growing customer…

How to prepare your business for secure cloud migration

The cloud holds a lot of potential for organisations. Moving your IT environment to the cloud provides flexibility and agility. It allows your team to…

Celebrating Sentrium’s contribution to cyber security

2020 is the year that remote working exploded. Businesses and the general public had to quickly adapt to new ways of working caused by the…

What is CREST and what are the benefits of using a CREST accredited company?

We’re delighted to announce that Sentrium Security is now a CREST accredited company! This is an exciting achievement for us and it’s great to be…

New Exchange RCE vulnerability actively exploited

Exchange admins now have another exploit to deal with despite still reeling from a number of high profile attacks this year including ProxyLogon and ProxyShell.…

How effective is secure code review for discovering vulnerabilities?

We’ve recently discussed application security and the trend we’re seeing in which companies are increasingly implementing security early on in the Software Development Life Cycle…

Application Security (AppSec)

There is a movement in the IT security world that is gaining traction, and it is based around the implementation of security within applications from…

Enhancing Security in your Software Development LifeCycle – Dealing with Dependencies

The adoption of agile practices has resulted in the emergence of shift-lift testing, where testing is performed much earlier in the Software Development LifeCycle (SDLC).…

Exchange Server Emergency Mitigation Service

It has been a tough few months for Microsoft. After the SolarWinds/NOBELLIUM attacks, Microsoft Exchange customers were afflicted with a slew of vulnerabilities. In March…