In the previous three articles we have reviewed how to create a secure password and how to make it difficult to know which user is the administrator of your WordPress installation, how to protect the wp-admin folder and one of the most critical files, as far as security is concerned: wp-config. As well as certain plugins that can help us to perform these tasks and best practices to modify the prefix of the tables of the WordPress DB, how to disable the WordPress file editor and how to configure and modify the security Keys and Salts.
Today I will go over four more actions to make your WordPress installation more secure with four actions (two today and two on Friday):
- Add HTTP security headers to our server.
- Assign the appropriate permissions to the folders and files on our Web server.
- Make your WordPress not look like a WordPress, and not give clues.
- Schedule a backup system.
Table of contents
Add HTTP security headers to our server.
First of all, what is an HTTP header?
When a communication takes place between a browser and a Web server, both send a series of requests to each other, the end result of which is, or usually is, the sending of the content requested by the browser from the server. Among these requests are the HTTP headers.
Let's look at an example:
When you type in the address bar of the browser, whatever it is, an Url, for example, http://icarlosmd6.sg-host.com/blog/ and press enter, you send to the server where the Web is hosted the request for a specific content. At the same time that you send that request, you inform the server of several things:
1.- The content you want to download in your browser, in this case it is a page: /blog/.
2.- The host you request this page from (carlosmd6.sg-host.com).
3.- The browser and operating system making the request.
4.- The language in which the browser is configured by default.
5.- Whether or not the browser accepts file compression in the transmission and the type or types of compression accepted.
6.- If it exists, the referrer or referrer from which the visit comes.
7.- If the browser already has a cookie or cookies stored, it will send them to the server.
8.- The request to the server of its status, i.e. if it is available (keep alive).
... etc
When all these requests arrive, the server responds to them one by one through another series of headers and, at the end, includes the HTML code with which the browser will build the page you see.
All this information travels in what are called HTTP headers and does so transparently to the user who does not know, and does not need to know at first, that this is happening.

If you are somewhat familiar with Google Analytics and with the measurement protocol that it uses to create the urls that make up the hit, you can now understand where the JavaScript gets this information from.
Well, having defined what an HTTP header is, let's get down to business.
There are six security headers that we can add to any web server, these headers are:
- X-Frame- Options.
- X-Content-Type-Options.
- X-XSS-Protection.
- Strict-Transport-Security.
- Content-Security-Policy.
- Public-Key-Pins.
X-Frame- Options.
With this header, we protect users from possible attacks of clickjakingattacks, i.e. that someone sneaks a malicious code into a frame or, for example, a submit button. Enabling this header prevents the page from being opened in an iframe. The values it can take are: Deny, Allowfrom and SameOrigin:
DENY: As the name suggests, it does not allow an iFrame to be executed in any case.
ALLOWFROM: Allows content to be used in the urls we specify.
SAMEORIGIN: Allows the content to be used only from the domain where the content is located, not from any other domain.
X-Content-Type-Options
This header prevents a CSS, JS, JPG, PNG, JPG, PNG... file from loading if its MIME-Type is different. It happens in many cases that malicious scripts are masked within the Web by modifying its extension, for example if we see the file name it will have a jpg extension and we will think it is an image, but in reality its content is a php or JavaScript script that executes the malicious code.
To prevent this from happening, simply set this header to nosniff. This will block the upload of files that do not match the extension declared in their MIME-Type with the extension they show. In this way, we will be able to block those files that could execute malicious code and that have been introduced in our server in a masked way.
X-XSS-Protection
This header enables the XSS filter, an additional layer of security that is already implemented in the browsers themselves.
An XSS (Cross-site scripting) attack takes advantage of an insecurity that many web applications, and let's remember that WordPress is an application, to inject code in the form of JavaScript or another programming language into the pages that a user visits.
There are two values for this header: 0 (disabled) and 1 (enabled). Obviously we will use the value 1.
Strict-Transport-Security
With this header we can implement HTTP Strict Transport Security (HSTS)a web security policy mechanism by which a web server informs browsers that they must interact with it only through secure HTTP connections (SSL/TLS).
The vulnerability it covers is to prevent an SSL extraction attack that would cause all secure connections to become normal connections, i.e. HTTPS connections to become HTTP connections.
Content-Security-Policy
With this header we can implement Content Security Policy, a security standard that allows us to prevent various types of code injection attacks.
The implementation of this standard will depend on the environment in which we work with our Web. If this environment is simple and all the contents are served from the server itself, it will be enough with the line that I will leave in the example below.
But if we set up an environment in which we make use of CDN (Content Delivery Networks), or we have banners served by third parties, such as Google AdSense, or we use Google Fonts that we don't have on the server itself, or Analytics through Google Tag Manager... that is, if our website needs third parties to serve the content, then things get a bit more complicated and we will have to generate a more complex header. In this second case, you can generate your Content-Security-Policy header using CSP is Awesome an online generator that will help us with this task.
Public-Key-Pins
By means of this header we will be able to implement HTTP Plublic Key Pins (HPKP) a security mechanism that will only be necessary if your website is HTTPS, i.e. if it has a security certificate.
The idea is to prevent an attacker from tampering with the certification chain that is generated when visiting a secure website and that guarantees that the security certificate is valid and correct for this domain.
Bad news, to implement this security header it is necessary to do a complex configuration that, frankly, I don't know how to do. So, if you want to do it, take a look at this presentation by Pablo González and Carmen Torrano or this document in English.
Adding security headers to WordPress
Ok and now, how do I add the security headers to my WordPress? The answer is very simple: It depends 🙂 🙂
No seriously, as always, you have two ways, add a few lines to your functions.php file or your custom functions plugin or add a few lines to your .httaccess file. Personally I would go with the second option, but here are both.
In functions.php or your custom functions plugin you should add the following. Remember what I told you about HTTP Content Security Policy Headers:
/* Añadir las cabeceras HTTP de seguridad a WordPress */
function add_security_headers() {
header( 'X-Frame-Options: SAMEORIGIN' );
header( 'X-Content-Type-Options: nosniff' );
header( 'X-XSS-Protection: 1;mode=block' );
header( 'Strict-Transport-Security: max-age=10886400' );
/* Cabecera de Seguridad HTTP Content Security Policy. Ten en cuenta que esta configuración es correcta únicamente si es tu servidor quien sirve todos los contenidos (css, javascripts, imágenes, etiquetas…) en el caso de no ser así, deberás generar tu propia cabecera personalizada con esta herramienta online http://cspisawesome.com/ */
header( 'Content-Security-Policy: default-src self' );
add_action( 'send_headers', 'add_security_headers' );
If we choose to do it from .httaccess, we will have to add the following:
# Añadir las cabeceras HTTP de seguridad a WordPress
Header always append X-Frame-Options SAMEORIGIN
Header set X-Content-Type-Options nosniff
Header set X-XSS-Protection "1; mode=block"
Header set Strict-Transport-Security "max-age=10886400; includeSubDomains; preload"
# Cabecera de Seguridad HTTP Content Security Policy. Ten en cuenta que esta configuración es correcta únicamente si es tu servidor quien sirve todos los contenidos (css, javascripts, imágenes, etiquetas…) en el caso de no ser así, deberás generar tu propia cabecera personalizada con esta herramienta online http://cspisawesome.com/ */
Header set Content-Security-Policy "default-src 'self'"
In short, by including these headers, we will be able to protect the communication between our server and the browser that makes a request to it and prevent "bugs" from sneaking into the server or, if they have already sneaked in, from being executed.
Assign the appropriate permissions to the folders and files on our Web server.
I already talked about this in the second article when I talked about assigning read-only permissions to the wp-configfile.
As a rule, the permissions of the folders in your WordPress installation should be 755 and the permissions of the files 644. Additionally, for the wp-config file, and also for the .httaccess file, these permissions should be set to 444.
You may be wondering, what the hell do these numbers mean?
I won't bore you too much, just tell you that these permissions are assigned to three entities: The owner or administrator or root, the group and the public or visitor. That's why there are three numbers.
As for the values, they are obtained from the sum of the permits held by each entity, the unique values being the following:
4 - Reading permission.
2 - Writing permission.
1 - Permission to execute.
Thus 7, which is equal to 4+2+1, means that the entity has read write and execute permission, 6, which is 4+2, means that it has only read and write permission, but not execute permission, 5 (4+1) is read and execute and 3 (2+1) is write and execute.
These permissions, as I already explained when I talked about protect the wp-config file, are enabled from the FTP of our website.

Don't go yet
That's enough for today, the next will be the last in a series of articles showing you how to make your WordPress not advertise to the world as such and how to set up a backup system for your WordPress installation and database.
I invite you to leave your impressions and/or doubts in the contact form and to suggest new topics that you would like me to cover in these tutorials. I will be happy to answer you by email and write in this blog.