How to add Security Headers to your webserver

This post describes how to add specific security headers to your apache2 or nginx webservers. This eliminates some client side attack vectors in the client browsers using your site. Additionally this makes your site more trustworthy which may have an influence on your search engine score.

How to test your website

There are multiple sites available which can test your website regarding various security settings. The following table shows three of them.

URLDescription
https://securityheaders.comScans mainly for the security headers described in this post.
https://internet.nlScans for security headers, dnssec, https parameters and more
https://observatory.mozilla.orgScans mainly for headers, but with detailed analysis for some.
Some security scanners for websites

The Security Headers

We are concentrating on the following seven security headers which are the most important ones at the moment.

Header typeExample ValueExplanation
Strict-Transport-Securitymax-age=31536000Enforces https connection to the website, see also https://en.wikipedia.org/wiki/HTTP_Strict_Transport_Security
X-Frame-OptionsSAMEORIGINClickjacking protection header, which avoids frames from untrusted sources on your site. See also wikipedia.
X-Content-Type-OptionsnosniffWith this header the server tells the browser to strictly follow the MIME types in Content-Type headers and disable sniffing. See also wikipedia.
Referrer-Policyno-referrerThe Referrer-Policy header controls how much referrer info (sent with the Referer header) should be included with requests.
Permissions-Policygeolocation=(); midi=();....Permissions Policy tells the browser whether a feature can be used on a site or not. These are referred to as policy-controlled features. A list of features which can be controlled by this policz is available under https://github.com/w3c/webappsec-permissions-policy/blob/main/features.md. The Permissions API gates access to features based on user-granted permissions.
X-XSS-Protection1; mode=blockThis header enables cross site scripting protection feature in modern browsers.
Content-Security-Policyscript-src domain.orgContent Security Policy (CSP) is an added layer of security that helps to detect and mitigate certain types of attacks, including Cross-Site Scripting (XSS) and data injection attacks. The CSP can be configured very fine grained and thus closes a lot of attack vectors but this comes with additional management effort. See also https://en.wikipedia.org/wiki/Content_Security_Policy.
List of security headers

I will use values which I have applied on one of my sites. Please be aware that wordpress or other CMS systems need some special treatments to get a working setup, see section Problems with Content-Security-Policy for more information.

How to configure Security Headers for Nginx

In Nginx headers can be configured in the central nginx config file. The following example is for debian based systems.

user1@host:/etc/nginx$ sudo nano nginx.conf 

In the http section you can add the seven headers described before with add_header as seen below (for webpage domain.org).

http {
        ##
        # Basic Settings
        ##
        ....       
        add_header Strict-Transport-Security "max-age=31536000";
        add_header X-Frame-Options "SAMEORIGIN" always;
        add_header X-Content-Type-Options "nosniff" always;
        add_header Referrer-Policy "no-referrer";
        add_header Permissions-Policy "geolocation=(); midi=();notifications=();push=();sync-xhr=();accelerometer=(); gyroscope=(); magnetometer=();  payment=(); camera=(); microphone=();usb=(); xr=();speaker=(self);vibrate=);fullscreen=(self);";
       add_header X-XSS-Protection "1; mode=block";
        add_header Content-Security-Policy "default-src domain.org";
}

After the changes are saved nginx need to be restarted, e.g. with

user1@host:/sudo systemctl restart nginx

How to configure Security Headers for Apache2

For apache2 webserver the headers can be added in the config for each site. It is important that the mod_headers module is activated. You can do that with

user1@host:/sudo a2enmod headers
user1@host:/sudo systemctl restart apache2

Again the site I will configure is domain.org.

user1@host:/etc/apache2/sites-available# nano domain.org-le-ssl.conf 

In the mod_headers section add the following:

</IfModule>
<IfModule mod_headers.c>
  Header always set Strict-Transport-Security "max-age=31536000;" 
  Header always append X-Frame-Options SAMEORIGIN
  Header always append X-Content-Type-Options "nosniff"
  Header always set Permissions-Policy "geolocation=(); midi=();notifications=()push=();sync-xhr=();accelerometer=(); gyroscope=(); magnetometer=();  payment=(); camera=(); microphone=();usb=(); xr=();speaker=(self);vibrate=);fullscreen=(self);";
  Header always set Referrer-Policy "no-referrer"
  Header always set X-XSS-Protection "1; mode=block"
  Header always set Content-Security-Policy "default-src 'self' "
</IfModule>

After the changes are saved apache2 need to be restarted, e.g. with

user1@host:/sudo systemctl restart apache2

Problems with Content-Security-Policy

Especially the Content-Security-Policy header can cause problems and can make your site not working because it prevents the browser from loading needed resources. The general strategy here is that you check blocked resources with the Developer tools in your browser (usually F12) and add this as trusted source (e.g. script-src) to your header definition. Additionally some CMS like wordpress need, depending on the used plugins, script-src unsafe-inline and unsafe-eval which are in general unsecure options. There are tons of articles in the internet how to prevent even that securely, but this means alot of effort. The following example is for a wordpress site with google analytics and google adsense.

 Content-Security-Policy "script-src 'self' 'unsafe-inline' 'unsafe-eval'  *.googleapis.com *.googlesyndication.com *.googletagmanager.com *.googleadservices.com *.google.com"

Please note that only script-src is secured here, not style etc. (e.g again lots of effort for a wordpress site). Additionally unsafe-inline and unsafe-eval are not considered secure.

Leave a Reply

Your email address will not be published. Required fields are marked *