Strengthen the security of websites with HTTP Security Headers 2023

Securing websites is tricky, and it may be hard to see what you're gaining from the effort. But if your site is compromised, you risk harming both your company's reputation and ranking in search results. Updated with deprecation of Expect-CT og X XSS-Protection

So you’ve tuned performance, SEO and web accessibility, Google Lighthouse in DevTools gives you the thumbs up and you sit back. And then you get the notion to check your site on webpagetest.org

It never hurts to get a “second opinion”, right?

If you work your way through this article, you’ll be well-equipped to turn your website’s Security Score from a red F to a spring green A.

WebPageTest.org after screenshot
You can improve your security score in few hours, including thorough testing.

What does a site with a low security score lack?

What WebPageTest.org and other services point out – if your WordPress site is like most – is the lack of HTTP Security Headers. These are a set of rules, or policies, that tells your browser how your website should behave, what your website is permitted to do, and in particular, what it is not permitted to do. For example, they may require that all communication to and from your website must be encrypted, and that your website is not allowed to execute code from other websites.

The Olsen Gang breaks into the Royal Theatre

Think of it as locking the front door as well as putting your valuables in the safe. If someone breaks into the house, you’ve made the burglar’s job a little harder.

With HTTP Security Headers in place, there is a reduced risk of your site being compromised, and should it happen anyway, the opportunities for abuse are greatly reduced.

What this article is not about

I assume you already have an SSL certificate on your website, so I won’t go into that here. However, I will show you how to ensure that the certificate is used, while you are editing your .htaccess file.

Other helpful ideas for securing your WordPress site include firewalls and 2-factor authentication, user privileges, backup and uptime monitoring. If you want to read more about these topics, please leave a comment at the bottom of the page.

Online services to check your security headers

Security Headers by Probely

At Probely you get an easy overview of the “raw headers” and their settings. Probely one of the few sites that checks for the new and still not so widely used Content Security Policy and not only if it is present, but also how it is configured.

The HTTP response headers that this site analyses provide huge levels of protection and it’s important that sites deploy them. Hopefully, by providing an easy mechanism to assess them, and further information on how to deploy missing headers, we can drive up the usage of security based headers across the web.

securityheaders.com

Mozilla Observatory

The next service I recommend is the Mozilla Observatory. It gives you actionable recommendations for improved settings, and has the most comprehensive “Content Security Policy analysis”, which reveals not only if, but also how you have configured CSP. It is also the most technical service, which is why I think SerpWorx and Probely also have their merits. This way you can get started securing your website without getting completely discouraged :)

The scores and grades offered by the Mozilla Observatory are designed to alert developers when they’re not taking advantage of the latest web security features, as recommended in Mozilla’s web security guidelines and server side TLS guidelines. Individual developers will need to determine which of these security technologies is right for their sites.

observatory.mozilla.org

Hardenize

Finally, there’s Hardenize, which checks your domain’s security in a broad sense. The check includes both the setup of the domain itself, the email services (SPF, DMARC etc) as well as the website itself including the somehow special HSTS preloading. Hardenize has quite helpful recommendations on how to improve the security of your website.

With so many security features to deploy and network services to configure, everybody needs help to understand what their networks look like.

hardenize.com
Hardenize report - all green
DNS, Email and WWW security check on Hardenize

How to add HTTP security headers to your website

You will need an Apache or LiteSpeed web server

The tips on this page assume that you have an Apache web server or the LiteSpeed variant, and that you have access to edit the .htaccess file, either via the control panel, FTP or a plugin like the very useful Htaccess File Editor.

Take a backup – and test one header at a time

Don’t mindlessly throw all the headers into your busy webshop on a payday. Try them out on a test site and get familiar with the process. Take a backup. Since you’re just making changes to the .htaccess file, which is a simple text file, you can copy all the existing content to notepad before you start.

Editing .htaccess with Htaccess File Editor

If you use the Htaccess File Editor plugin, you can edit .htaccess files directly within your WordPress Dashboard.

WP Htaccess Editor with the "Test Before Saving" button pointed out.
In the WP Htaccess Editor you can check for syntax errors before saving.

Remember to read the “Read carefully” section before you start. And use the Test Before Saving button before saving. This feature can catch some syntax errors, but is not a 100% guarantee.

Check if it works

To quickly check if an added security header is in effect:

  1. Open Developer Tools
  2. Select the tab Network
  3. Select your domain in the column Name
  4. Look under Response Headers. Can you find your new header?

