Every website and application served over the internet should use HTTPS. Let's Encrypt provides free, automated TLS certificates, and Certbot is the official client that handles obtaining, installing, and renewing those certificates on your Ubuntu VPS. There are no excuses left for serving traffic over plain HTTP.
This guide covers the complete Let's Encrypt workflow on Ubuntu 24.04: installing Certbot, obtaining certificates for both Nginx and Apache, configuring automatic renewal, generating wildcard certificates with DNS validation, securing multiple domains on a single server, testing your SSL configuration, and troubleshooting common issues. By the end, your sites will score an A+ on SSL Labs.
Prerequisites
Before starting, you need:
- An Ubuntu 24.04 VPS. A Cloud VPS with 1 vCPU / 1 GB RAM is sufficient for serving HTTPS sites with Certbot — certificate operations are lightweight.
- Root or sudo access. If you haven't configured your server yet, follow our Ubuntu VPS setup guide and security hardening guide first.
- A domain name with DNS A records pointing to your server's public IP address.
- A running web server — either Nginx or Apache. See our LEMP stack guide or Nginx reverse proxy guide for installation instructions.
- Port 80 open in your firewall. Certbot's HTTP-01 challenge requires incoming connections on port 80.
MassiveGRID Ubuntu VPS — Ubuntu 24.04 LTS pre-installed, Proxmox HA cluster with automatic failover, Ceph 3x replicated NVMe storage, independent CPU/RAM/storage scaling, 12 Tbps DDoS protection, 4 global datacenter locations, 100% uptime SLA, and 24/7 human support rated 9.5/10. Deploy a Cloud VPS from $1.99/mo.
How Let's Encrypt Works
Let's Encrypt uses the ACME (Automatic Certificate Management Environment) protocol to verify that you control the domain you're requesting a certificate for. The two primary validation methods are:
- HTTP-01 challenge — Certbot places a temporary file on your web server, and Let's Encrypt's servers fetch it via HTTP on port 80. This proves you control the domain's web server. This is the default and easiest method.
- DNS-01 challenge — Certbot creates a specific DNS TXT record for your domain. Let's Encrypt verifies the record exists. This is required for wildcard certificates and works even if port 80 is not accessible.
Let's Encrypt certificates are valid for 90 days. Certbot sets up automatic renewal so you never have to think about expiration after the initial setup.
Installing Certbot on Ubuntu 24.04
The recommended installation method is via snap, which ensures you always get the latest version of Certbot with automatic updates.
First, make sure snapd is installed and up to date:
sudo snap install core
sudo snap refresh core
Remove any older OS-packaged version of Certbot to avoid conflicts:
sudo apt remove certbot -y
Install Certbot via snap:
sudo snap install --classic certbot
Create a symbolic link so you can run certbot from any directory:
sudo ln -s /snap/bin/certbot /usr/bin/certbot
Verify the installation:
certbot --version
# certbot 3.1.0
Obtaining a Certificate for Nginx
If you're running Nginx, Certbot's Nginx plugin handles everything automatically — it obtains the certificate, modifies your Nginx configuration to use it, and sets up HTTPS redirection.
Make sure your Nginx server block has the correct server_name directive. For example, in /etc/nginx/sites-available/yourdomain.com:
server {
listen 80;
listen [::]:80;
server_name yourdomain.com www.yourdomain.com;
root /var/www/yourdomain.com/html;
index index.html;
location / {
try_files $uri $uri/ =404;
}
}
Enable the site and test the configuration:
sudo ln -s /etc/nginx/sites-available/yourdomain.com /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
Now run Certbot with the Nginx plugin:
sudo certbot --nginx -d yourdomain.com -d www.yourdomain.com
Certbot will:
- Verify domain ownership via the HTTP-01 challenge
- Obtain the certificate from Let's Encrypt
- Modify your Nginx server block to include SSL configuration
- Ask if you want to redirect HTTP to HTTPS (select option 2 — redirect)
- Reload Nginx with the new configuration
After Certbot finishes, your Nginx configuration will be updated automatically. You can inspect the changes:
cat /etc/nginx/sites-available/yourdomain.com
Certbot adds a new server block listening on port 443 with the certificate paths and SSL parameters, plus a redirect block that sends all HTTP traffic to HTTPS.
Certificate File Locations
Certbot stores certificates in /etc/letsencrypt/live/yourdomain.com/:
| File | Description |
|---|---|
fullchain.pem |
Your certificate + intermediate certificates (what Nginx uses for ssl_certificate) |
privkey.pem |
Your private key (what Nginx uses for ssl_certificate_key) |
cert.pem |
Your certificate only |
chain.pem |
Intermediate certificates only |
These are symbolic links managed by Certbot. They always point to the latest certificate, even after renewal.
Obtaining a Certificate for Apache
If you're using Apache instead of Nginx, install the Apache plugin for Certbot:
sudo snap install --classic certbot
sudo apt install -y python3-certbot-apache
Ensure your Apache virtual host has the correct ServerName. In /etc/apache2/sites-available/yourdomain.com.conf:
<VirtualHost *:80>
ServerName yourdomain.com
ServerAlias www.yourdomain.com
DocumentRoot /var/www/yourdomain.com/html
<Directory /var/www/yourdomain.com/html>
AllowOverride All
Require all granted
</Directory>
ErrorLog ${APACHE_LOG_DIR}/yourdomain-error.log
CustomLog ${APACHE_LOG_DIR}/yourdomain-access.log combined
</VirtualHost>
Enable the site and required modules:
sudo a2ensite yourdomain.com.conf
sudo a2enmod ssl
sudo a2enmod rewrite
sudo systemctl reload apache2
Run Certbot with the Apache plugin:
sudo certbot --apache -d yourdomain.com -d www.yourdomain.com
Certbot creates a new SSL virtual host configuration and enables HTTPS redirection, then reloads Apache automatically.
Standalone Mode (No Web Server)
If you don't have a web server running — for example, you're using Certbot to obtain a certificate for a mail server, database, or other non-web service — use standalone mode. Certbot temporarily starts its own web server on port 80:
sudo certbot certonly --standalone -d mail.yourdomain.com
Important: Port 80 must be free when using standalone mode. If Nginx or Apache is running, stop it first or use the appropriate plugin instead.
You can also use port 443 for standalone mode with the TLS-ALPN-01 challenge:
sudo certbot certonly --standalone --preferred-challenges tls-alpn-01 -d mail.yourdomain.com
Webroot Mode
Webroot mode is useful when you don't want Certbot to modify your web server configuration at all. Certbot places the challenge file in your web server's document root, and you manage the SSL configuration yourself:
sudo certbot certonly --webroot -w /var/www/yourdomain.com/html -d yourdomain.com -d www.yourdomain.com
This only obtains the certificate. You must configure your web server to use it manually. In Nginx:
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name yourdomain.com www.yourdomain.com;
ssl_certificate /etc/letsencrypt/live/yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/yourdomain.com/privkey.pem;
# SSL settings
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers off;
root /var/www/yourdomain.com/html;
index index.html;
}
Automatic Renewal
Certbot sets up automatic renewal during installation. On Ubuntu 24.04 with the snap package, renewal is handled by a systemd timer.
Check the timer status:
sudo systemctl status snap.certbot.renew.timer
You should see output indicating the timer is active and scheduled:
● snap.certbot.renew.timer - Timer renew for snap application certbot.renew
Loaded: loaded (/etc/systemd/system/snap.certbot.renew.timer; enabled)
Active: active (waiting)
Trigger: Wed 2026-02-28 05:22:00 UTC; 11h left
The timer runs twice daily and only attempts renewal when a certificate is within 30 days of expiration. Since certificates are valid for 90 days, this gives you plenty of buffer.
Test that renewal works without actually renewing:
sudo certbot renew --dry-run
If the dry run succeeds, your certificates will renew automatically. If it fails, the error output will tell you exactly what to fix.
Renewal Hooks
After a certificate is renewed, your web server needs to reload its configuration to pick up the new certificate. Certbot handles this automatically for Nginx and Apache plugins, but if you used certonly mode, you need to set up a renewal hook.
Create a deploy hook that reloads Nginx after renewal:
sudo mkdir -p /etc/letsencrypt/renewal-hooks/deploy
sudo nano /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh
Add the following:
#!/bin/bash
systemctl reload nginx
Make it executable:
sudo chmod +x /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh
You can also specify hooks directly in the renewal configuration file at /etc/letsencrypt/renewal/yourdomain.com.conf:
[renewalparams]
# ... existing settings ...
[[ post-hook ]]
post_hook = systemctl reload nginx
Wildcard Certificates with DNS Validation
A wildcard certificate covers all subdomains of a domain — *.yourdomain.com matches app.yourdomain.com, api.yourdomain.com, staging.yourdomain.com, and any other subdomain. Wildcard certificates require DNS-01 validation.
Manual DNS Validation
Request a wildcard certificate:
sudo certbot certonly --manual --preferred-challenges dns \
-d "yourdomain.com" \
-d "*.yourdomain.com"
Certbot will ask you to create a DNS TXT record. You'll see output like:
Please deploy a DNS TXT record under the name:
_acme-challenge.yourdomain.com
with the following value:
gfj9Xq...Rg5nTzs
Before continuing, verify the TXT record has been deployed.
Press Enter to continue...
Log in to your DNS provider and create the TXT record. Then verify it has propagated before pressing Enter:
dig -t TXT _acme-challenge.yourdomain.com +short
Wait until the correct value appears, then press Enter in the Certbot prompt. Note that if you're obtaining both yourdomain.com and *.yourdomain.com, Certbot will ask you to create two TXT records (both under _acme-challenge.yourdomain.com).
Important: Manual DNS validation does not support automatic renewal. Every 90 days, you'll need to manually update the TXT record. For automated wildcard renewal, use a DNS plugin.
Automated DNS Validation with Cloudflare
If your DNS is managed by Cloudflare, you can automate DNS validation using the Certbot Cloudflare plugin:
sudo snap set certbot trust-plugin-with-root=ok
sudo snap install certbot-dns-cloudflare
Create a Cloudflare API credentials file:
sudo mkdir -p /etc/letsencrypt/secrets
sudo nano /etc/letsencrypt/secrets/cloudflare.ini
Add your Cloudflare API token (create a token with Zone:DNS:Edit permission in the Cloudflare dashboard):
# Cloudflare API token
dns_cloudflare_api_token = your-cloudflare-api-token-here
Secure the credentials file:
sudo chmod 600 /etc/letsencrypt/secrets/cloudflare.ini
Now request the wildcard certificate with automatic DNS validation:
sudo certbot certonly \
--dns-cloudflare \
--dns-cloudflare-credentials /etc/letsencrypt/secrets/cloudflare.ini \
-d "yourdomain.com" \
-d "*.yourdomain.com"
This method fully supports automatic renewal. Certbot will create and clean up the DNS TXT record automatically during each renewal cycle.
Other DNS Plugins
Certbot has DNS plugins for many providers:
| Provider | Plugin Package |
|---|---|
| Cloudflare | certbot-dns-cloudflare |
| DigitalOcean | certbot-dns-digitalocean |
| Route 53 (AWS) | certbot-dns-route53 |
| Google Cloud DNS | certbot-dns-google |
| Linode | certbot-dns-linode |
| OVH | certbot-dns-ovh |
Install them via snap with the same pattern: sudo snap install certbot-dns-PROVIDER.
Multi-Domain Certificates (SAN)
A single certificate can cover multiple different domains using Subject Alternative Names (SAN). This is useful when you host several sites on one server and want to manage fewer certificates:
sudo certbot --nginx \
-d yourdomain.com \
-d www.yourdomain.com \
-d app.yourdomain.com \
-d anotherdomain.com \
-d www.anotherdomain.com
All listed domains will be included in a single certificate. Each domain must pass the validation challenge — make sure all DNS records point to your server and all domains are configured in Nginx.
To add a domain to an existing certificate, use --expand:
sudo certbot --nginx --expand \
-d yourdomain.com \
-d www.yourdomain.com \
-d app.yourdomain.com \
-d newdomain.com
You must include all existing domains plus the new one. Certbot issues a replacement certificate covering all listed domains.
Hardening Your SSL Configuration
The default Certbot configuration is good but can be improved for maximum security. After Certbot has configured your Nginx server block, add these settings inside the server block listening on port 443:
Protocols and Ciphers
# Only allow TLS 1.2 and 1.3 (disable older protocols)
ssl_protocols TLSv1.2 TLSv1.3;
# Use strong cipher suites
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305;
# Let the server choose the cipher order
ssl_prefer_server_ciphers off;
OCSP Stapling
OCSP stapling improves SSL performance by allowing the server to send the certificate's revocation status directly, instead of requiring the client to check with the certificate authority:
# Enable OCSP stapling
ssl_stapling on;
ssl_stapling_verify on;
# Use Google and Cloudflare DNS for OCSP resolution
resolver 8.8.8.8 1.1.1.1 valid=300s;
resolver_timeout 5s;
SSL Session Caching
SSL session caching reduces the overhead of repeated TLS handshakes from the same clients:
# Session cache shared between all worker processes
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 1d;
# Disable session tickets (for forward secrecy)
ssl_session_tickets off;
Security Headers
Add HTTP security headers to your HTTPS server block:
# Enforce HTTPS for 2 years (with subdomains and preload)
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
# Prevent clickjacking
add_header X-Frame-Options "SAMEORIGIN" always;
# Prevent MIME type sniffing
add_header X-Content-Type-Options "nosniff" always;
# Control referrer information
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
# Basic permissions policy
add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;
Diffie-Hellman Parameters
Generate a strong Diffie-Hellman parameter file for additional security:
sudo openssl dhparam -out /etc/letsencrypt/ssl-dhparams-4096.pem 4096
This command takes several minutes. Then reference it in your Nginx configuration:
ssl_dhparam /etc/letsencrypt/ssl-dhparams-4096.pem;
Reload Nginx after making changes:
sudo nginx -t && sudo systemctl reload nginx
Complete Hardened Nginx SSL Configuration
Here is a complete Nginx server block with all the hardening options applied:
# HTTP — redirect all traffic to HTTPS
server {
listen 80;
listen [::]:80;
server_name yourdomain.com www.yourdomain.com;
return 301 https://$server_name$request_uri;
}
# HTTPS
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name yourdomain.com www.yourdomain.com;
# Let's Encrypt certificate
ssl_certificate /etc/letsencrypt/live/yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/yourdomain.com/privkey.pem;
# Protocols and ciphers
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305;
ssl_prefer_server_ciphers off;
# DH parameters
ssl_dhparam /etc/letsencrypt/ssl-dhparams-4096.pem;
# OCSP stapling
ssl_stapling on;
ssl_stapling_verify on;
resolver 8.8.8.8 1.1.1.1 valid=300s;
resolver_timeout 5s;
# Session caching
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 1d;
ssl_session_tickets off;
# Security headers
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;
# Document root
root /var/www/yourdomain.com/html;
index index.html;
location / {
try_files $uri $uri/ =404;
}
# Block dotfiles
location ~ /\. {
deny all;
access_log off;
log_not_found off;
}
}
Testing Your SSL Configuration
SSL Labs Test
The most authoritative SSL test is Qualys SSL Labs. Open your browser and go to:
https://www.ssllabs.com/ssltest/analyze.html?d=yourdomain.com
With the hardened configuration above, you should score an A+ rating. The test checks protocol support, key exchange strength, cipher suite quality, and certificate chain validity.
Command-Line Testing
Test your SSL configuration locally with openssl:
# Check certificate details
openssl s_client -connect yourdomain.com:443 -servername yourdomain.com < /dev/null 2>/dev/null | openssl x509 -text -noout
Check the expiration date:
echo | openssl s_client -connect yourdomain.com:443 -servername yourdomain.com 2>/dev/null | openssl x509 -noout -dates
Verify the certificate chain:
openssl s_client -connect yourdomain.com:443 -servername yourdomain.com -showcerts < /dev/null
Test specific TLS versions:
# This should succeed (TLS 1.3)
openssl s_client -connect yourdomain.com:443 -tls1_3
# This should fail (TLS 1.1 disabled)
openssl s_client -connect yourdomain.com:443 -tls1_1
Using Certbot to Check Certificate Status
List all certificates managed by Certbot:
sudo certbot certificates
This shows each certificate's domains, expiry date, certificate path, and private key path. Use this to quickly verify which certificates are installed and when they expire.
Managing Certificates
Revoking a Certificate
If a certificate's private key is compromised or you no longer use a domain, revoke the certificate:
sudo certbot revoke --cert-path /etc/letsencrypt/live/yourdomain.com/fullchain.pem
Certbot will ask if you also want to delete the certificate files. It's usually safe to say yes.
Deleting Certificate Files
To remove a certificate without revoking it (e.g., you've already moved the domain to a different server):
sudo certbot delete --cert-name yourdomain.com
Renewing a Specific Certificate
Force renewal of a specific certificate (useful for testing):
sudo certbot renew --cert-name yourdomain.com --force-renewal
Caution: Don't use --force-renewal frequently. Let's Encrypt has rate limits (50 certificates per registered domain per week). The --dry-run flag doesn't count against rate limits.
Troubleshooting Common Issues
Port 80 Blocked by Firewall
The HTTP-01 challenge requires port 80 to be open. If Certbot fails with a connection error, check your firewall:
# Check UFW status
sudo ufw status
# Allow HTTP traffic
sudo ufw allow 80/tcp
# Also allow HTTPS
sudo ufw allow 443/tcp
If you're using iptables directly:
sudo iptables -A INPUT -p tcp --dport 80 -j ACCEPT
sudo iptables -A INPUT -p tcp --dport 443 -j ACCEPT
Also check if your cloud provider has a separate firewall or security group that blocks port 80.
DNS Record Not Pointing to Server
The HTTP-01 challenge requires the domain's A record to resolve to your server's IP. Verify:
dig +short A yourdomain.com
# Should return your server's IP address
If the DNS record is correct but recently changed, wait for propagation (up to 48 hours, though typically 5-30 minutes).
Rate Limits
Let's Encrypt enforces rate limits to prevent abuse. The main limits are:
| Limit | Value | Period |
|---|---|---|
| Certificates per registered domain | 50 | Per week |
| Duplicate certificates | 5 | Per week |
| Failed validations | 5 | Per hour per account per hostname |
| Accounts per IP | 10 | Per 3 hours |
If you hit a rate limit, you'll need to wait until the window expires. Use the staging environment for testing to avoid hitting production limits:
sudo certbot --nginx --staging -d yourdomain.com
Staging certificates are not trusted by browsers, but the issuance process is identical. Once your setup works with staging, switch to production by removing the --staging flag and running again with --force-renewal.
Nginx Configuration Errors
If Certbot fails while modifying Nginx configuration, check for syntax errors:
sudo nginx -t
Common issues include duplicate server_name directives or conflicting listen directives across server blocks. Fix any errors, then re-run Certbot.
Certbot Cannot Find Your Server Block
Certbot looks for server blocks in /etc/nginx/sites-enabled/ with a server_name matching the domain you specified. If Certbot says "Unable to find a matching server block," verify:
- The server block file is symlinked to
sites-enabled/ - The
server_namedirective matches exactly (no typos) - Nginx has been reloaded after the configuration change
Permission Denied
Certbot must run as root or with sudo. The /etc/letsencrypt/ directory is owned by root with restricted permissions. Never change ownership of this directory.
Certificate Renewal Failing
If automatic renewal stops working, check the renewal configuration:
cat /etc/letsencrypt/renewal/yourdomain.com.conf
Common causes of renewal failure:
- Web server stopped or misconfigured
- Port 80 blocked (new firewall rule added since initial setup)
- DNS records changed
- Certbot version outdated (update with
sudo snap refresh certbot)
Test renewal manually:
sudo certbot renew --dry-run
Automating SSL Monitoring
Even with automatic renewal, it's wise to monitor your certificates. Create a simple script that checks expiration and alerts you if a certificate is expiring within 14 days:
sudo nano /usr/local/bin/check-ssl.sh
#!/bin/bash
DOMAIN="yourdomain.com"
DAYS_WARNING=14
EMAIL="admin@yourdomain.com"
EXPIRY=$(echo | openssl s_client -connect "$DOMAIN:443" -servername "$DOMAIN" 2>/dev/null \
| openssl x509 -noout -enddate 2>/dev/null \
| cut -d= -f2)
if [ -z "$EXPIRY" ]; then
echo "ERROR: Could not retrieve certificate for $DOMAIN" | mail -s "SSL Check Failed: $DOMAIN" "$EMAIL"
exit 1
fi
EXPIRY_EPOCH=$(date -d "$EXPIRY" +%s)
NOW_EPOCH=$(date +%s)
DAYS_LEFT=$(( (EXPIRY_EPOCH - NOW_EPOCH) / 86400 ))
if [ "$DAYS_LEFT" -lt "$DAYS_WARNING" ]; then
echo "WARNING: SSL certificate for $DOMAIN expires in $DAYS_LEFT days (on $EXPIRY)" \
| mail -s "SSL Expiring Soon: $DOMAIN" "$EMAIL"
fi
sudo chmod +x /usr/local/bin/check-ssl.sh
Schedule this as a daily cron job (see our cron jobs and task scheduling guide for more details):
sudo crontab -e
# Add:
0 9 * * * /usr/local/bin/check-ssl.sh
For comprehensive monitoring, including uptime, resource usage, and alerting, refer to our Ubuntu VPS monitoring guide.
SSL for Reverse Proxy Setups
If you're running applications behind Nginx as a reverse proxy (Node.js, Python, Docker containers, etc.), SSL termination happens at Nginx. The backend application communicates with Nginx over plain HTTP on localhost.
Here's a typical reverse proxy setup with SSL. For a deeper guide, see our Nginx reverse proxy tutorial:
server {
listen 80;
listen [::]:80;
server_name app.yourdomain.com;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name app.yourdomain.com;
ssl_certificate /etc/letsencrypt/live/app.yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/app.yourdomain.com/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
Running multiple applications on different subdomains? Each subdomain gets its own Certbot certificate or you can use a wildcard certificate to cover them all. If you're deploying Docker containers with automatic SSL, consider using Traefik as described in our Traefik Docker guide.
Prefer Managed SSL?
If managing SSL certificates, renewal hooks, web server configuration, and security headers feels like too much operational overhead, consider MassiveGRID's Managed Dedicated Cloud Servers. The managed service handles SSL certificate provisioning, automatic renewal, web server hardening, security patches, and 24/7 monitoring — so you can focus on your application instead of infrastructure. Every managed server runs on a Proxmox HA cluster with automatic failover and Ceph triple-replicated NVMe storage for enterprise-grade reliability.
What's Next
- Ubuntu VPS initial setup guide — server configuration fundamentals
- Security hardening guide — firewall, SSH hardening, fail2ban
- Nginx reverse proxy guide — advanced proxy configuration, load balancing, caching
- Cron jobs and task scheduling — automate certificate monitoring and other tasks
- VPS monitoring setup — system metrics, alerting, and dashboards
- Traefik with Docker — automatic SSL for containerized applications