How to Install Free SSL with Let's Encrypt on Ubuntu

12 min read 2,204 words
Setting Up Free SSL with Let's Encrypt featured image

Setting Up Free SSL with Let's Encrypt on Ubuntu

Browser security warnings stop visitors in their tracks, and modern web APIs simply refuse to work without HTTPS. If you run a website on Ubuntu and have been putting off SSL setup because of costs, Let's Encrypt removes that barrier entirely. This guide walks through the complete process: installing Certbot on Ubuntu, obtaining certificates for Nginx or Apache, setting up automatic renewal, and fixing the problems that commonly surface in real server environments.

With a domain pointing to your server and Certbot handling the technical work, HTTPS can be live within thirty minutes. The important part is understanding how domain verification functions, what triggers certificate renewal, and why your web server needs to reload after each renewal cycle.

What Let's Encrypt Offers

Let's Encrypt is a non-profit certificate authority that issues TLS certificates at no charge. The process runs entirely through automation, meaning you never submit a purchase order, wait for approval, or handle manual renewals. Instead, you use a tool called Certbot that communicates with the Let's Encrypt servers using the ACME (Automated Certificate Management Environment) protocol.

Certbot handles domain verification by serving a challenge response over port 80. Once it confirms you control the domain, the certificate is issued immediately. Certificates are valid for 90 days, which sounds like a maintenance burden, but automated renewal handles this transparently in the background.

Certificates from Let's Encrypt carry the same browser trust as commercial certificates. Chrome, Firefox, Safari, Edge, and all other major browsers recognise them without warnings or exceptions. The practical benefit is straightforward: free TLS encryption for your website without recurring certificate costs.

Prerequisites Before You Begin

Let's Encrypt will not issue a certificate unless specific conditions are met. Most verification failures trace back to skipping this preparation step.

  • A registered domain name: An A record must point to your server's public IP address.
  • Web server installed and running: Either Nginx or Apache must be serving content on port 80.
  • Port 80 accessible: Let's Encrypt uses HTTP for domain verification. Port 443 is not required for initial issuance.
  • SSH access with sudo privileges: All steps require command-line access with administrative rights.
  • Firewall configured to allow HTTP: If you run UFW or another firewall, ensure it permits traffic on port 80.

Before starting, confirm your domain resolves correctly. Check your DNS provider's control panel and verify the A record points to your server's public IP. 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 without packet loss. The curl command should show an HTTP response from your web server. If any check fails, resolve the DNS or firewall issue before requesting a certificate. Certificate issuance will fail if the domain does not resolve correctly from the server.

Installing Certbot on Ubuntu

Ubuntu 22.04 and later includes Certbot in its default repositories, which makes installation straightforward. Update your package list first, then install Certbot along with the appropriate plugin for 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 is running, check with:

sudo systemctl status nginx
sudo systemctl status apache2

Only one should show as active. Install the corresponding Certbot plugin. You can safely install both plugins if you run both web servers on the same machine.

Obtaining Your First Certificate

Certbot can handle certificate issuance and web server configuration automatically. For most setups, this automatic mode is the right choice. It modifies your Nginx or Apache configuration to use HTTPS, redirects HTTP to HTTPS, and enables OCSP stapling without manual editing.

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 include multiple domains on a single certificate. Certbot prompts for your email address during the process, which is used only for expiry notifications.

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/. This directory contains four files: fullchain.pem (certificate plus intermediate certificates), privkey.pem (private key), cert.pem (certificate only), and chain.pem (intermediate certificates). Your web server configuration references fullchain.pem and privkey.pem.

Manual Nginx SSL Configuration

If you used certonly mode or want full control over your SSL settings, edit your Nginx configuration file directly. The file typically lives in /etc/nginx/sites-available/.

Here is a complete HTTPS server block with settings that reflect current best practices:

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 deprecated cryptographic algorithms, reduces handshake latency, and improves both security and performance. All modern browsers support it. Running both TLS 1.2 and TLS 1.3 covers every current browser while phasing out older protocols that carry known vulnerabilities.

Setting Up HTTP to HTTPS Redirects

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

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

After saving, test the configuration before reloading:

sudo nginx -t
sudo systemctl reload nginx

The nginx -t command validates your configuration for syntax errors. Always run this before reloading. A syntax error can prevent Nginx from starting, which would take your site offline.