It is also always a good idea to check the Console tab regularly for any error messages.

Chrome Developer Tools screenshot with the Response Headers tab opened.
HTTP Response Header in developer tools

Automatic 301 redirect to https (SSL/TLS encryption)

If you already have automatic redirect from http to https, for example with the free plugin Really Simple SSL, you don’t need the snippet below. They do exactly the same thing. However, if you choose to implement one or more of the recommended security headers and are therefore editing your .htaccess file anyway, you can use the snippet here and have one less plugin in your WordPress installation.

There are times when we need to tell users that a resource has moved, either temporarily or permanently. This is what we use Redirect and RedirectMatch for.

Apache Configuration: .htaccess
# Automatic 301 redirect to https
<IfModule mod_rewrite.c>
RewriteEngine on
RewriteCond %{HTTPS} !=on [NC]
RewriteRule ^(.*)$ https://%{HTTP_HOST}/$1 [R=301,L]
</IfModule>

Below are the headers you can most easily implement. For each header you will find a short description, a link to more information, and a code block you can paste into .htaccess. The line with the hash tag # is only seen as a comment by the server, so you can choose to omit it.

X Frame Options

The X-Frame-Options HTTP response header can be used to indicate whether or not a browser should be allowed to render a page in a <embed> or <object>. Sites can use this to avoid click-jacking attacks, by ensuring that their content is not embedded into other sites.

X Frame Options documentation
# X Frame Options
Header always set X-Frame-Options "SAMEORIGIN"

X XSS Protection (deprecated)

Non-standard: This feature is non-standard and is not on a standards track. Do not use it on production sites facing the Web: it will not work for every user. There may also be large incompatibilities between implementations and the behavior may change in the future.

X XSS Protection documentation
# X XSS-Protection (deprecated)
Header set X-XSS-Protection "0"

X-Content-Type-Options

The X-Content-Type-Options response HTTP header is a marker used by the server to indicate that the MIME types advertised in the Content-Type headers should not be changed and be followed. This is a way to opt out of MIME type sniffing, or, in other words, to say that the MIME types are deliberately configured.

X Content Type Options documentation
#  X Content-Type-Options
Header set X-Content-Type-Options "nosniff"

X Permitted Cross Domain Policies

Specifies if a cross-domain policy file (crossdomain.xml) is allowed. The file may define a policy to grant clients, such as Adobe’s Flash Player (now obsolete), Adobe Acrobat, Microsoft Silverlight (now obsolete), or Apache Flex, permission to handle data across domains that would otherwise be restricted due to the Same-Origin Policy.

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

X-Powered-By and Server

May be set by hosting environments or other frameworks and contains information about them while not providing any usefulness to the application or its visitors. Unset this header to avoid exposing potential vulnerabilities.

X-Powered-By documentation
# Remove Unwanted HTTP Response Headers (Litespeed + PHP)
Header unset X-Powered-By
Header unset Server

HTTP Strict Transport Security (HSTS)

If a website accepts a connection through HTTP and redirects to HTTPS, visitors may initially communicate with the non-encrypted version of the site before being redirected, if, for example, the visitor types http://www.foo.com/ or even just foo.com. This creates an opportunity for a man-in-the-middle attack. The redirect could be exploited to direct visitors to a malicious site instead of the secure version of the original site.

Strict Transport Security documentation
# Strict Transport Security
Header set Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" env=HTTPS 

Referrer Policy

The Referrer-Policy HTTP header controls how much referrer information (sent via the Referer header) should be included with requests. Aside from the HTTP header, you can set this policy in HTML.

Referrer Policy documentation
# Referrer Policy 
Header set Referrer-Policy "strict-origin-when-cross-origin"

Expect-CT (deprecated)

Deprecated: This feature is no longer recommended. Though some browsers might still support it, it may have already been removed from the relevant web standards, may be in the process of being dropped, or may only be kept for compatibility purposes. Avoid using it, and update existing code if possible.

Expect-CT documentation
# Expect-CT (deprecated)
# Header set Expect-CT "max-age=86400, enforce" env=HTTPS

Four extra advanced policies

Now it starts to get a bit messy and you should only implement the following policies if you have plenty of time. The effectiveness of policies, especially the Content Security Policy, depends heavily on whether they are properly tailored to your particular website. For example, if you tell a website not to use the microphone, then of course the microphone won’t work on the site, and if it was supposed to, then security will get in the way of functionality.

Bye bye Feature-Policy, hello Permissions-Policy

HTTP Toolkit

