Let’s Encrypt is a free, automated, and open certificate authority run by the Internet Security Research Group (ISRG). Certificates issued by Let’s Encrypt are trusted by virtually all modern browsers and provide full HTTPS at no cost.
This guide shows you how to secure Nginx with Let’s Encrypt on Ubuntu 18.04 using Certbot as the ACME client and the Webroot plugin for domain validation. The setup includes strong Diffie-Hellman parameters, reusable Nginx SSL snippets, and an auto-renewal cron job.
Prerequisites:
example.com)Install Certbot. The certbot package is available in the default Ubuntu repositories:
bashsudo apt updatesudo apt install certbot
Generate a Diffie-Hellman parameter file. DH key exchange strengthens the SSL handshake by making session negotiation resistant to passive interception attacks. Generate a 2048-bit DH file:
bashsudo openssl dhparam -out /etc/ssl/certs/dhparam.pem 2048
You can increase this to 4096 bits for stronger security, but generation can take over 30 minutes on lower-resource servers.
Create the ACME challenge directory for the Webroot plugin:
bashsudo mkdir -p /var/lib/letsencrypt/.well-knownsudo chgrp www-data /var/lib/letsencryptsudo chmod g+s /var/lib/letsencrypt
The Webroot plugin validates your domain by writing a temporary file inside .well-known/acme-challenge/. Let’s Encrypt makes an HTTP request to that file to confirm the domain resolves to your server.
Create two Nginx snippet files so you do not duplicate SSL settings across multiple server blocks.
/etc/nginx/snippets/letsencrypt.conf — maps all ACME challenge requests to the webroot directory:
nginxlocation ^~ /.well-known/acme-challenge/ { allow all; root /var/lib/letsencrypt/; default_type "text/plain"; try_files $uri =404;}
/etc/nginx/snippets/ssl.conf — includes Mozilla-recommended cipher suites, OCSP stapling, HSTS, and security headers (X-Frame-Options, X-Content-Type-Options). Reference your dhparam.pem file at the top.
Include the letsencrypt.conf snippet in your current HTTP server block and restart Nginx. Then run Certbot:
bashsudo certbot certonly --agree-tos --email admin@example.com \ --webroot -w /var/lib/letsencrypt/ -d example.com -d www.example.com
Certbot stores the certificate at /etc/letsencrypt/live/example.com/, with fullchain.pem, privkey.pem, and chain.pem as the key files.
Update your server block with three separate server blocks. The first redirects all HTTP traffic to HTTPS. The second redirects www to the bare domain. The third is the main HTTPS block, which references the Let’s Encrypt certificate files and includes both Nginx snippets.
Reload Nginx to apply the changes:
bashsudo systemctl reload nginx
Auto-renewal. Let’s Encrypt certificates expire every 90 days. Certbot automatically creates a cron job at /etc/cron.d/certbot that runs twice daily. Since Nginx needs a reload after each renewal, open that file and add the --renew-hook directive:
0 */12 * * * root ... certbot -q renew --renew-hook "systemctl reload nginx"
Test the renewal pipeline without actually renewing any certificates:
bashsudo certbot renew --dry-run
No errors means the renewal configuration is working correctly.
Your Nginx server is now secured with a Let’s Encrypt SSL certificate. The certificate renews automatically, and your SSL settings stay consistent across domains through the shared Nginx snippets. Leave a comment below if you run into any issues.