Vucense

How to Enable HTTPS on Apache with Let's Encrypt on Ubuntu 24.04

🟢Beginner

Secure Apache with a free SSL certificate from Let's Encrypt using Certbot. Covers mod_ssl, certificate installation, auto-renewal, HTTPS redirect, and security headers.

Divya Prakash

Author

Divya Prakash

AI Systems Architect & Founder

Published

Duration

Reading

13 min

Build

15 min

How to Enable HTTPS on Apache with Let's Encrypt on Ubuntu 24.04
Article Roadmap

Key Takeaways

  • Certbot does the work: sudo certbot --apache -d domain.com obtains the certificate AND modifies your Apache config. You don’t need to manually edit SSL config for a basic setup. For Nginx, use certbot --nginx.
  • Enable mod_ssl first: sudo a2enmod ssl headers — both modules must be active before Certbot can configure HTTPS. See Apache Web Server Setup Guide.
  • Test renewal immediately: sudo certbot renew --dry-run — confirm auto-renewal works on day zero, not day 89 when you’re scrambling before expiry.
  • HSTS after SSL: Once HTTPS is working, add the HSTS header to tell browsers to never use HTTP for this domain again. Follow with Apache Security Hardening to lock down further.

Introduction

Direct Answer: How do I enable HTTPS on Apache with Let’s Encrypt on Ubuntu 24.04?

Install Certbot with sudo apt-get install -y certbot python3-certbot-apache. Enable required Apache modules: sudo a2enmod ssl headers && sudo systemctl restart apache2. Obtain and install a certificate with sudo certbot --apache -d yourdomain.com --non-interactive --agree-tos --email [email protected] --redirect. Certbot will: verify domain ownership via HTTP-01 challenge, download the certificate, modify your Apache virtual host to enable SSL, and configure port 80 to redirect to HTTPS. Verify with curl -sI https://yourdomain.com | head -3. Test auto-renewal with sudo certbot renew --dry-run. Certificates renew automatically via a systemd timer — no cron job setup needed on Ubuntu 24.04.


Prerequisites

Apache must be installed and a virtual host configured for the domain. If starting fresh:

# Install Apache
sudo apt-get install -y apache2

# Enable your site (or use the default)
sudo a2ensite 000-default
sudo systemctl reload apache2

# Verify Apache responds on port 80
curl -sI http://localhost | head -2

Expected output:

HTTP/1.1 200 OK
Server: Apache/2.4.58 (Ubuntu)

DNS must point to this server. Let’s Encrypt verifies domain ownership by connecting to your server on port 80. nslookup yourdomain.com should return your server’s public IP.


Part 1: Install Certbot and Enable Modules

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

# Enable required Apache modules
sudo a2enmod ssl       # SSL/TLS support
sudo a2enmod headers   # Required for HSTS and security headers
sudo a2enmod rewrite   # Required for HTTP→HTTPS redirect

sudo systemctl restart apache2
apache2ctl -M | grep -E "ssl|headers|rewrite"

Expected output:

 headers_module (shared)
 rewrite_module (shared)
 ssl_module (shared)

Part 2: Obtain and Install Certificate

# Replace example.com with your actual domain
DOMAIN="example.com"

sudo certbot --apache \
  -d ${DOMAIN} \
  -d www.${DOMAIN} \
  --non-interactive \
  --agree-tos \
  --email admin@${DOMAIN} \
  --redirect   # Automatically configure HTTP→HTTPS redirect

Expected output:

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
This certificate expires on 2026-07-28.

Deploying certificate to VirtualHost /etc/apache2/sites-enabled/000-default-le-ssl.conf
Redirecting all traffic on port 80 to ssl in /etc/apache2/sites-enabled/000-default.conf

Congratulations! You have successfully enabled HTTPS on https://example.com
# Verify HTTPS is working
curl -sI https://example.com | head -4

Expected output:

HTTP/2 200
server: Apache/2.4.58 (Ubuntu)
content-type: text/html

Part 3: Review What Certbot Created

# View the modified virtual host config
sudo cat /etc/apache2/sites-enabled/000-default-le-ssl.conf

Certbot adds a new HTTPS virtual host and modifies the port 80 block to redirect. The certificate paths are:

/etc/letsencrypt/live/DOMAIN/fullchain.pem  ← Certificate + chain
/etc/letsencrypt/live/DOMAIN/privkey.pem    ← Private key

Part 4: Add Security Headers