In addition, some of the policies are under redesign and still not widely supported by all browsers. So I have chosen some policies that are – for beginners if you will. They improve security compared to not having the policies implemented, they have been tested on several different sites, and they serve as a good starting point for more thorough inspection, should you wish to tighten things up even further one day.

Again, test your site thoroughly after implementation – and if the site breaks, just implement one policy at a time to see where you might go wrong.

Feature Policy

Feature Policy allows web developers to selectively enable, disable, and modify the behavior of certain features and APIs in the browser. It is similar to Content Security Policy but controls features instead of security behavior.

Feature Policy documentation
# Feature Policy (rudimentary policies supported by most browsers)
Header set Feature-Policy "microphone 'none'; camera 'none'"

Permissions Policy

Permissions Policy is a web platform API which gives a website the ability to allow or block the use of browser features in its own frame or in iframes that it embeds.

Permissions Policy documentation
# Permissions Policy (rudimentary policies supported by chrome and FF)
Header set Permissions-Policy "autoplay=(self), encrypted-media=(self), fullscreen=(self), geolocation=(self), midi=(self), payment=(self)"

Content Security Policy

Content 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. These attacks are used for everything from data theft, to site defacement, to malware distribution.

Content Security Policy documentation
# Content Security Policy (CSP - rudimentary policies enforcing https)
Header set Content-Security-Policy "default-src * data:; script-src https: data: 'unsafe-inline' 'unsafe-eval'; style-src https: 'unsafe-inline'"

ForceSecureCookie

As of LSWS v 5.4.9 build 2, a new directive ForceSecureCookie has been introduced to enforce secure , SameSite and httponly cookie attributes

LiteSpeed Alternative to Apache Header Edit
## ForceSecureCookie (LiteSpeed Set Cookie HTTPOnly Secure alternative)
<IfModule LiteSpeed>
	ForceSecureCookie same_site_strict
</IfModule>

The combined code for your .htacess file.

Below is the complete code I use on all my sites, including the SSL redirect stub, the eight simple policies and the four advanced ones, ready to paste into the top of .htaccess. And I’ll mention it again: remember backup and test :)

### BEGIN Improved Site Security 2023 by oldrup.dk - 2023-01-27

## Automatic 301 redirect to https

<IfModule mod_rewrite.c>
RewriteEngine on
RewriteCond %{HTTPS} !=on [NC]
RewriteRule ^(.*)$ https://%{HTTP_HOST}/$1 [R=301,L]
</IfModule>

## Additional security headers

<ifModule mod_headers.c>

# X Frame Options
Header always set X-Frame-Options "SAMEORIGIN"

# X XSS-Protection (deprecated)
Header set X-XSS-Protection "0"

#  X Content-Type-Options
Header set X-Content-Type-Options "nosniff"

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

# X-Powered-By and Server
Header unset X-Powered-By
Header unset Server

# Enable Strict Transport Security
Header set Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" env=HTTPS 

# Referrer Policy 
Header set Referrer-Policy "strict-origin-when-cross-origin"

# Expect-CT (deprecated)
# Header set Expect-CT "max-age=86400, enforce" env=HTTPS

## Advanced policies - basic implementation

# Feature Policy (rudimentary policies supported by most browsers)
Header set Feature-Policy "microphone 'none'; camera 'none'"

# Permissions Policy (rudimentary policies supported by chrome and FF)
Header set Permissions-Policy "autoplay=(self), encrypted-media=(self), fullscreen=(self), geolocation=(self), midi=(self), payment=(self)"

# Content Security Policy (CSP - rudimentary policies enforcing https)
Header set Content-Security-Policy "default-src * data:; script-src https: data: 'unsafe-inline' 'unsafe-eval'; style-src https: 'unsafe-inline'"

</IfModule>

## ForceSecureCookie (LiteSpeed Set Cookie HTTPOnly Secure alternative)
<IfModule LiteSpeed>
	ForceSecureCookie same_site_strict
</IfModule>

### END Improved Site Security 2023

Final words

Take it from someone who’s done both; prevention is far less time-consuming than cleaning up after a hacker attack.

What are you doing to secure your websites? Would you benefit from the suggestions in this article, and is this a topic you’d like to read more about? Then please leave a comment below. Thanks for staying with me so long :)

Share this article
Læs mere om Bjarne Oldrup, Sønderborg

Bjarne Oldrup

Web developer with a passion for WordPress, sustainable web design, GDPR and web accessibility.

Leave a Reply

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