Introduction
A TLS/SSL certificate encrypts the connection between your server and every visitor's browser, protecting login credentials, payment data, and personal information from interception. Without HTTPS, browsers display a "Not Secure" warning that erodes trust and hurts search rankings — Google has used HTTPS as a ranking signal since 2014.
Let's Encrypt is a free, automated certificate authority trusted by every major browser. Combined with Certbot — the recommended client — you can go from plain HTTP to fully encrypted HTTPS in under fifteen minutes. Certbot handles certificate issuance, Nginx configuration, and automatic renewal with zero ongoing cost.
In this tutorial, you will install Certbot and its Nginx plugin, obtain a free TLS certificate for your domain, configure Nginx to redirect all HTTP traffic to HTTPS, verify the certificate and TLS configuration, and set up automatic renewal so the certificate never expires. We tested this process on a Raff Ubuntu 24.04 VM, where the combination of NVMe SSD storage and unmetered bandwidth means TLS handshakes add negligible latency to your response times.
Step 1 — Install Certbot and the Nginx Plugin
Certbot is the official Let's Encrypt client. The Nginx plugin allows Certbot to automatically modify your Nginx server blocks to enable HTTPS without manual configuration file editing.
Install Certbot and the Nginx plugin from Ubuntu's default repository:
bashsudo apt update
sudo apt install -y certbot python3-certbot-nginx
The certbot package provides the core ACME client, and python3-certbot-nginx gives Certbot the ability to read and modify your Nginx configuration automatically.
Verify the installation:
bashcertbot --version
You should see output similar to:
certbot 2.9.0
Step 2 — Verify Your Nginx Server Block and DNS
Before requesting a certificate, Certbot needs two things: an Nginx server block with a server_name directive matching your domain, and a DNS A record pointing that domain to your server's IP address.
Open your Nginx server block configuration. If you followed the Install Nginx tutorial, your site configuration is in /etc/nginx/sites-available/:
bashsudo nano /etc/nginx/sites-available/your_domain
Confirm the server_name directive matches the domain you want to secure:
nginxserver {
listen 80;
listen [::]:80;
server_name your_domain www.your_domain;
root /var/www/your_domain/html;
index index.html index.htm;
location / {
try_files $uri $uri/ =404;
}
}
Replace your_domain with your actual domain name. If you are using the default Nginx configuration at /etc/nginx/sites-available/default, update the server_name line there instead.
Test the Nginx configuration for syntax errors:
bashsudo nginx -t
You should see:
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
If the test passes, reload Nginx to apply any changes:
bashsudo systemctl reload nginx
Now verify your DNS is resolving correctly. From your local machine or any terminal:
bashdig +short your_domain
The output should show your server's public IP address. If it does not, update your DNS A record at your domain registrar and wait for propagation before continuing. DNS changes typically take 5-30 minutes but can take up to 48 hours.
Step 3 — Allow HTTPS Traffic Through the Firewall
If you have UFW enabled on your server, you need to allow HTTPS traffic on port 443. Nginx registers application profiles with UFW during installation, which simplifies this step.
Allow both HTTP and HTTPS traffic:
bashsudo ufw allow 'Nginx Full'
If you previously allowed only Nginx HTTP, you can remove the redundant rule since Nginx Full covers both ports 80 and 443:
bashsudo ufw delete allow 'Nginx HTTP'
Verify the firewall status:
bashsudo ufw status
You should see Nginx Full listed as ALLOW:
Status: active
To Action From
-- ------ ----
OpenSSH ALLOW Anywhere
Nginx Full ALLOW Anywhere
OpenSSH (v6) ALLOW Anywhere (v6)
Nginx Full (v6) ALLOW Anywhere (v6)
Step 4 — Obtain the SSL Certificate
Run Certbot with the Nginx plugin to obtain and install the certificate. Replace your_domain with your actual domain:
bashsudo certbot --nginx -d your_domain -d www.your_domain
If this is your first time running Certbot, it will prompt you to:
- Enter an email address for renewal notifications and urgent security notices
- Agree to the Let's Encrypt Terms of Service
- Optionally subscribe to EFF's mailing list
After answering these prompts, Certbot communicates with the Let's Encrypt servers to verify that you control the domain. It uses the HTTP-01 challenge method — placing a temporary file on your server and asking Let's Encrypt to fetch it.
Warning
The domain verification will fail if your DNS A record does not point to this server or if port 80 is blocked by a firewall. Ensure both are configured correctly before running this command.
On success, you will see output similar to:
Successfully received certificate.
Certificate is saved at: /etc/letsencrypt/live/your_domain/fullchain.pem
Key is saved at: /etc/letsencrypt/live/your_domain/privkey.pem
Deploying certificate
Successfully deployed certificate for your_domain to /etc/nginx/sites-available/your_domain
Successfully deployed certificate for www.your_domain to /etc/nginx/sites-available/your_domain
Congratulations! You have successfully enabled HTTPS on https://your_domain and https://www.your_domain
Certbot automatically modifies your Nginx server block to include SSL directives, adds a redirect from HTTP to HTTPS, and reloads Nginx. You do not need to edit any configuration files manually.
Step 5 — Verify HTTPS and the Certificate
Open your domain in a web browser using https://:
https://your_domain
You should see a padlock icon in the address bar confirming a secure connection. Click the padlock to inspect the certificate details — you should see "Let's Encrypt" listed as the issuer.
Verify from the command line using curl:
bashcurl -I https://your_domain
You should see HTTP/2 200 in the response headers. The HTTP/2 confirms that both HTTPS and HTTP/2 are active.
Test that HTTP-to-HTTPS redirection is working:
bashcurl -I http://your_domain
You should see a 301 Moved Permanently response with a Location header pointing to the HTTPS version:
HTTP/1.1 301 Moved Permanently
Location: https://your_domain/
For a comprehensive check, test your domain with the Qualys SSL Labs tool at https://www.ssllabs.com/ssltest/analyze.html?d=your_domain. A properly configured Certbot installation typically scores an A or A+ rating.
Step 6 — Verify Automatic Renewal
Let's Encrypt certificates expire after 90 days. Certbot installs a systemd timer that checks for expiring certificates twice per day and renews them automatically when they are within 30 days of expiration.
Verify the renewal timer is active:
bashsudo systemctl status certbot.timer
You should see active (waiting) in the output, indicating the timer is scheduled.
Test the renewal process without actually renewing (a dry run):
bashsudo certbot renew --dry-run
If the dry run completes without errors, automatic renewal is working correctly. You should see:
Congratulations, all simulated renewals succeeded:
/etc/letsencrypt/live/your_domain/fullchain.pem (success)
Tip
If the dry run fails, the most common cause is port 80 being blocked. Certbot needs port 80 open for the HTTP-01 challenge during renewal, even though your site serves traffic on HTTPS. Keep the Nginx Full UFW rule active to ensure both ports remain accessible.
After renewal, Certbot automatically reloads Nginx to apply the new certificate. No manual intervention or cron job is needed.
Step 7 — Harden Your TLS Configuration
Certbot's default TLS settings are good, but you can strengthen them for production by adding security headers. Open your Nginx server block:
bashsudo nano /etc/nginx/sites-available/your_domain
Inside the server block that listens on port 443 (the one Certbot modified), add these security headers:
nginx # Security headers
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
The Strict-Transport-Security header (HSTS) tells browsers to always use HTTPS for your domain for the next year. This prevents protocol downgrade attacks and cookie hijacking. The other headers protect against content-type sniffing, clickjacking, and referrer leakage.
Warning
Only enable HSTS after you have confirmed HTTPS is working correctly. HSTS is difficult to undo — browsers will refuse to connect over HTTP for the duration of the max-age value. Start with a shorter value like max-age=86400 (1 day) for testing.
Test the configuration and reload Nginx:
bashsudo nginx -t
sudo systemctl reload nginx
Verify the headers are present:
bashcurl -I https://your_domain
You should see strict-transport-security, x-content-type-options, x-frame-options, and referrer-policy in the response headers.
Conclusion
You have secured your Nginx web server with a free Let's Encrypt TLS certificate on your Raff Ubuntu 24.04 VM. Your site now encrypts all traffic with HTTPS, redirects HTTP visitors to HTTPS automatically, renews the certificate without manual intervention, and includes hardened security headers for production use.
From here, you can:
- Set up WordPress behind your newly secured Nginx server
- Build a LEMP stack for dynamic PHP applications with HTTPS from day one
- Configure Nginx as a reverse proxy for Docker-based applications like Uptime Kuma or n8n
Raff VMs include unmetered bandwidth on every tier, which means HTTPS traffic — including the TLS handshake overhead — never triggers overage charges. Combined with NVMe SSD storage for fast static file delivery, your encrypted site performs just as well as plain HTTP.
This tutorial was tested and verified by our security engineering team on a Raff CPU-Optimized Tier 2 VM.
