Secure Your Nginx Server with HTTPS Using Let’s Encrypt and Certbot

Beinset Hounwanou
6 min readMay 26, 2023

--

Hey there ! Got a cool Nginx server up and running? Fantastic! But let’s add some shimmering armor to it. Yes, we’re talking about HTTPS. With this nifty guide, you’ll be setting up a rock-solid HTTPS fortress for your Nginx server in no time.

Intro

Before we dive into the configurations, let’s clarify what we are setting up.

HTTPS (Hypertext Transfer Protocol Secure) is an internet communication protocol that protects the integrity and confidentiality of data between the user’s computer and the ‘site’ server. It uses SSL/TLS protocol to encrypt communications. To establish an HTTPS connection, you need an SSL certificate from a Certificate Authority (CA). Let’s Encrypt is one of such CAs, and it provides SSL certificates for free!

We’ll use Certbot, an open-source client that fetches and deploys SSL/TLS certificates from Let’s Encrypt.

We’ll cover both Docker and non-Docker based Nginx setups. So, whether you’re a Docker fan or not, you’re all covered!

Prerequisites

  • An Nginx server or Docker environment running Nginx
  • Domain name (example.com) pointing to your server IP. ( Ensure that the “A Record” of your domain is correctly configured to point to your server’s IP address and “AAAA record” if your server supports IPv6.)
  • Access to the server with SSH.
  • Port 80 open and available : Make sure there are no processes occupying port 80, as Certbot’s standalone mode uses this port to validate domain ownership during the initial certificate request. To check whether a process is using port 80, run the following command:
sudo lsof -i -P -n | grep 80

This will list all processes currently utilizing port 80.

If Nginx is the process running on port 80 and you need to free up the port, you can stop it by running:

sudo systemctl stop nginx

If a different process is using port 80, you can stop it by using the kill command along with the Process ID (PID), which is displayed in the output of the previous command:

kill -9 [PID]

Configuration Steps

Step 1: Installing Certbot

SSH into your server and run the following commands:

sudo apt update
sudo apt install certbot

Step 2: Obtaining SSL Certificate

To fetch the SSL certificate for your domain, execute the following:

sudo certbot certonly --standalone -d example.com

Follow the prompts to complete the process. Once done, Certbot will create a new directory /etc/letsencrypt/live/example.com/ that contains the SSL certificate (fullchain.pem) and private key (privkey.pem).

Step 3: Nginx Configuration

An NGINX configuration file is composed of different server blocks. Each server block is a way to define settings for a specific domain, allowing NGINX to respond to requests for different websites on the same server.

Here’s the content of an example NGINX configuration file:

server {
listen 80;
listen [::]:80;
server_name example.com;
# Redirect all HTTP requests to HTTPS
return 301 https://$host$request_uri;
}

server {
listen 443 ssl;
listen [::]:443 ssl;
server_name example.com;
# Path to SSL certificates
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

# Configure SSL settings according to your needs
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
ssl_ciphers ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;

# Configure additional security options according to your needs
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;

# Add the rest of your configuration here
...
}

Let’s break it down:

server {
listen 80;
listen [::]:80;
server_name example.com;
# Redirect all HTTP requests to HTTPS
return 301 https://$host$request_uri;
}
  • listen 80; and listen [::]:80;: These lines instruct NGINX to listen for incoming connections on port 80 (the default HTTP port) for both IPv4 and IPv6 addresses.
  • server_name example.com;: This line tells NGINX that this server block is for handling requests for "example.com".
  • return 301 https://$host$request_uri;: If a request is received, NGINX is instructed to send a 301 redirect to the client, redirecting them from the HTTP version of the site to the HTTPS version.

So in effect, a request to http://example.com is first handled by the first server block, which redirects it to https://example.com. This secure request is then handled by the second server block.

server {
listen 443 ssl;
listen [::]:443 ssl;
server_name example.com;
}
  • server: It starts the definition of a server block.
  • listen 443 ssl; and listen [::]:443 ssl;: These lines instruct NGINX to listen for incoming secure connections on port 443 (the default HTTPS port) for both IPv4 and IPv6 addresses.
  • server_name example.com;: This line tells NGINX that this server block is for handling requests for "example.com".