# Add security headers to the HTTPS virtual host
sudo tee /etc/apache2/conf-available/security-headers.conf << 'EOF'
# Security headers for all HTTPS virtual hosts
<IfModule mod_headers.c>
    # HSTS: tell browsers to always use HTTPS for 1 year
    Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"
    # Prevent clickjacking
    Header always set X-Frame-Options "SAMEORIGIN"
    # Prevent MIME sniffing
    Header always set X-Content-Type-Options "nosniff"
    # Referrer policy
    Header always set Referrer-Policy "strict-origin-when-cross-origin"
</IfModule>
EOF

sudo a2enconf security-headers
sudo apache2ctl configtest && sudo systemctl reload apache2

# Verify headers are present
curl -sI https://example.com | grep -E "Strict|X-Frame|X-Content"

Expected output:

strict-transport-security: max-age=31536000; includeSubDomains
x-frame-options: SAMEORIGIN
x-content-type-options: nosniff

Part 5: Modern TLS Configuration

sudo tee /etc/apache2/conf-available/ssl-modern.conf << 'EOF'
# Modern TLS: TLS 1.2 and 1.3 only
SSLProtocol             all -SSLv3 -TLSv1 -TLSv1.1
SSLCipherSuite          ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384
SSLHonorCipherOrder     on
SSLSessionCache         "shmcb:${APACHE_RUN_DIR}/ssl_scache(512000)"
SSLSessionCacheTimeout  300
EOF

sudo a2enconf ssl-modern
sudo apache2ctl configtest && sudo systemctl reload apache2

Part 6: Verify Auto-Renewal

# Test renewal (dry run — no changes made)
sudo certbot renew --dry-run

Expected output:

Simulating renewal of an existing certificate for example.com

Congratulations, all simulated renewals succeeded:
  /etc/letsencrypt/live/example.com/fullchain.pem (success)
# Confirm systemd timer is active
systemctl list-timers | grep certbot

Expected output:

Mon 2026-07-28 04:00:00 UTC  91 days left  snap.certbot.renew.timer

Certbot renews automatically ~30 days before expiry. No manual action needed.


Troubleshooting

Challenge failed for domain during certificate request

Cause: Port 80 is blocked by firewall, or DNS doesn’t resolve to this server. Fix:

sudo ufw allow 80/tcp
# Verify DNS: nslookup yourdomain.com (should return your server IP)
# Verify port 80 is accessible: curl http://yourdomain.com

AH01909: example.com:443:0 server certificate does NOT include a SAN

Cause: Old SSL certificate format. Let’s Encrypt certificates are always SAN-based — this error means an old self-signed cert is loading instead. Fix: sudo apache2ctl -t -D DUMP_VHOSTS to find which config is loading the old cert. Remove or update it.

Certificate obtained but site still shows HTTP

Cause: --redirect flag not used, or Apache not reloaded after Certbot ran. Fix:

# Manually add redirect to port 80 virtual host:
sudo tee -a /etc/apache2/sites-available/000-default.conf << 'EOF'
RewriteEngine On
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
EOF
sudo systemctl reload apache2

Conclusion

Apache now serves HTTPS with a Let’s Encrypt certificate, TLS 1.2/1.3 only, security headers, and auto-renewal via systemd. The certificate renews automatically every 60–90 days without manual intervention.

See Apache Web Server Setup on Ubuntu 24.04 for the base installation this guide builds on, and UFW Firewall Tutorial 2026 to ensure ports 80 and 443 are open.


People Also Ask

How often do Let’s Encrypt certificates expire and renew?

Let’s Encrypt certificates expire after 90 days. Certbot’s systemd timer runs twice daily and renews certificates that are within 30 days of expiry — so certificates renew automatically around day 60. The short expiry (90 days vs traditional 1–2 year certs) is intentional: it limits the damage window if a private key is compromised. You should never need to manually renew — if certbot renew --dry-run succeeds, the automatic renewal will work.

Can I use Let’s Encrypt for a wildcard certificate with Apache?

Yes, but wildcard certificates require a DNS challenge instead of the HTTP challenge. Install the DNS plugin for your DNS provider: sudo apt-get install python3-certbot-dns-cloudflare (for Cloudflare). Then: sudo certbot certonly --dns-cloudflare --dns-cloudflare-credentials ~/.secrets/cloudflare.ini -d "*.example.com" -d "example.com". Configure the credentials file with your Cloudflare API token. This approach doesn’t auto-configure Apache — you’ll need to manually add the certificate paths to your virtual host config.



Further Reading

Vucense Guides

Official Documentation

Certificate Tools

Tested on: Ubuntu 24.04 LTS (Hetzner CX22). Apache 2.4.58, Certbot 2.11.0. Last verified: May 16, 2026.

Further Reading

All Dev Corner

Comments