Security Headers: A Concise Guide

on in Blog
Last modified on

Security is as essential as content and SEO, and thousands of websites get compromised due to misconfiguration or lack of protection.

Mitigate the security vulnerabilities by implementing the necessary secure HTTP response headers below.

Security Headers

Table of Contents

Note that all the Apache configuration goes into the .htaccess file in your website’s root (or in your httpd.conf) and all the Nginx configuration goes under the http block (unless otherwise instructed) in nginx.conf or any custom file you use.

Note that you need to restart your Nginx server after each change in order to see any results.


X-XSS-Protection header can prevent some level of XSS (cross-site-scripting) attacks.

There are four possible ways you can configure this header:

Parameter ValueMeaning
0XSS filter disabled
1XSS filter enabled and sanitized the page if attack detected
1;mode=blockXSS filter enabled and prevented rendering the page if attack detected
1;report= filter enabled and reported the violation if attack detected

Let’s implement 1;mode=block:


Header set X-XSS-Protection "1; mode=block"


add_header X-XSS-Protection "1; mode=block";

HTTP Strict Transport Security

HSTS (HTTP Strict Transport Security) header ensures all communication from a browser is sent over HTTPS (HTTP Secure). This prevents HTTPS click through prompts and redirects HTTP requests to HTTPS.

There are three parameters to configure:

Parameter ValueMeaning
max-ageDuration (in seconds) to tell a browser that requests are available only over HTTPS.
includeSubDomainsConfiguration is valid for subdomain as well.
preloadUse if you would like your domain to be included in the HSTS preload list

So let’s take an example of having HSTS configured for one year, including preloading for domain and subdomains:


Header set Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"


Add the following under the server (SSL) directive/block:

add_header Strict-Transport-Security 'max-age=31536000; includeSubDomains; preload';


Use the X-Frame-Options header to prevent the clickjacking vulnerability. By implementing this header, you instruct the website not to be embedded in a frame or an iframe.

You can configure the following three parameters:

Parameter ValueMeaning
SAMEORIGINFrame/iframe of content is only allowed from the same site origin.
DENYPrevent any domain to embed your content using frame/iframe.
ALLOW-FROMAllow framing the content only on particular URI.

Let’s take a look at how to implement SAMEORIGIN so no external domain can embed your web page.


Header always append X-Frame-Options DENY


Add the following under the server directive/block:

add_header X-Frame-Options "DENY";


Prevent MIME types security risk by adding this header to your web page’s HTTP response. Having this header instructs the browser to consider files types as defined and disallow content sniffing.


Header set X-Content-Type-Options nosniff


Add the following under the server directive/block:

add_header X-Content-Type-Options nosniff;

HTTP Public Key Pinning

Minimize the man-in-the-middle (MITM) attacks risk by pinning certificate. This is possible with HPKP (HTTP Public Key Pinning) header.

You can pin the root certificate public key or immediate certificate. At the time of writing, HPKP currently works in Firefox and Chrome and supports the SHA-256 hash algorithm.

There are four possible parameter configurations:

Parameter ValueMeaning
report-uri="url"Report to the specified URL if pin validation fails. This is optional.
pin-sha256="sha256key"Specify the pins here
max-age=Browser to remember the time in seconds that site is accessible only using one of the pinned keys.
IncludeSubDomainsThis is applicable on a subdomain as well.

Let’s see the HPKP header example from

public-key-pins-report-only:max-age=500; pin-sha256="WoiWRyIOVNa9ihaBciRSC7XHjliYS9VwUGOIud4PB18="; pin-sha256="r/mIkG3eEpVdm+u/ko/cwxzOMo1bk4TyHIlByibiA5E="; pin-sha256="q4PO2G2cbkZhZ82+JgmRUyGMoAeozA+BSXVXQWB8XWQ="; report-uri=

If this is something you need to implement on your website, then check the implementation guide written by Scott Helme.

Content Security Policy

Prevent XSS, clickjacking or code injection attacks by implementing the Content Security Policy (CSP) header in your web page HTTP response. CSP instructs the browser to only load allowed content.

There are multiple parameters possible to implement CSP, and you can refer OWASP for an idea. However, let’s go through two most used parameters.

Parameter ValueMeaning
default-srcLoad everything from a defined source
script-srcLoad only scripts from a defined source