Notice there is ssl keyword after port number in listen directive. This is used to specify that the secure SSL/TLS protocol is to be used for handling connections on this port.

    # Path to SSL certificates
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

# Configure SSL settings according to your needs
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
ssl_ciphers ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;

# Configure additional security options according to your needs
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;

The rest of the directives in the Nginx configuration file concern the security settings of your Nginx server, especially those related to SSL/TLS.

  • ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;: This line points to the location of the SSL certificate for your domain.
  • ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;: This line points to the location of the private key for your SSL certificate.
  • ssl_protocols TLSv1.2 TLSv1.3;: This line defines the SSL/TLS protocols that the server should use to establish the secure connection. TLSv1.2 and TLSv1.3 are the latest and most secure versions of TLS (Transport Layer Security), which has replaced SSL (Secure Sockets Layer).
  • ssl_prefer_server_ciphers on;: This directive tells the server to prefer the order of ciphers it has defined (via ssl_ciphers) over the client’s preferred order during SSL/TLS negotiation.
  • ssl_ciphers ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;: This line specifies the cipher suites that the server should use when establishing the secure connection. Cipher suites are algorithms used for encryption key, data encryption, data hashing, etc.
  • ssl_session_cache shared:SSL:10m;: This directive enables caching of SSL sessions to improve SSL performance. SSL sessions are cached for 10 minutes (10m), which means that if the same client reconnects within that time, there is no need to renegotiate a new SSL session.
  • ssl_session_timeout 10m;: This line defines how long an SSL session can remain valid after it is used. In this example, an SSL session is considered valid for 10 minutes after the last use.

These settings can be adapted to the specific security needs of your website or application.

Where to put this configuration?

For Non-Docker setups

The previously discussed Nginx server block configuration should be added to the following file: /etc/nginx/sites-available/default.

Before starting Nginx, it’s crucial to update your firewall settings to permit traffic on the ports that Nginx is configured to use:

The exact process of updating your firewall settings will depend on the type of firewall software your server is using. For example, if you’re using UFW (Uncomplicated Firewall), you might use the following commands to allow traffic on ports 80 (HTTP) and 443 (HTTPS):

sudo ufw enable
sudo ufw allow 'Nginx Full'

Now you can start the Nginx service:

sudo systemctl start nginx

For Docker setups

In this case, the file that will contain the Nginx configurations is: ./conf.d/default.conf.

Then, modify your docker-compose.yml

...
nginx:
image: nginx:1.21.0
container_name: nginx
restart : unless-stopped
ports:
- '80:80'
- '443:443'
volumes:
- "./conf.d:/etc/nginx/conf.d"
- "/etc/letsencrypt/live/example.com/fullchain.pem:/etc/nginx/certs/fullchain.pem:ro"
- "/etc/letsencrypt/live/example.com/privkey.pem:/etc/nginx/certs/privkey.pem:ro"
depends_on:
- ...

Ensuring you select the Nginx image version that suits your needs and is maintained with the latest security patches.

Rebuild and restart your Nginx container:

docker-compose up -d nginx

Step 4: Automatic Certificate Renewal

Let’s Encrypt certificates are valid for 90 days. You should set up automatic renewal to avoid service disruptions.

Configuring Auto-Renewal

To set up automatic certificate renewal, add a cron job that runs the Certbot renewal command:

sudo crontab -e

Then add the following line to the end of the file:

0 0,12 * * * certbot renew --quiet

This command will check for certificate renewal twice a day. If the certificate is close to expiry, Certbot will renew it automatically.

That’s a Wrap, Securely!

Whether you run an Nginx server in a Docker container or on bare metal, securing your server with HTTPS is a must. Thanks to Certbot and Let’s Encrypt, the process is straightforward and free. Always remember to set up automatic renewal for your SSL certificates to avoid downtime. Now, your Nginx server is set up to serve your domain over HTTPS. Enjoy your secure website!

--

--