In 2026, there is no excuse for running a website without HTTPS. Search engines penalize non-secure sites, browsers display alarming warnings to visitors, and even simple static sites need encryption to prevent content injection by ISPs and intermediaries. Thanks to Let's Encrypt, SSL certificates are completely free and can be set up in under five minutes.
This guide covers everything: installing Certbot on Ubuntu, getting certificates for both Nginx and Apache, setting up automatic renewal, obtaining wildcard certificates, and troubleshooting the most common issues.
- A domain name pointing to your server's IP address (A record configured)
- Port 80 open in your firewall (required for HTTP validation)
- Nginx or Apache installed and running
- SSH access with sudo privileges
How Let's Encrypt Works
Let's Encrypt is a Certificate Authority (CA) that issues SSL/TLS certificates at zero cost. It uses the ACME (Automatic Certificate Management Environment) protocol to verify that you control a domain before issuing a certificate.
Certbot
Let's Encrypt
(HTTP or DNS)
domain control
issued (90 days)
The two most common validation methods are:
| Challenge Type | How It Works | Pros | Cons |
|---|---|---|---|
| HTTP-01 | Places a file at /.well-known/acme-challenge/ | Simplest, works with any web server | Requires port 80 open, no wildcards |
| DNS-01 | Creates a TXT record at _acme-challenge.domain.com | Supports wildcards, no port 80 needed | Requires DNS API access or manual updates |
Step 1: Install Certbot
Certbot is the recommended ACME client for Let's Encrypt. On Ubuntu 22.04 and 24.04, the best way to install it is via snap (the apt package is outdated):
$ sudo apt remove certbot -y
# Install via snap (always up-to-date)
$ sudo snap install --classic certbot
certbot 2.12.0 from Certbot Project (certbot-eff) installed
# Create symlink for convenience
$ sudo ln -s /snap/bin/certbot /usr/bin/certbot
# Verify installation
$ certbot --version
certbot 2.12.0
Step 2a: Get a Certificate for Nginx
Certbot has a dedicated Nginx plugin that automatically configures SSL for you. It reads your existing Nginx server blocks, obtains the certificate, and modifies the configuration to use HTTPS:
Saving debug log to /var/log/letsencrypt/letsencrypt.log
Enter email address (used for renewal notices): [email protected]
- - - - - - - - - - - - - - - - - - - - - - - - - -
Please read the Terms of Service. Do you agree?
- - - - - - - - - - - - - - - - - - - - - - - - - -
(Y)es/(N)o: Y
Requesting a certificate for example.com and www.example.com
Successfully received certificate.
Certificate is saved at: /etc/letsencrypt/live/example.com/fullchain.pem
Key is saved at: /etc/letsencrypt/live/example.com/privkey.pem
Deploying certificate
Successfully deployed certificate for example.com
Successfully deployed certificate for www.example.com
Congratulations! HTTPS is now enabled for your domain.
Certbot automatically adds the following to your Nginx configuration:
listen 443 ssl;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
Step 2b: Get a Certificate for Apache
For Apache, the process is nearly identical — just use the --apache flag:
$ sudo a2enmod ssl rewrite
$ sudo systemctl restart apache2
$ sudo certbot --apache -d example.com -d www.example.com
Saving debug log to /var/log/letsencrypt/letsencrypt.log
Requesting a certificate for example.com and www.example.com
Successfully received certificate.
Certificate is saved at: /etc/letsencrypt/live/example.com/fullchain.pem
Key is saved at: /etc/letsencrypt/live/example.com/privkey.pem
Deploying certificate
Successfully deployed certificate for example.com
Successfully deployed certificate for www.example.com
Step 3: Verify SSL Is Working
After Certbot finishes, verify your certificate is properly installed:
$ curl -I https://example.com
HTTP/2 200
server: nginx/1.27.0
strict-transport-security: max-age=31536000
# Check certificate details
$ echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null | openssl x509 -noout -dates -subject
notBefore=Mar 17 00:00:00 2026 GMT
notAfter=Jun 15 23:59:59 2026 GMT
subject=CN = example.com
# Test SSL configuration grade
$ # Visit: https://www.ssllabs.com/ssltest/analyze.html?d=example.com
Step 4: Set Up Automatic Renewal
Let's Encrypt certificates expire after 90 days. Certbot installs a systemd timer (or cron job) that automatically renews certificates when they are within 30 days of expiration:
$ sudo systemctl status snap.certbot.renew.timer
snap.certbot.renew.timer - Timer renew for snap.certbot.renew
Loaded: loaded
Active: active (waiting)
Trigger: Mon 2026-03-18 03:42:00 UTC
# Test renewal (dry run - does not actually renew)
$ sudo certbot renew --dry-run
Saving debug log to /var/log/letsencrypt/letsencrypt.log
Processing /etc/letsencrypt/renewal/example.com.conf
Account registered.
Simulating renewal of an existing certificate for example.com
Congratulations, all simulated renewals succeeded:
/etc/letsencrypt/live/example.com/fullchain.pem (success)
Certbot runs twice daily and only renews certificates that are within 30 days of expiration. You never need to manually renew again. If renewal fails, Certbot sends you an email warning at the address you provided during setup.
Step 5: Wildcard Certificates (DNS Challenge)
A wildcard certificate covers all subdomains of a domain (e.g., *.example.com). This requires DNS validation because Let's Encrypt needs to verify you control the entire domain, not just a single server:
-d example.com -d "*.example.com"
Saving debug log to /var/log/letsencrypt/letsencrypt.log
Requesting a certificate for example.com and *.example.com
- - - - - - - - - - - - - - - - - - - - - - - - - -
Please deploy a DNS TXT record under the name:
_acme-challenge.example.com
with the following value:
Xb7dG3kJ9mN2pL5qR8tV1wY4zA6cE0fH3jK7nQ9s
Before continuing, verify the TXT record has been deployed.
Press Enter to Continue...
At this point, you need to add the TXT record in your DNS provider's control panel. Once the record propagates (usually 1-5 minutes), press Enter and Certbot will verify and issue the wildcard certificate.
Manual DNS validation does not auto-renew because it requires human interaction each time. For automated wildcard renewal, use a DNS plugin for your provider:
$ sudo snap install certbot-dns-cloudflare
# Create API credentials file
$ sudo mkdir -p /etc/letsencrypt
$ sudo cat > /etc/letsencrypt/cloudflare.ini << EOF
dns_cloudflare_api_token = YOUR_CLOUDFLARE_API_TOKEN
EOF
$ sudo chmod 600 /etc/letsencrypt/cloudflare.ini
# Get wildcard cert with auto-renewal support
$ sudo certbot certonly --dns-cloudflare \
--dns-cloudflare-credentials /etc/letsencrypt/cloudflare.ini \
-d example.com -d "*.example.com"
Successfully received certificate.
Available DNS plugins for popular providers:
| Provider | Plugin Package | Auto-Renewal |
|---|---|---|
| Cloudflare | certbot-dns-cloudflare | Yes |
| Route53 (AWS) | certbot-dns-route53 | Yes |
| Google Cloud DNS | certbot-dns-google | Yes |
| DigitalOcean | certbot-dns-digitalocean | Yes |
| Namecheap | Not officially supported | Manual only |
| GoDaddy | Not officially supported | Manual only |
SSL Best Practices: Hardening Your Configuration
The default Certbot configuration is good, but you can achieve an A+ rating on SSL Labs with some additional hardening. Here are the recommended settings:
For Nginx
# Only allow TLS 1.2 and 1.3
ssl_protocols TLSv1.2 TLSv1.3;
# Use strong cipher suites only
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;
# HSTS (force HTTPS for 1 year)
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
# OCSP stapling for faster handshakes
ssl_stapling on;
ssl_stapling_verify on;
resolver 1.1.1.1 8.8.8.8 valid=300s;
# Session caching
ssl_session_timeout 1d;
ssl_session_cache shared:SSL:10m;
ssl_session_tickets off;
For Apache
SSLProtocol -all +TLSv1.2 +TLSv1.3
SSLCipherSuite ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384
SSLHonorCipherOrder off
SSLSessionTickets off
SSLUseStapling On
SSLStaplingCache "shmcb:logs/ssl_stapling(32768)"
Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
HTTP to HTTPS Redirect
After enabling SSL, you want to redirect all HTTP traffic to HTTPS. Certbot usually does this automatically, but here is how to configure it manually:
Nginx Redirect
listen 80;
server_name example.com www.example.com;
return 301 https://$host$request_uri;
}
Apache Redirect
ServerName example.com
Redirect permanent / https://example.com/
</VirtualHost>
Troubleshooting Common Issues
SSL certificate issuance can fail for several reasons. Here are the most common problems and their solutions:
Problem 1: Port 80 Is Blocked
Connection refused or The server could not connect to the client to verify the domain
$ sudo ss -tlnp | grep :80
LISTEN 0 511 0.0.0.0:80 nginx
# Check firewall
$ sudo ufw status | grep 80
80/tcp ALLOW Anywhere
# If port 80 is blocked by your hosting provider or ISP:
# Use DNS challenge instead (see wildcard section above)
Problem 2: DNS Not Pointing to Your Server
The requested domain name does not resolve to the server IP
$ dig +short example.com
203.0.113.50
# Compare with your server IP
$ curl -s ifconfig.me
203.0.113.50
# If using Cloudflare, temporarily disable proxy (orange cloud -> grey)
# Certbot needs to reach your server directly
Problem 3: Rate Limits
Too many certificates already issued for exact domain
Let's Encrypt has rate limits to prevent abuse:
| Limit | Value | Window |
|---|---|---|
| Certificates per registered domain | 50 | 7 days |
| Duplicate certificates | 5 | 7 days |
| Failed validations | 5 | 1 hour |
| Accounts per IP | 10 | 3 hours |
--staging flag during testing. The staging server has much higher rate limits and issues test certificates (not trusted by browsers, but useful for debugging).
certbot --staging --nginx -d example.com
Problem 4: Nginx Configuration Errors
$ sudo nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
# If there are errors, check your server block
$ sudo nginx -t 2>&1 | head -5
# Fix the reported line number in the config file
Certificate File Locations
Certbot stores certificates and keys in a well-organized directory structure:
lrwxrwxrwx 1 root root 39 Mar 17 cert.pem -> ../../archive/example.com/cert1.pem
lrwxrwxrwx 1 root root 40 Mar 17 chain.pem -> ../../archive/example.com/chain1.pem
lrwxrwxrwx 1 root root 44 Mar 17 fullchain.pem -> ../../archive/example.com/fullchain1.pem
lrwxrwxrwx 1 root root 42 Mar 17 privkey.pem -> ../../archive/example.com/privkey1.pem
| File | Contains | Used For |
|---|---|---|
cert.pem | Your server certificate only | Rarely used directly |
chain.pem | Intermediate CA certificate | Rarely used directly |
fullchain.pem | cert.pem + chain.pem combined | Use this for ssl_certificate |
privkey.pem | Your private key | Use this for ssl_certificate_key |
Managing Multiple Certificates
$ sudo certbot certificates
Found the following certs:
Certificate Name: example.com
Domains: example.com www.example.com
Expiry Date: 2026-06-15 (VALID: 89 days)
Certificate Path: /etc/letsencrypt/live/example.com/fullchain.pem
Certificate Name: api.example.com
Domains: api.example.com
Expiry Date: 2026-06-10 (VALID: 84 days)
# Add a new domain to an existing certificate
$ sudo certbot --nginx --expand -d example.com -d www.example.com -d blog.example.com
# Revoke and delete a certificate
$ sudo certbot revoke --cert-name old-domain.com
$ sudo certbot delete --cert-name old-domain.com
Automatic SSL with Panelica
If managing Certbot, cron jobs, renewal hooks, and web server configuration sounds like too much work, Panelica automates the entire SSL lifecycle. When you add a domain in Panelica, the SSL certificate process happens automatically as part of the 9-step domain provisioning:
in Panel
Created
Generated
Auto-Issued
Configured
- Certificates are issued automatically for every domain and subdomain
- Renewal happens silently in the background — no cron jobs to manage
- Both Nginx and Apache configurations are updated automatically
- Cloudflare integration handles DNS validation for wildcard certificates
- Failed renewals trigger alerts in the panel dashboard
- SSL status is visible for all domains in a single overview
Panelica handles SSL certificate issuance, web server configuration, HTTPS redirection, and automatic renewal for every domain — all through a clean web interface. You never need to touch the command line for SSL management.