Let’s Encrypt on Nginx and SSL Grade A+

Posted on October 28, 2016 at 6:38 pm

A few steps to install Let’s Encrypt on Debian with Nginx and score a A+ grade on SSL Labs. We will install Certbot to simplify the creation and renew of SSL certificates with Let’s Encrypt. Simple tutorial that can help you setup Let’s Encrypt with Nginx in just a few minutes.

Login via SSH on your server as root.

Install Certbot via apt-get using -t jessie-backports:

apt-get update
apt-get install certbot -t jessie-backports

*** Make sure to enable jessie-backports on APT sources.list.

Or install the latest version with git:

apt install -y git
cd /opt
git clone https://github.com/certbot/certbot
cd certbot
./certbot-auto -h

Or install the latest compiled binary version:

cd /usr/bin/
wget https://dl.eff.org/certbot-auto
chmod a+x certbot-auto
mv certbot-auto certbot

Generate DH param (we’ll use this later):

openssl dhparam -out /etc/letsencrypt/dhparams.pem 2048

Let’s Encrypt will try to validate your domain name with an HTTP GET request on a special folder that is automatically created by certbot on your website root path, example:

"GET /.well-known/acme-challenge/%RANDOM_STRING% HTTP/1.1"

Make sure Nginx does not block access to “/.well-known/” folder:

# Deny access to .htaccess-like files
location ~ /\. {
    deny all;
}
 
# Allow access to .well-known directory (Let's Encrypt)
location ^~ /.well-known/ {
    allow all;
}

Generate the certificate for example.com (and www.example.com);

certbot certonly --webroot -w /var/www/vhosts/example.com/public -d www.example.com -d example.com --non-interactive --agree-tos --email your@email.com

You should see a new folder named “www.example.com” on:

/etc/letsencrypt/live/

Edit the Nginx vhost config file as this:

# Redirect HTTP traffic to HTTPS
server {
        listen 80;
        server_name example.com www.example.com;
        access_log off;
        error_log off;
        return 301 https://www.example.com$request_uri;
}
# Redirect https://example.com to https://www.example.com
server {
        listen 443 ssl http2;
        server_name example.com;
        ssl on;
        ssl_ciphers  EECDH+ECDSA+AESGCM:EECDH+aRSA+AESGCM:EECDH+ECDSA+SHA384:EECDH+ECDSA+SHA256:EECDH+aRSA+SHA384:EECDH+aRSA+SHA256:EECDH:EDH+aRSA:HIGH:!aNULL:!eNULL:!LOW:!RC4:!3DES:!MD5:!EXP:!PSK:!SRP:!SEED:!DSS:!CAMELLIA;
        ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
        ssl_prefer_server_ciphers on;
        ssl_session_cache shared:SSL:50m;
        ssl_session_timeout 5m;
        ssl_stapling on;
        ssl_stapling_verify on;
        ssl_certificate      /etc/letsencrypt/live/www.example.com/fullchain.pem;
        ssl_certificate_key  /etc/letsencrypt/live/www.example.com/privkey.pem;
        ssl_trusted_certificate /etc/letsencrypt/live/www.example.com/chain.pem;
        ssl_dhparam /etc/letsencrypt/dhparams.pem; # Generated by running "openssl dhparam -out /etc/letsencrypt/dhparams.pem 2048" as root user
        add_header Strict-Transport-Security "max-age=31536000; includeSubdomains";
        add_header X-Frame-Options SAMEORIGIN;
        add_header X-Content-Type-Options nosniff;
        add_header X-XSS-Protection "1; mode=block";
        resolver 8.8.8.8;
        access_log off;
        error_log off;
        return 301 https://www.example.com$request_uri;
}
# Handle https://www.example.com
server {
        listen 443 ssl http2;
        server_name www.example.com;
        ssl on;
        ssl_ciphers  EECDH+ECDSA+AESGCM:EECDH+aRSA+AESGCM:EECDH+ECDSA+SHA384:EECDH+ECDSA+SHA256:EECDH+aRSA+SHA384:EECDH+aRSA+SHA256:EECDH:EDH+aRSA:HIGH:!aNULL:!eNULL:!LOW:!RC4:!3DES:!MD5:!EXP:!PSK:!SRP:!SEED:!DSS:!CAMELLIA;
        ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
        ssl_prefer_server_ciphers on;
        ssl_session_cache shared:SSL:50m;
        ssl_session_timeout 5m;
        ssl_stapling on;
        ssl_stapling_verify on;
        ssl_certificate      /etc/letsencrypt/live/www.example.com/fullchain.pem;
        ssl_certificate_key  /etc/letsencrypt/live/www.example.com/privkey.pem;
        ssl_trusted_certificate /etc/letsencrypt/live/www.example.com/chain.pem;
        ssl_dhparam /etc/letsencrypt/dhparams.pem; # Generated by running "openssl dhparam -out /etc/letsencrypt/dhparams.pem 2048" as root user
        add_header Strict-Transport-Security "max-age=31536000; includeSubdomains";
        add_header X-Frame-Options SAMEORIGIN;
        add_header X-Content-Type-Options nosniff;
        add_header X-XSS-Protection "1; mode=block"; 
        resolver 8.8.8.8;
 
        access_log /var/www/vhosts/example.com/logs/access.log main;
        error_log /var/www/vhosts/example.com/logs/error.log warn;
	root /var/www/vhosts/example.com/public;
	index index.html index.htm index.php;
        [...]

Some online tools that can help you configure Nginx for HTTPS:
Security/Server Side TLS – MozillaWiki
Mozilla SSL Configuration Generator

Restart Nginx service:

/etc/init.d/nginx restart

Now test your website on SSL Labs and you should get A+ grade:

ssl-labs-grade-a

The SSL certificate created by Let’s Encrypt is valid for 90 days.

To renew it before it is expired, just create a simple bash script that is run via cronjob every week or at the begin of each month. Here is the command used to renew all SSL certificates present in /etc/letsencrypt/renew/ directory:

certbot renew --webroot --noninteractive --post-hook "service nginx reload"

Example output:

-------------------------------------------------------------------------------
Processing /etc/letsencrypt/renewal/www.example.com.conf
-------------------------------------------------------------------------------
 
-------------------------------------------------------------------------------
Processing /etc/letsencrypt/renewal/www.test.com.conf
-------------------------------------------------------------------------------
 
The following certs are not due for renewal yet:
  /etc/letsencrypt/live/www.example.com/fullchain.pem (skipped)
  /etc/letsencrypt/live/www.test.com/fullchain.pem (skipped)
No renewals were attempted.

You may add this line in the /etc/crontab file:

0 */12 * * * root certbot renew --webroot --noninteractive --post-hook "service nginx reload"

References and useful links:

Let’s Encrypt – Free SSL/TLS Certificates
SSL Server Test (Powered by Qualys SSL Labs)
Mozilla SSL Configuration Generator
Check your HTTP Security Headers

Updated on May 15, 2017 at 11:32 pm

Other Posts

Updated Posts