Key Takeaways
- Certbot does the work:
sudo certbot --apache -d domain.comobtains the certificate AND modifies your Apache config. You don’t need to manually edit SSL config for a basic setup. For Nginx, usecertbot --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.
Related Vucense Guides
- Apache Security Hardening 2026 — combine HTTPS with ModSecurity WAF and security headers
- Nginx Reverse Proxy with Let’s Encrypt — alternative to direct Apache HTTPS for load balancing
- Self-Hosted Web Infrastructure 2026 — deploy Apache with HTTPS in production stacks
Further Reading
Vucense Guides
- Apache Web Server Setup on Ubuntu 24.04 — prerequisite: install Apache before adding SSL
- Apache Security Hardening 2026 — secure Apache post-HTTPS setup
- Nginx Reverse Proxy Tutorial 2026 — SSL termination with Nginx as an alternative
- UFW Firewall Configuration 2026 — open ports 80 and 443 for this SSL setup
Official Documentation
- Let’s Encrypt Official — free HTTPS certificate authority
- Certbot Documentation — Let’s Encrypt client; Apache plugin at
certbot plugins - Certbot Apache Plugin — automatic Apache configuration
- Apache SSL/TLS Configuration — SSL module reference (mod_ssl)
- RFC 8555: ACME Protocol — protocol behind Let’s Encrypt automation
- acme.sh Shell Client — alternative pure-shell ACME client
Certificate Tools
- SSL Labs Best Practices — TLS hardening guidelines
- Mozilla SSL Configuration Generator — optimal Apache SSL config
- Certificate Transparency Search — audit your issued certificates
- SSL Protocol Analyzer — test your HTTPS configuration
Tested on: Ubuntu 24.04 LTS (Hetzner CX22). Apache 2.4.58, Certbot 2.11.0. Last verified: May 16, 2026.