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.
Table of Contents
- X-XSS-Protection
- HTTP Strict Transport Security
- X-Frame-Options
- X-Content-Type-Options
- HTTP Public Key Pinning
- Content Security Policy
- X-Permitted-Cross-Domain-Policies
- Referrer Policy
- Expect-CT
- Feature-Policy
- X-Powered-By
- Server
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
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 Value | Meaning |
0 | XSS filter disabled |
1 | XSS filter enabled and sanitized the page if attack detected |
1;mode=block | XSS filter enabled and prevented rendering the page if attack detected |
1;report=https://example.com/report_URI | XSS filter enabled and reported the violation if attack detected |
Let’s implement 1;mode=block
:
Apache
Header set X-XSS-Protection "1; mode=block"
Nginx
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 Value | Meaning |
max-age | Duration (in seconds) to tell a browser that requests are available only over HTTPS. |
includeSubDomains | Configuration is valid for subdomain as well. |
preload | Use 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:
Apache
Header set Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
Nginx
Add the following under the server
(SSL) directive/block:
add_header Strict-Transport-Security 'max-age=31536000; includeSubDomains; preload';
X-Frame-Options
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 Value | Meaning |
SAMEORIGIN | Frame/iframe of content is only allowed from the same site origin. |
DENY | Prevent any domain to embed your content using frame/iframe. |
ALLOW-FROM | Allow 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.
Apache
Header always append X-Frame-Options DENY
Nginx
Add the following under the server
directive/block:
add_header X-Frame-Options "DENY";
X-Content-Type-Options
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.
Apache
Header set X-Content-Type-Options nosniff
Nginx
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 Value | Meaning |
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. |
IncludeSubDomains | This is applicable on a subdomain as well. |
Let’s see the HPKP header example from facebook.com:
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=http://reports.fb.com/hpkp/
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 Value | Meaning |
default-src | Load everything from a defined source |
script-src | Load only scripts from a defined source |
The following example allows loading everything from the same origin:
Apache
Header set Content-Security-Policy "default-src 'self';"
Nginx
Add the following under the server
directive/block:
add_header Content-Security-Policy "default-src 'self';";
X-Permitted-Cross-Domain-Policies
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:
Value | Description |
none | No policy is allowed |
master-only | Allow only the master policy |
all | Everything is allowed |
by-content-only | Allow only a certain type of content. Example – XML |
by-ftp-only | Applicable only for an FTP server |
Apache
If you don’t want to allow any policy:
Header set X-Permitted-Cross-Domain-Policies "none"
Nginx
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:
Value | Description |
no-referrer | Referrer information will not be sent with the request. |
no-referrer-when-downgrade | The default setting where referrer is sent to the same protocol as HTTP to HTTP, HTTPS to HTTPS. |
unsafe-url | Full URL will be sent with the request. |
same-origin | Referrer will be sent only for same origin site. |
strict-origin | Send only when a protocol is HTTPS |
strict-origin-when-cross-origin | The full URL will be sent over a strict protocol like HTTPS |
origin | Send the origin URL in all the requests |
origin-when-cross-origin | Send FULL URL on the same origin. However, send only origin URL in other cases. |
Apache
Header set Referrer-Policy "no-referrer-when-downgrade"
Nginx
add_header Referrer-Policy no-referrer-when-downgrade;
Expect-CT
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:
Value | Description |
max-age | In seconds, for how long browser should cache the policy. |
enforce | An optional directive to enforce the policy. |
report-uri | Browser to send a report to the specified URL when valid certificate transparency not received. |
Apache
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="https://example.com/report"'
Nginx
What if you want to report and cache for 1 hour?
add_header Expect-CT 'max-age=60, report-uri="https://example.com/report"';
Feature-Policy
Control browser features such as geolocation, full-screen, speaker, USB, autoplay, speaker, vibrate, microphone, payment, VR, and more.
Apache
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'"
Nginx
add_header Feature-Policy "vibrate 'none';";
Or, disable geolocation, camera, and speaker:
add_header Feature-Policy "geolocation 'none'; camera 'none'; speaker 'none';";
X-Powered-By
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:
Apache
<IfModule mod_headers.c>
Header unset X-Powered-By
Header always unset X-Powered-By
</IfModule>
Nginx
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;
Server
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.
Apache
# Hide PHP headers
ServerSignature Off
Header unset Server
Header always unset Server
Nginx
Use the following in the http/server
block:
http {
...
server_tokens off;
...
}