Tutorial

How to Install a Free SSL Certificate with Lets Encrypt: Nginx and Apache

March 27, 2026

Back to Blog

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.

What You Need Before Starting
  • 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.

You run
Certbot
Certbot contacts
Let's Encrypt
Challenge issued
(HTTP or DNS)
You prove
domain control
Certificate
issued (90 days)

The two most common validation methods are:

Challenge TypeHow It WorksProsCons
HTTP-01Places a file at /.well-known/acme-challenge/Simplest, works with any web serverRequires port 80 open, no wildcards
DNS-01Creates a TXT record at _acme-challenge.domain.comSupports wildcards, no port 80 neededRequires 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):

# Remove old apt version if present
$ 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:

$ sudo certbot --nginx -d example.com -d www.example.com
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:

# Added by Certbot to your server block
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:

# Enable required Apache modules first
$ 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:

# Test with curl
$ 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
A+
Target SSL Labs Grade
90
Days Until Expiry
$0
Certificate Cost
TLS 1.3
Protocol Version

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:

# Check if the timer is active
$ 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)
Set It and Forget It
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:

$ sudo certbot certonly --manual --preferred-challenges dns \
-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.

Wildcard Renewal Challenge
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:
# Install Cloudflare DNS plugin (example)
$ 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:

ProviderPlugin PackageAuto-Renewal
Cloudflarecertbot-dns-cloudflareYes
Route53 (AWS)certbot-dns-route53Yes
Google Cloud DNScertbot-dns-googleYes
DigitalOceancertbot-dns-digitaloceanYes
NamecheapNot officially supportedManual only
GoDaddyNot officially supportedManual 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

# /etc/nginx/snippets/ssl-hardening.conf

# 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

# /etc/apache2/conf-available/ssl-hardening.conf

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

server {
listen 80;
server_name example.com www.example.com;
return 301 https://$host$request_uri;
}

Apache Redirect

<VirtualHost *:80>
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

Error: Connection refused or The server could not connect to the client to verify the domain
# Check if port 80 is open
$ 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

Error: The requested domain name does not resolve to the server IP
# Check DNS resolution
$ 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

Error: Too many certificates already issued for exact domain

Let's Encrypt has rate limits to prevent abuse:

LimitValueWindow
Certificates per registered domain507 days
Duplicate certificates57 days
Failed validations51 hour
Accounts per IP103 hours
Tip: Use --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

# Always test config before reloading
$ 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:

$ sudo ls -la /etc/letsencrypt/live/example.com/
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
FileContainsUsed For
cert.pemYour server certificate onlyRarely used directly
chain.pemIntermediate CA certificateRarely used directly
fullchain.pemcert.pem + chain.pem combinedUse this for ssl_certificate
privkey.pemYour private keyUse this for ssl_certificate_key

Managing Multiple Certificates

# List all 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:

Add Domain
in Panel
Directory
Created
Nginx Vhost
Generated
SSL Certificate
Auto-Issued
Auto-Renewal
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
No Certbot, No Cron, No Worries
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.
Share: