Setting Up Free SSL with Let's Encrypt on Ubuntu

Running a website without HTTPS means browsers flag your site as insecure, and modern APIs refuse to work with it. Let's Encrypt removes the cost barrier by issuing free TLS certificates that every major browser trusts. This guide covers the full process: installing Certbot on Ubuntu, obtaining and configuring certificates for Nginx or Apache, setting up automatic renewal, and resolving the issues that commonly appear in production.

If you already have a web server running and a domain pointing to it, you can have HTTPS working within an hour. The key is understanding how domain verification works, how renewal is triggered, and why your web server needs to reload after each renewal.

What Let's Encrypt Provides

Let's Encrypt is a certificate authority run by the Electronic Frontier Foundation that issues TLS certificates at no cost. Unlike commercial certificate providers, the entire process runs through automation. You use a tool called Certbot to prove you control the domain, receive the certificate, and install it on your web server without manual steps.

The technical foundation is ACME, the Automated Certificate Management Environment protocol. Certbot implements this protocol and handles domain verification by serving a challenge file over port 80. Once verified, certificates are issued immediately. Certificates last 90 days, which sounds inconvenient but automated renewal makes this irrelevant in practice.

Certificates from Let's Encrypt are fully trusted by Chrome, Firefox, Safari, Edge, and all other major browsers. There is no difference in browser trust compared to paid certificates. The practical difference is that you never receive an invoice.

What You Need Before Starting

Several conditions must be met before Let's Encrypt will issue a certificate. Skipping this preparation is the most common reason verification fails.

  • A registered domain name: The domain must point to your server's public IP address through an A record.
  • Web server installed and running: Either Nginx or Apache must be installed and serving content on port 80.
  • Port 80 open: Let's Encrypt uses HTTP for domain verification. Port 443 is not needed for initial issuance.
  • SSH access to the server: All steps require command-line access with sudo privileges.
  • UFW or another firewall configured: If you use a firewall, ensure it allows HTTP traffic on port 80.

Verify your domain resolves correctly before starting. Log into your DNS provider and confirm an A record points to your server's public IP address. Then run these checks from your server:

nslookup your-domain.com
ping -c 3 your-domain.com
curl -I http://your-domain.com

The nslookup should return your server's public IP. The ping should succeed. The curl command should return an HTTP response from your web server. If any of these fail, fix the DNS or firewall before proceeding. Certificate issuance will fail if the domain does not resolve correctly.

Installing Certbot on Ubuntu

Ubuntu 22.04 and later includes Certbot in its default repositories. The installation is straightforward. Update your package list first, then install Certbot along with the Nginx or Apache plugin depending on your web server.

For Nginx:

sudo apt update
sudo apt install certbot python3-certbot-nginx

For Apache:

sudo apt update
sudo apt install certbot python3-certbot-apache

If you are unsure which web server you use, run:

sudo systemctl status nginx
sudo systemctl status apache2

Only one should be active. Install the corresponding Certbot plugin. You can install both plugins without issue if you use both web servers on the same machine.

Obtaining Your First Certificate

Certbot can handle both certificate issuance and web server configuration automatically. For most users, the automatic mode is the right choice. It modifies your Nginx or Apache configuration to use HTTPS, sets up the redirect from HTTP to HTTPS, and enables OCSP stapling.

For Nginx with automatic configuration:

sudo certbot --nginx -d your-domain.com -d www.your-domain.com

For Apache with automatic configuration:

sudo certbot --apache -d your-domain.com -d www.your-domain.com

Replace your-domain.com with your actual domain. The -d flag specifies each domain the certificate should cover. You can list multiple domains on a single certificate. Certbot will ask for your email address during the process, which is used for expiry warnings.

If you prefer to obtain the certificate without modifying your web server configuration, use certonly mode instead:

sudo certbot certonly --nginx -d your-domain.com -d www.your-domain.com

Certificates are stored in /etc/letsencrypt/live/your-domain.com/. You will find four files: fullchain.pem (certificate plus intermediates), privkey.pem (private key), cert.pem (certificate only), and chain.pem (intermediate certificates). Your web server configuration points to fullchain.pem and privkey.pem.

Configuring Nginx Manually

If you used certonly mode or want full control over your SSL configuration, you need to edit your Nginx configuration file manually. The file is typically located in /etc/nginx/sites-available/.

Here is a complete HTTPS server block with recommended settings:

server {
    listen 443 ssl http2;
    server_name your-domain.com www.your-domain.com;

    ssl_certificate /etc/letsencrypt/live/your-domain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/your-domain.com/privkey.pem;

    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;

    ssl_session_cache shared:SSL:10m;
    ssl_session_timeout 1d;
    ssl_session_tickets off;

    ssl_stapling on;
    ssl_stapling_verify on;

    root /var/www/html;
    index index.html index.php;

    location / {
        try_files $uri $uri/ =404;
    }
}
Why TLS 1.3 matters: TLS 1.3 removes outdated cryptographic algorithms, reduces handshake latency, and improves both security and performance. All modern browsers support it. Enabling both TLS 1.2 and TLS 1.3 covers all current browsers while phasing out older, weaker protocols.

Redirecting HTTP to HTTPS

Once HTTPS is working, every HTTP request should redirect to the secure version. Add this server block above your HTTPS block:

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

After saving your configuration, test it before reloading:

sudo nginx -t
sudo systemctl reload nginx

The nginx -t command checks your configuration for syntax errors. Always run this before reloading. A syntax error in your configuration can prevent Nginx from starting, taking your site offline.

If you use Apache, enable the rewrite module and redirect with a similar configuration in your VirtualHost on port 80:

sudo a2enmod rewrite
sudo systemctl restart apache2

Automatic Certificate Renewal

Let's Encrypt certificates expire after 90 days. Certbot installs a systemd timer that runs twice daily and renews any certificate within 30 days of expiry. In most cases, this timer works without any intervention. However, the renewal process requires a reload step that is easy to miss.

Check that the timer is active:

sudo systemctl status certbot.timer

If it is not running, enable and start it:

sudo systemctl enable certbot.timer
sudo systemctl start certbot.timer

Test the renewal process with a dry run. This simulates renewal without issuing a new certificate:

sudo certbot renew --dry-run

A successful dry run confirms your renewal configuration works correctly. If the dry run fails, check that port 80 is still open and your web server is running.

Reloading the Web Server After Renewal

When a certificate renews, the web server does not automatically pick up the new certificate file. You must configure Certbot to reload your web server after each successful renewal. Edit the Certbot renewal configuration file:

sudo nano /etc/letsencrypt/renewal/your-domain.com.conf

Add this line to the file:

renew_hook = systemctl reload nginx

For Apache, use:

renew_hook = systemctl reload apache2

After saving, test the full renewal hook:

sudo certbot renew --dry-run

The dry run should complete without errors and confirm the reload hook runs. This step is critical. Without it, visitors continue to see the old certificate even after it renews, which causes certificate expiry warnings in browsers.

Checking Certificate Status

View all certificates managed by Certbot:

sudo certbot certificates

This shows each certificate, the domains it covers, its expiry date, and the renewal status. You can also check the certificate directly from any machine using OpenSSL:

echo | openssl s_client -servername your-domain.com -connect your-domain.com:443 2>/dev/null | openssl x509 -noout -dates

This returns the certificate's notBefore and notAfter dates, confirming it is valid and showing when it expires.

Resolving Common Problems

Certificate issues fall into a few predictable categories. Understanding what went wrong makes fixing it straightforward.

  • Verification failed: Certbot cannot reach your domain on port 80. Check that your firewall allows HTTP traffic, that your DNS A record points to the correct IP, and that your web server is running. Run curl -I http://your-domain.com from the server itself to confirm connectivity.
  • Certificate renewed but site shows old certificate: The renewal hook did not reload the web server. Edit the renewal configuration file and add the reload command as shown above.
  • Mixed content warnings: Your page loads over HTTPS but includes resources from HTTP URLs. Check your page source for hardcoded HTTP links to images, scripts, or stylesheets. Replace them with relative URLs or HTTPS URLs. A Content-Security-Policy header can also enforce HTTPS for all resources.
  • Certificate expired in browser: Automatic renewal failed. Run sudo certbot renew --force-renewal manually, then check the systemd timer status and logs.
  • Too many requests error: You have hit Let's Encrypt's rate limit. Wait an hour and try again. The production rate limit is 50 certificates per registered domain per week.

For mixed content issues, you may find it useful to review common SSL certificate errors and their solutions in more detail. Certbot also provides detailed logs at /var/log/letsencrypt/letsencrypt.log when issues occur.

Hardening Your SSL Configuration

Getting a certificate working is the first step. Reviewing the configuration for security and performance is the second.

Test your SSL configuration with the SSL Labs tool at ssllabs.com/ssltest. This free tool analyses your TLS configuration and assigns a grade from A to F. A rating of A or A+ means your configuration follows current best practices.

Common improvements include enabling OCSP stapling, which reduces verification latency for visitors, configuring a strong cipher list, and enabling HTTP Strict Transport Security headers. These are optional but improve both security and user experience.