If you’re running a WordPress site on a Linux server running NGINX, then you might be interested in setting up a free SSL (HTTPS) certificate using Let’s Encrypt. The internet is moving towards HTTPS, and even Google is said to rank HTTPS pages higher than the standard HTTP pages. If you’re using WordPress, you have a few options to consider. You could serve your entire site through HTTPS, or you could select only your wp-admin directory and login page where a user might enter login credentials. Without HTTPS, these credentials would be sent in clear text, which could allow an attacker intercepting traffic to obtain that user’s credentials.

The current web standard is HTTP/1.1, but that is quickly moving to HTTP/2. If you’re going to be using HTTPS for your site, I would recommend enabling HTTP/2 because it’ll give you a performance boost and will be more compatible in the future.

There’s not an official plugin for Let’s Encrypt on NGINX, but there are plenty of reports showing it working well.


LEMP – NGINX, PHP7, MySQL Installation

Read LEMP Install NGINX PHP7 MySQL on Ubuntu 16.04 Server
Read LEMP Install NGINX PHP7 MySQL on CentOS 7 / RHEL / Fedora

Install Certbot – The Let’s Encrypt Client

I’m running an Ubuntu (Debian-based) server, so I can use apt-get for the installation.

sudo apt-get -y install letsencrypt

If you’re running another distro you can install git (using apt-get or yum, etc), then run:

sudo git clone https://github.com/certbot/certbot /opt/letsencrypt

You can now request a certificate for your domain. You’ll get prompted to provide your email address for the expiring notifications and accept the Terms:

export DOMAINS="example.com,www.example.com"
export DIR=/var/www/yourwebsite
sudo letsencrypt certonly -a webroot --webroot-path=$DIR -d $DOMAINS

You’ll obviously want to replace example.com with your URL and replace /var/www/yourwebsite with the path to your site’s HTML files.

If you get unauthorized errors while running the above script, you may need to add the following code into your server block for your website in your NGINX conf and restart NGINX:

# Allow access to the ACME Challenge for Let’s Encrypt
location ~ /.well-known/acme-challenge {
allow all;

Once you get your success message, the script will give you the path where it saved your certificates. They’re usually at /etc/letsencrypt/live/example.com like:

  • Your private key: /etc/letsencrypt/live/example.com/privkey.pem
  • Your certificate: /etc/letsencrypt/live/example.com/cert.pem
  • The intermediate certificates: /etc/letsencrypt/live/example.com/chain.pem
  • Your certificate and intermediate certificates concatenated in the correct order: /etc/letsencrypt/live/example.com/fullchain.pem

Configure NGINX and WordPress

Configure NGINX Server Block

Using the following configuration in your server block in NGINX will give you an A+ rating on SSL Labs as of October 2016. This will also enable HTTP/2 for the HTTPS section.

server {
server_name www.example.com;
listen 443 ssl http2;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
ssl_trusted_certificate /etc/letsencrypt/live/example.com/chain.pem;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_prefer_server_ciphers on;
ssl_session_timeout 10m;
ssl_session_cache shared:SSL:20m;
ssl_stapling on;
ssl_stapling_verify on;
add_header Strict-Transport-Security max-age=15768000;

You’ll want to include your other parameters in your server block, but this is the SSL HTTPS section. If you have users complaining about not being compatible with your site, you can change the ssl_ciphers string and add more compatible ciphers to the list. The above configuration offers the most secure configuration with less compatibility. For example, it won’t work with Windows XP and Internet Explorer 8.

After adding the above configuration to your NGINX conf, you’ll need to restart the NGINX service.

Automate Let’s Encrypt Renewal Process

With Linux, you can edit your /etc/crontab file and add an entry like this:

43 5 * * 1 /letsencrypt renew --quiet --post-hook "service nginx reload" >> /var/log/le-renew.log

Once the Let’s Encrypt client detects a renewal is available, it’ll renew the certificate and reload the NGINX service and add an entry into the log file.

If you want to redirect all of your HTTP requests to your HTTPS, add this into your server block:

location / {
if ($scheme = http) {
return 301 https://$server_name$request_uri;

If you already have a location / {} section, just add the if statement into your existing section.

If you want to redirect all of your website to the HTTPS version, also remember to go into your WordPress Admin console under Settings > General and change your WordPress Address and Site Address URLs to show HTTPS.

Security headers hardening

Now, what about the content / behavior of our website? Scott Helme created a great HTTP response headers analyzer to assess the security grade of our content based on headers. To get a better score, we can add a few more lines to our server block in our NGINX conf.

The X-Content-Type-Options header stops a browser from trying to MIME-sniff the content type and forces it to stick with the declared content-type.

add_header X-Content-Type-Options "nosniff" always;

The X-Frame-Options header tells the browser whether you want to allow your site to be framed or not. By preventing a browser from framing your site you can defend against attacks like clickjacking.

add_header X-Frame-Options "SAMEORIGIN" always;

The X-Xss-Protection header sets the configuration for the cross-site scripting filter built into most browsers.

add_header X-Xss-Protection "1";

The Content-Security-Policy header defines approved sources of content that the browser may load. It can be an effective countermeasure to Cross Site Scripting (XSS) attacks. WARNING! This header must be carefully planned before deploying it on production website as it could easily break stuff and prevent a website to load it’s content! Fortunately there is a “report mode” available. In the mode, the browser will only report any issue in the debug console but not actually block the content.

To enable:

add_header Content-Security-Policy "default-src 'self'";

For Report Mode:

add_header Content-Security-Policy-Report-Only "default-src 'self'";

Configure WordPress wp-config.php

The most important thing to secure on WordPress is your wp-admin login credentials. We can force the use of your encrypted HTTPS site by adding this line into the wp-config.php file:

define('FORCE_SSL_ADMIN', true);

A couple of other security precautions you can make include adding:

define('DISALLOW_FILE_MODS', true);
define('DISALLOW_FILE_EDIT', true);

Final Thoughts

If you’ve implemented the configuration above, let us know how it went. If you notice any errors, leave a comment below and I’ll try to help you out.

Do you have any other WordPress or server hardening configurations? Let us know in the comments!