There are many options to make a WordPress website more secure. For some of those options you might use additional plugins. But there is an easy way to increase the security by setting only some server settings. But before I go into details, let’s find out how to get a current security status of your site.
Mozilla Observatory
An excellent tool to test your website for security is the Mozilla Observatory tool. This tool scans not only the headers you server is sending, it will also scan TLS and SSH settings and will use some third-party tools to give you a brief overview. A result might look like this, before you adjust any settings:
Further down the page you will see all the tests that were passed and those who have failed, leading to this very bad score. In this example, the following test scores:
Test | Pass | Score | Reason |
---|---|---|---|
Content Security Policy | Pass | Score -25 | Content Security Policy (CSP) header not implemented |
Cookies | Pass | Score 0 | No cookies detected |
Cross-origin Resource Sharing | Pass | Score 0 | Content is not visible via cross-origin resource sharing (CORS) files or headers |
HTTP Public Key Pinning | Pass | Score 0 | HTTP Public Key Pinning (HPKP) header not implemented (optional) |
HTTP Strict Transport Security | Pass | Score 0 | HTTP Strict Transport Security (HSTS) header set to a minimum of six months (15768000) |
Redirection | Pass | Score 0 | Initial redirection is to HTTPS on same host, final destination is HTTPS |
Referrer Policy | Pass | Score 0 | Referrer-Policy header not implemented (optional) |
Subresource Integrity | Pass | Score -50 | Subresource Integrity (SRI) not implemented, and external scripts are loaded over HTTP or use protocol-relative URLs via src="//..." |
X-Content-Type-Options | Pass | Score -5 | X-Content-Type-Options header not implemented |
X-Frame-Options | Pass | Score -20 | X-Frame-Options (XFO) header not implemented |
X-XSS-Protection | Pass | Score -10 | X-XSS-Protection header not implemented |
Not all of those failed test can be fixed for a WordPress website, but we can improve on some things.
Set server headers for better security
In this blog post we will try to get the website to a “B” rating at least. To achieve this, we will add some server headers to increase the security.
Apache
On an Apache webserver, which many shared hostings are using, you can set those headers using the .htaccess
file in the root directory of your WordPress installation. Simply add these lines to the start or end of the file:
<IfModule mod_headers.c>
Header always set X-Frame-Options "SAMEORIGIN"
Header always set X-XSS-Protection "1; mode=block"
Header always set X-Content-Type-Options "nosniff"
Header always set Referrer-Policy "strict-origin-when-cross-origin"
Header always set Content-Security-Policy "default-src * data: 'unsafe-eval' 'unsafe-inline'"
</IfModule>
nginx
If you use the nginx webserver, you have to set those headers in the configuration files, either globally or per site. In the configuration file, add these lines:
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Content-Security-Policy "default-src * data: 'unsafe-eval' 'unsafe-inline'" always;
You don’t have to understand all those settings. The Content-Security-Policy
is the one where we need to use a not so secure option, as otherwise your WordPress site will most break.
New result
After we added those changes we will hopefully have passed a lot more tests. This is how it looks like on the example site:
I was expecting a “B” rating. But for some reasons it was only a “D” rating. The “Subresource Integrity” was still having a score of “-50”, so I searched for the reasons. I’ve found an embedded newsletter form using a “protocol-relative URL”. Additionally the plugin “Google Analytics for WordPress by MonsterInsights” was adding the script tag for the Google Tag Manager with a protocol-relative URL as well. Thankfully there was a filter I could use to overwrite the path adding the HTTPS protocol using a must-use plugin.
Final result
After fixing those changes and scanning the page again (you can initialize a rescan only every 5 minutes), I got the result I was hoping for:
It’s possible to get a “B+” rating, but only if you don’t embed any JavaScript files from an external domain. Otherwise you will fail the “Subresource Integrity” test with the result “Subresource Integrity (SRI) not implemented, but all external scripts are loaded over HTTPS”.
Conclusion
Improving the security of a WordPress site does not always needs the use of additional plugins. Sometimes a few lines of code in your server configuration is all you need. As this is also possible on most shared hostings using the .htaccess
file, there is no reason not to add it to your site as well.