Let’s Encrypt issues free SSL certificates that encrypt traffic between your Apache server and visitors. Without SSL, browsers display security warnings and search engines penalize your site rankings. This guide walks through installing Certbot, generating certificates for your domain, and configuring Apache with HTTPS redirection, HSTS headers, and OCSP stapling. By the end, your server will have automated certificate renewal and pass SSL Labs security tests.
Install Certbot for Apache
This section covers installing Certbot for Apache on Debian. Before proceeding, ensure you have Apache installed on Debian with at least one virtual host configured and your domain’s DNS pointing to your server. Certbot simplifies obtaining and configuring SSL certificates from Let’s Encrypt, working alongside Apache to serve your sites over HTTPS.
Update Debian Packages
Before installing Certbot, first update your system packages to ensure you get the latest version and dependencies:
sudo apt update
sudo apt upgrade
Install Certbot and Apache Plugin
Now that your Debian system is up to date, install Certbot along with its Apache plugin. The Apache plugin is essential because it enables Certbot to interact with Apache, automate obtaining and renewing certificates, and configure Apache to use them. Run the following command to install both packages:
sudo apt install certbot python3-certbot-apache
Certbot versions vary by Debian release: Debian 13 includes Certbot 4.x, Debian 12 includes Certbot 2.x, and Debian 11 includes Certbot 1.x. The commands in this guide work across all versions, though some newer features may only be available in Certbot 2.0 and later.
Important: If you’re using a firewall, ensure ports 80 (HTTP) and 443 (HTTPS) are open before proceeding. This is required for Certbot domain validation and for your website to be accessible. If you have UFW configured on Debian, run:
sudo ufw allow 80/tcp && sudo ufw allow 443/tcp. If connecting remotely via SSH, also ensure SSH is allowed:sudo ufw allow 22/tcpbefore enabling the firewall.
Setting Up Apache and Let’s Encrypt Certificate
This section covers configuring Apache and generating a Let’s Encrypt SSL certificate for your domain using Certbot, including security options that harden your server configuration.
Generate an SSL Certificate
After installing Certbot and its Apache plugin, you can now run Certbot to generate an SSL certificate for your domain. The command includes several options to optimize security.
Here’s a breakdown of the options used:
--apache: Specifies that the web server in use is Apache.--agree-tos: Indicates your agreement to Let’s Encrypt’s terms of service.--redirect: Sets up a permanent 301 redirect from HTTP to HTTPS, ensuring all traffic is encrypted.--hsts: Adds a Strict-Transport-Security header to enforce secure connections.--staple-ocsp: Enables OCSP Stapling, enhancing SSL negotiation performance while maintaining user privacy.--email: This is the email address to which you will receive notifications related to your SSL certificate, such as renewal reminders and security alerts.
Replace admin@example.com with your actual email address and yourdomain.com with your domain name. Then execute the following command:
sudo certbot --apache --agree-tos --redirect --hsts --staple-ocsp --email admin@example.com -d yourdomain.com
Upon successful execution, Certbot will generate an SSL certificate for your domain, configure Apache to use it, and apply the specified security options. As a result, your server will now be secure, and your website will be accessible via HTTPS.
To confirm the certificate was installed correctly, run:
sudo certbot certificates
You should see output showing your domain, certificate path, and expiration date:
Found the following certs:
Certificate Name: yourdomain.com
Domains: yourdomain.com
Expiry Date: YYYY-MM-DD HH:MM:SS+00:00 (VALID: XX days)
Certificate Path: /etc/letsencrypt/live/yourdomain.com/fullchain.pem
Private Key Path: /etc/letsencrypt/live/yourdomain.com/privkey.pem
Key Type: ECDSA
The expiration date and days remaining will reflect your certificate’s actual validity period. Let’s Encrypt certificates are valid for 90 days from issuance.
As of Certbot 2.0, new certificates use ECDSA keys by default. These keys provide equivalent security to RSA with smaller key sizes and faster cryptographic operations.
Securing Multiple Domains with One Certificate
To secure multiple domains or subdomains with a single certificate, simply add additional -d flags. This approach is useful for covering both your root domain and www subdomain, or multiple related sites:
sudo certbot --apache --agree-tos --redirect --hsts --staple-ocsp --email admin@example.com -d yourdomain.com -d www.yourdomain.com -d blog.yourdomain.com
All domains will be included as Subject Alternative Names (SANs) on a single certificate. Each domain must point to your server and be accessible for validation.
Wildcard Certificates with DNS Validation
Wildcard certificates secure all subdomains under a single domain (for example, *.yourdomain.com covers blog.yourdomain.com, shop.yourdomain.com, and any other subdomain). Since wildcard certificates cannot use HTTP validation, you must verify domain ownership through DNS records.
Request a wildcard certificate using DNS challenge:
sudo certbot certonly --manual --preferred-challenges=dns --email admin@example.com --agree-tos -d yourdomain.com -d *.yourdomain.com
Certbot will then prompt you to create a TXT record in your DNS configuration. You will see output similar to:
Please deploy a DNS TXT record under the name: _acme-challenge.yourdomain.com with the following value: aBcDeFgHiJkLmNoPqRsTuVwXyZ123456789 Before continuing, verify the TXT record has been deployed.
Add this TXT record through your DNS provider’s control panel, wait for DNS propagation (typically 1-5 minutes), then press Enter to continue. After validation succeeds, configure Apache to use the certificate by updating your virtual host configuration with the certificate paths shown in the Certbot output.
Wildcard certificates require manual DNS validation and do not auto-renew without DNS API integration. For automated wildcard renewal, consider using Certbot DNS plugins for providers like Cloudflare, Route53, or DigitalOcean. Check
apt search python3-certbot-dnsfor available plugins.
Interactive Configuration Method
For those who prefer a more guided and interactive approach, Certbot provides an alternative method that prompts you for information and configuration choices. Here’s how to use this method:
Run the following command:
sudo certbot --apache
Certbot will initiate an interactive session. Below is a walkthrough of the prompts you may encounter:
- Enter email address (used for urgent renewal and security notices): Provide your email address. Let’s Encrypt will use this to communicate about your certificates.
- Agree to the Let’s Encrypt terms of service: You will be asked to agree to the terms of service. Input A to agree.
- Share your email with the Electronic Frontier Foundation for updates on their work: If you want to support the EFF, input Y for yes. Otherwise, input N for no.
- Which names would you like to activate HTTPS for: Certbot will display the domain names it can issue certificates for. Enter the numbers corresponding to your domains, or press Enter to select all.
- Select the appropriate action: You will be given an option to either:
- 1: Attempt to reinstall the certificate
- 2: Renew & replace the certificate (limit ~5 per 7 days)
- Select the option that suits your needs.
- Choose whether or not to redirect HTTP traffic to HTTPS: You will be asked if you want to redirect HTTP traffic to HTTPS. This is advisable for most websites:
- 1: No redirect – Make no further changes to the webserver configuration.
- 2: Redirect – Make all requests redirect to secure HTTPS access.
- Select option 2 for better security.
Once you have gone through all the prompts and the process is complete, Certbot will output a message similar to the one mentioned, indicating the location of your certificate files and further information.
Automating SSL Certificate Renewal
Let’s Encrypt SSL certificates have a 90-day lifespan, so automated renewal is essential to avoid service disruptions. Fortunately, modern Debian installations of Certbot include a systemd timer that handles renewal automatically. This section explains how to verify the timer is active and provides an alternative cron-based approach if needed.
Verify Systemd Timer (Recommended)
Debian’s Certbot package includes a systemd timer that runs renewal checks twice daily. To verify it is active, run:
sudo systemctl status certbot.timer
As a result, you should see output indicating the timer is active:
● certbot.timer - Run certbot twice daily
Loaded: loaded (/lib/systemd/system/certbot.timer; enabled; preset: enabled)
Active: active (waiting) since [date]; [time] ago
Trigger: [next run date]; [time] left
Triggers: ● certbot.service
The bullet symbol (●) before the service name indicates it is active. Your output will display actual dates and times based on when the timer was started and when it will next run.
If the timer is active and enabled, automatic renewal is already configured. You can skip the cron setup below.
Test Renewal with Dry Run
Regardless of which renewal method you use, always test that renewal works correctly by performing a dry run. This approach simulates the renewal process without making changes to your certificates:
sudo certbot renew --dry-run
If successful, you should see output confirming the simulation succeeded. When your certificate is already valid and not due for renewal, Certbot will simulate the renewal process without making changes:
Saving debug log to /var/log/letsencrypt/letsencrypt.log Processing /etc/letsencrypt/renewal/yourdomain.com.conf Cert not due for renewal, but simulating renewal for dry run Congratulations, all simulated renewals succeeded: /etc/letsencrypt/live/yourdomain.com/fullchain.pem (success)
If there are errors during the dry run, the output will display them clearly. Common issues include DNS resolution failures or firewall blocks. Address any errors before proceeding to automatic scheduling.
Alternative: Schedule Renewals with Cron
Alternatively, if the systemd timer is not available or you prefer cron-based scheduling, you can configure automatic renewal manually. This approach is particularly useful for systems without systemd or when you need custom renewal timing.
First, open the crontab file in edit mode using the following command:
sudo crontab -e
Next, at the end of the file, add the following line to schedule a daily renewal check at 2:30 AM. This timing is chosen for low-traffic periods and helps distribute load on Let’s Encrypt servers:
30 2 * * * /usr/bin/certbot renew --quiet
The --quiet option ensures that the renewal process runs silently in the background without producing any output unless there’s an error.
After adding this line, save and close the file. You have now set up an automatic renewal process for your SSL certificates. As a result, cron will check daily if any certificates are due for renewal and renew them as needed, ensuring that your Apache server always uses valid SSL certificates.
Enhance Apache SSL Configuration
This section optimizes your Apache server’s SSL configuration for security and performance. It covers SSL certificates, HTTP/2, HTTP Strict Transport Security (HSTS), and modern SSL protocols and ciphers. To apply these settings, you need the Apache modules mod_ssl, mod_socache_shmcb, mod_rewrite, mod_headers, and mod_http2.
Enable Required Apache Modules
First, before modifying the SSL configuration, ensure all required modules are enabled. While Certbot typically enables mod_ssl automatically during certificate setup, this command verifies all required modules are active. If a module is already enabled, a2enmod will note it without making changes:
sudo a2enmod ssl socache_shmcb rewrite headers http2
Then reload Apache to activate the modules:
sudo systemctl reload apache2
Edit the Apache Configuration File
To start, you need to access the configuration file for your domain within Apache. If Certbot has already created this file, open it with:
sudo nano /etc/apache2/sites-available/your_domain-le-ssl.conf
Certbot automatically creates SSL-enabled virtual host files with the -le-ssl.conf suffix. If this file does not exist, use your_domain.conf instead. Once inside, locate the <VirtualHost *:443> block and add the security configurations below. For the HTTP redirect rules, add them to the <VirtualHost *:80> block if it exists.
Redirect HTTP to HTTPS
Set up a rule to redirect all HTTP traffic to HTTPS, ensuring all connections to your server are secure. This rule excludes requests to the .well-known/acme-challenge/ directory, which Certbot uses for domain validation. Add the following configuration within the <VirtualHost *:80> block:
RewriteEngine On
RewriteCond %{REQUEST_URI} !^/\.well\-known/acme\-challenge/
RewriteRule ^(.*)$ https://%{HTTP_HOST}$1 [R=301,L]
Enable SSL and Specify Certificates
Within the <VirtualHost *:443> block, enable SSL and specify the paths to your SSL certificate and private key. Let’s Encrypt stores certificates in /etc/letsencrypt/live/yourdomain.com/:
SSLEngine on
SSLCertificateFile /etc/letsencrypt/live/yourdomain.com/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/yourdomain.com/privkey.pem
Replace yourdomain.com with your actual domain name.
Enable HTTP/2
To improve performance, enable HTTP/2 support. All currently supported Debian versions (11 Bullseye, 12 Bookworm, and 13 Trixie) ship Apache 2.4.57 or newer with full HTTP/2 support via mod_http2. After enabling the module earlier, add this directive to the <VirtualHost *:443> block:
Protocols h2 http/1.1
Implement HSTS
Add a Strict-Transport-Security header to enforce secure connections for 2 years:
Header always set Strict-Transport-Security "max-age=63072000; includeSubDomains; preload"
The HSTS header enforces secure connections for 2 years (63072000 seconds). The includeSubDomains directive applies the policy to all subdomains, and preload allows your domain to be submitted to the HSTS preload list, preventing any initial unencrypted connection attempts.
Warning: Before including the
preloaddirective and submitting your domain to the HSTS preload list, understand that removal takes months. Only enable preload after confirming that HTTPS works correctly for your domain and all subdomains.
Add Additional Security Headers
Beyond HSTS, several HTTP security headers protect against common web vulnerabilities. Add these headers to your <VirtualHost *:443> block for comprehensive protection:
Header always set X-Content-Type-Options "nosniff"
Header always set X-Frame-Options "SAMEORIGIN"
Header always set Referrer-Policy "strict-origin-when-cross-origin"
Each header serves a specific security purpose:
X-Content-Type-Options: nosniffprevents browsers from MIME-sniffing responses away from the declared content type, blocking attacks that rely on disguising malicious files.X-Frame-Options: SAMEORIGINprevents your site from being embedded in iframes on other domains, protecting against clickjacking attacks.Referrer-Policy: strict-origin-when-cross-origincontrols what referrer information is sent with requests, balancing functionality with privacy.
Configure SSL Protocols and Ciphers
Specify which SSL protocols and ciphers to use for high security and compatibility:
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:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-CHACHA20-POLY1305
SSLHonorCipherOrder off
SSLSessionTickets off
This configuration explicitly enables only TLSv1.2 and TLSv1.3, which are the most secure protocols available. Additionally, the cipher suite prioritizes modern authenticated encryption algorithms (ECDHE with AES-GCM or ChaCha20-Poly1305) that provide forward secrecy and strong protection against attacks.
Enable OCSP Stapling
Enable OCSP stapling to improve SSL negotiation performance while maintaining visitor privacy:
SSLUseStapling On
SSLStaplingCache "shmcb:/var/run/apache2/ssl_stapling(32768)"
OCSP stapling allows the server to fetch and cache certificate revocation status, then include it in the TLS handshake. This improves connection speed by eliminating the client’s need to contact the certificate authority separately, while also protecting visitor privacy.
Note: The
SSLStaplingCachedirective must be placed in the global Apache configuration (outside any<VirtualHost>block), typically in/etc/apache2/mods-enabled/ssl.confor at the top of your site config file. TheSSLUseStapling Ondirective can be inside the VirtualHost.
Validate and Apply the Changes
After making configuration changes, validate the Apache configuration to catch syntax errors before reloading:
sudo apachectl configtest
You should see:
Syntax OK
If there are no issues, apply the changes by reloading Apache. Note that using reload instead of restart allows Apache to apply configuration changes without dropping active connections:
sudo systemctl reload apache2
Next Steps for Server Security
With your Apache server secured with SSL encryption and optimized security headers, consider implementing additional security layers:
- Configure UFW firewall on Debian to restrict port access and block unwanted traffic
- Install Fail2ban on Debian to protect against brute-force attacks on SSH and web services
- Implement ModSecurity with Apache for web application firewall protection
- Configure unattended upgrades on Debian to keep your system patched automatically
If you are using Nginx instead of Apache, see our guide on securing Nginx with Let’s Encrypt on Debian. For more advanced Certbot options and automation, consult the official Certbot documentation.
Test Your SSL Configuration
Verify your security grade and identify any remaining issues using the SSL Labs Server Test. Enter your domain on their website or visit:
https://www.ssllabs.com/ssltest/analyze.html?d=yourdomain.com
Replace yourdomain.com with your actual domain. A properly configured server following this guide should achieve an A or A+ rating.
Troubleshooting Common Issues
This section covers common problems you may encounter when securing Apache with Let’s Encrypt and how to resolve them.
Certificate Validation Fails
When Certbot cannot validate your domain, you will see an error like:
Failed authorization procedure. yourdomain.com (http-01): urn:ietf:params:acme:error:connection :: The server could not connect to the client to verify the domain
This error typically means port 80 is blocked or Apache is not serving the validation file. To diagnose, check if Apache is listening on port 80:
sudo ss -tlnp | grep :80
If Apache is running correctly, you should see output showing it listening:
LISTEN 0 511 0.0.0.0:80 0.0.0.0:* users:(("apache2",pid=1234,fd=4))
Should nothing be listening on port 80, start Apache. Alternatively, if a firewall is blocking the port, check firewall status first:
sudo ufw status
When UFW is active and blocking ports 80 and 443, allow HTTP and HTTPS traffic:
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
Apache Fails to Start After SSL Configuration
If Apache fails to start after editing the configuration, check the error log:
sudo journalctl -xeu apache2
A common error is a syntax mistake in the configuration file:
AH00526: Syntax error on line 42 of /etc/apache2/sites-available/your_domain.conf: Invalid command 'SSLEngine', perhaps misspelled or defined by a module not included
This error indicates the SSL module is not enabled. To fix it, enable the module with:
sudo a2enmod ssl
sudo systemctl reload apache2
Certificate Renewal Fails
If automatic renewal fails, you will receive an email notification. Check the renewal log:
sudo cat /var/log/letsencrypt/letsencrypt.log
Common causes include changed DNS records, firewall rules blocking validation, or rate limits. Test renewal manually:
sudo certbot renew --force-renewal
Caution: Use
--force-renewalonly for troubleshooting. Each forced renewal counts against Let’s Encrypt rate limits (5 duplicate certificates per domain per week). For routine testing, use--dry-runinstead.
Finally, verify the renewal succeeded by checking the certificate expiration date:
sudo certbot certificates
Rate Limit Errors
If you see an error mentioning rate limits, you have exceeded Let’s Encrypt’s issuance limits. The main limits are 50 certificates per registered domain per week and 5 duplicate certificates per week. If you hit this limit:
- Wait for the rate limit window to reset (typically 7 days)
- Use the staging environment for testing: add
--stagingto your Certbot commands - Consolidate multiple domains into a single certificate using
-dflags
DNS Validation Takes Too Long (Wildcard Certificates)
Should DNS validation for wildcard certificates fail or time out, the TXT record may not have propagated yet. Before pressing Enter to continue, verify the record exists using dig or an online DNS lookup tool:
dig TXT _acme-challenge.yourdomain.com +short
If validation succeeds, you should see the challenge string Certbot asked you to add. However, if the output is empty, wait a few more minutes for DNS propagation. Note that some DNS providers take up to 30 minutes to propagate changes globally. You can also check propagation status at third-party DNS checking services like whatsmydns.net.
Mixed Content Warnings in Browser
When your website shows a mixed content warning, it means some resources are still loading over HTTP instead of HTTPS. To identify the problematic resources, check your browser’s developer console for details. Then update all internal links, images, scripts, and stylesheets to use HTTPS or relative URLs. For WordPress sites, plugins like Really Simple SSL can help fix mixed content issues automatically.
Test SSL Certificate Chain
To verify your SSL certificate chain is properly configured and trusted by clients, use OpenSSL to test the connection:
openssl s_client -connect yourdomain.com:443 -servername yourdomain.com
The command will display detailed certificate information and connection details. Press Ctrl+C to exit. In the output, look for the line Verify return code: 0 (ok), which confirms the certificate chain is valid and trusted by your system. Any other return code indicates chain validation failed.
Remove Certbot and Certificates
If you need to remove Certbot and clean up your SSL configuration, follow these steps. First, revoke any active certificates before uninstalling (optional but recommended if you’re decommissioning the domain):
sudo certbot revoke --cert-path /etc/letsencrypt/live/yourdomain.com/cert.pem
Remember to replace yourdomain.com with your actual domain. Afterward, remove the Certbot packages and their dependencies:
sudo apt remove certbot python3-certbot-apache
sudo apt autoremove
The autoremove command cleans up orphaned dependencies that were installed alongside Certbot.
Remove Certificate Files
Warning: The following command permanently deletes all Let’s Encrypt certificates, private keys, renewal configurations, and account data. Only proceed if you no longer need SSL certificates managed by Certbot on this server.
To completely remove all Certbot data including certificates and account information:
sudo rm -rf /etc/letsencrypt
After removing Certbot, update your Apache virtual host configurations to remove any SSL directives that reference the deleted certificates. Otherwise, Apache will fail to start due to missing certificate files.
Conclusion
You now have a production-ready Apache server secured with automated Let’s Encrypt SSL certificates. Your configuration includes HTTP to HTTPS redirection, HSTS with preload support, OCSP stapling for faster handshakes, and TLSv1.2/TLSv1.3 with modern cipher suites. With systemd timer or cron handling automatic renewal, your certificates stay valid without manual intervention. If you plan to host a CMS, our guide on installing WordPress with Apache on Debian walks through the complete setup.