For Apache, enable the rewrite module and add a similar redirect in your VirtualHost listening 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. On most systems, this timer runs without any intervention. However, the renewal process includes a reload step that is easy to overlook.

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 is working correctly. If the dry run fails, verify that port 80 remains open and your web server is running. Setting up auto-renewal properly means certificates renew automatically without manual intervention, which is worth confirming before relying on it.

Reloading the Web Server After Renewal

When a certificate renews, the web server does not automatically detect 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, verify the renewal hook works:

sudo certbot renew --dry-run

The dry run should complete without errors and indicate the reload hook executes. This step is critical. Without it, visitors continue to see the old certificate after it renews, which causes browser warnings when the original certificate approaches expiry.

Checking Certificate Status

View all certificates managed by Certbot:

sudo certbot certificates

This command lists each certificate, the domains it covers, its expiry date, and 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 valid-from and valid-until dates, confirming it is currently 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, your DNS A record points to the correct IP, and your web server is running. Run curl -I http://your-domain.com from the server to confirm local 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 in the previous section.
  • Mixed content warnings: Your page loads over HTTPS but includes resources from HTTP URLs. Inspect your page source for hardcoded HTTP links to images, scripts, or stylesheets. Replace them with relative URLs or HTTPS URLs. Mixed content issues can also surface after HTTPS migration and may require reviewing your SSL certificate errors systematically.
  • Certificate expired in browser: Automatic renewal failed. Run sudo certbot renew --force-renewal manually, then check the systemd timer status and logs at /var/log/letsencrypt/letsencrypt.log.
  • 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.

Testing and Hardening Your SSL Setup

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 analysis tool evaluates your TLS configuration and assigns a letter grade from A to F.

A rating of A or A+ means your configuration follows current best practices. Common improvements that push scores higher include enabling OCSP stapling, configuring a strong cipher list, and adding HTTP Strict Transport Security headers. These refinements affect both security and the experience your visitors have when connecting to your site.

For those running multiple services behind a single server, a reverse proxy setup with Nginx can centralise SSL termination and simplify certificate management across all your web applications. If you want a practical walkthrough of that configuration, the nginx reverse proxy with Let's Encrypt guide covers the full process on Ubuntu.

Frequently Asked Questions

How long does Let's Encrypt certificate installation take?
For a server with all prerequisites in place, the full process takes 15 to 30 minutes. Most of that time is DNS propagation if you recently created your domain record. Certificate issuance itself takes seconds once verification begins.
Do I need to renew certificates manually?
No. Certbot's systemd timer handles renewals automatically. The only manual step is confirming the renewal hook reloads your web server after each renewal, as covered earlier in this guide. Running sudo certbot renew --dry-run periodically is a useful way to verify the automation is working as expected.
Can I use Let's Encrypt on multiple subdomains?
Yes. You can include multiple subdomains in a single certificate request using multiple -d flags. For five or more subdomains, consider a wildcard certificate using sudo certbot certonly --manual --preferred-challenges=dns -d your-domain.com -d *.your-domain.com. Wildcard certificates require DNS verification through a TXT record rather than HTTP verification.
What happens if I switch web servers after getting a certificate?
The certificate files remain valid regardless of which web server you use. Simply update your new web server configuration to point to the existing certificate files in /etc/letsencrypt/live/your-domain.com/. You do not need to request a new certificate when switching between Nginx and Apache.
Can I move a Let's Encrypt certificate to a different server?
Yes. Copy the contents of /etc/letsencrypt/ to your new server. This directory contains the certificate, private key, and renewal configuration. Keep the directory structure intact. You do not need to reissue the certificate, but back up the directory before any server migration.
Is Let's Encrypt suitable for business websites?
Yes. Business websites use Let's Encrypt extensively. The certificate provides the same encryption strength and browser trust as paid certificates. The 90-day validity period, which automated renewal handles transparently, is the only practical difference. Many UK businesses now rely on Let's Encrypt as their standard approach to website security.
How do I know if my auto-renewal is working?
Check the systemd timer status with sudo systemctl status certbot.timer. The output should show the timer as active. You can also run sudo certbot renew --dry-run to test the renewal process without making any changes. Certbot logs successful renewals to /var/log/letsencrypt/letsencrypt.log, which you can check if renewals seem to be failing silently.