The following example allows loading everything from the same origin:


Header set Content-Security-Policy "default-src 'self';"


Add the following under the server directive/block:

add_header Content-Security-Policy "default-src 'self';";


Using Adobe products like PDF? You can implement this header to instruct the browser how to handle the requests over a cross-domain. By implementing this header, you restrict loading your site’s assets from other domains to avoid resource abuse. Think of hot-linking.

There are a few options available:

noneNo policy is allowed
master-onlyAllow only the master policy
allEverything is allowed
by-content-onlyAllow only a certain type of content. Example – XML
by-ftp-onlyApplicable only for an FTP server


If you don’t want to allow any policy:

Header set X-Permitted-Cross-Domain-Policies "none"


And, let’s say you need to implement master-only, then add the following under the server directive/block:

add_header X-Permitted-Cross-Domain-Policies master-only;

Referrer Policy

Looking to control the referrer policy of your site? There are certain privacy and security benefits. However, not all the options are supported by all the browsers, so review your requirement before the implementation.

Referrer-Policy supports the following syntax:

no-referrerReferrer information will not be sent with the request.
no-referrer-when-downgradeThe default setting where referrer is sent to the same protocol as HTTP to HTTP, HTTPS to HTTPS.
unsafe-urlFull URL will be sent with the request.
same-originReferrer will be sent only for same origin site.
strict-originSend only when a protocol is HTTPS
strict-origin-when-cross-originThe full URL will be sent over a strict protocol like HTTPS
originSend the origin URL in all the requests
origin-when-cross-originSend FULL URL on the same origin. However, send only origin URL in other cases.


Header set Referrer-Policy "no-referrer-when-downgrade"


add_header Referrer-Policy no-referrer-when-downgrade;


A new header, still in experimental status, is instructing the browser to validate the connection with web servers for certificate transparency (CT).

The following three variables are available for the Expect-CT header:

max-ageIn seconds, for how long browser should cache the policy.
enforceAn optional directive to enforce the policy.
report-uriBrowser to send a report to the specified URL when valid certificate transparency not received.


Let’s assume you want to enforce this policy, report, and cache for 12 hours:

Header set Expect-CT 'enforce, max-age=43200, report-uri=""'


What if you want to report and cache for 1 hour?

add_header Expect-CT 'max-age=60, report-uri=""';


Control browser features such as geolocation, full-screen, speaker, USB, autoplay, speaker, vibrate, microphone, payment, VR, and more.


Let’s say you need to disable the full-screen feature:

Header always set Feature-Policy "fullscreen 'none' "

How about adding multiple features in a single line?

Header always set Feature-Policy "fullscreen 'none'; microphone 'none'"


add_header Feature-Policy "vibrate 'none';";

Or, disable geolocation, camera, and speaker:

add_header Feature-Policy "geolocation 'none'; camera 'none'; speaker 'none';";


The X-Powered-By response header is set by some frameworks and server-side languages (e.g., ASP.NET, PHP), and its value contains information about them (e.g., their name, version number). It doesn’t provide any value to users, contributes to header bloat, and in some cases, the information it provides can expose vulnerabilities.

If you can, you should disable the X-Powered-By header from the language/framework level (e.g., for PHP, you can do that by setting expose_php = off in php.ini). If not, read below:


<IfModule mod_headers.c>
    Header unset X-Powered-By
    Header always unset X-Powered-By


For generic Nginx configuration files (PHP-FPM or FastCGI), use the following in the location block:

# Hide PHP headers
fastcgi_hide_header X-Powered-By;
fastcgi_hide_header X-CF-Powered-By;

If you are using Nginx as a proxy, use the following in the server block:

# Hide PHP headers
proxy_hide_header X-Powered-By;
proxy_hide_header X-CF-Powered-By;


Error pages of your website, like 403 or 404 pages, contain the server signature (i.e., server version number, operating system and more). Such information could be misused by attackers. Revealing such information about your server/operating system/PHP version is a potential threat.

Use the instructions below to prevent the server from sending in the Server response header, its exact version number, the description of the generic OS-type or information about its compiled-in modules.


# Hide PHP headers
ServerSignature Off  

Header unset Server
Header always unset Server


Use the following in the http/server block:

http {
    server_tokens off;

Related posts