How to Secure Apache with Let’s Encrypt on Debian

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 a Debian-based Linux system. Before proceeding, ensure you have Apache installed on Debian and running. Certbot simplifies obtaining and configuring SSL certificates from Let’s Encrypt, working alongside Apache to serve your sites over HTTPS.

Update Debian Package Repositories Before Certbot Installation for Apache

Before installing Certbot, it’s crucial to ensure that the package repositories and the existing packages on your Debian system are updated. Keeping the system updated ensures you install the latest version of Certbot and dependencies. Execute the following commands to update the package repositories and upgrade the existing packages:

sudo apt update
sudo apt upgrade

Install Certbot and Apache Plugin

Now that your Debian system is up to date, the next step is to install Certbot along with its Apache plugin. The Apache plugin is essential as 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 Certbot and the Apache plugin:

sudo apt install certbot python3-certbot-apache

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. You can configure this now using UFW: sudo ufw allow 80/tcp && sudo ufw allow 443/tcp (if UFW is enabled).

Setting Up Apache and Let’s Encrypt Certificate

In this section, we’ll guide you through configuring Apache and generating a Let’s Encrypt SSL certificate for your domain using Certbot. We’ll also cover the use of various options that improve the security of your server configuration.

Certbot Configuration and SSL Certificate Generation on Apache with Debian

After installing Certbot and its Apache plugin, the next action is to 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 you@example.com with your actual email and yourdomain.com with your domain name. Execute the following command:

sudo certbot --apache --agree-tos --redirect --hsts --staple-ocsp --email you@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. Your server will now be secure, and your website will be accessible via HTTPS.

Verify the certificate was installed correctly:

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: 2026-05-15 14:22:00+00:00 (VALID: 171 days)
    Certificate Path: /etc/letsencrypt/live/yourdomain.com/fullchain.pem
    Private Key Path: /etc/letsencrypt/live/yourdomain.com/privkey.pem
    Key Type: ECDSA

As of Certbot 2.0, new certificates use ECDSA keys by default, which 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, add additional -d flags. This 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 you@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 you@example.com --agree-tos -d yourdomain.com -d *.yourdomain.com

Certbot will 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-dns for available plugins.

Alternative Certbot Configuration Method with Apache

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. Input the numbers corresponding to your domains or leave them blank for everyone.
  • 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, making automated renewal essential to avoid service disruptions. 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. Verify it is active:

sudo systemctl status certbot.timer

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 Mon 2025-11-25 10:00:00 UTC; 2h ago
    Trigger: Tue 2025-11-26 00:00:00 UTC; 11h left
   Triggers: * certbot.service

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, test that renewal works correctly by performing a dry run. This simulates the renewal process without making changes to your certificates:

sudo certbot renew --dry-run

You should see output confirming the simulation succeeded. If 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

If the systemd timer is not available or you prefer cron-based scheduling, you can configure automatic renewal manually. This approach is 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

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’ve now set up an automatic renewal process for your SSL certificates. Cron will check daily if any certificates are due for renewal and renew them as needed. This ensures that your Apache server always uses valid SSL certificates, maintaining a secure user connection.

Enhance Apache SSL Configuration

In this section, we’ll optimize your Apache server’s performance by modifying its SSL configuration. This involves setting up SSL certificates, enabling HTTP/2, implementing HTTP Strict Transport Security (HSTS), and configuring SSL protocols and ciphers. This configuration requires the Apache modules mod_ssl, mod_socache_shmcb, mod_rewrite, mod_headers, and mod_http2.

Enable Required Apache Modules

Before modifying the SSL configuration, ensure all required modules are enabled. Certbot typically enables mod_ssl automatically during certificate setup, but 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

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 doesn’t exist, use your_domain.conf instead. This opens the configuration file in Nano text editor. 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

First, we’ll set up a rule to redirect all HTTP traffic to HTTPS, ensuring that all connections to your server are secure. This rule excludes requests to the .well-known/acme-challenge/ directory, which is used by Certbot for domain validation during the certificate issuance process. 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

Next, within the <VirtualHost *:443> block, we’ll 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, we’ll enable HTTP/2. All supported Debian versions (12 Bookworm, 13 Trixie) include Apache 2.4.57 or newer with full HTTP/2 support via mod_http2. If you enabled the module earlier, add this directive to the <VirtualHost *:443> block:

Protocols h2 http/1.1

Implement HSTS

We’ll also 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.

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: nosniff prevents browsers from MIME-sniffing responses away from the declared content type, blocking attacks that rely on disguising malicious files.
  • X-Frame-Options: SAMEORIGIN prevents your site from being embedded in iframes on other domains, protecting against clickjacking attacks.
  • Referrer-Policy: strict-origin-when-cross-origin controls what referrer information is sent with requests, balancing functionality with privacy.

Configure SSL Protocols and Ciphers

Next, we’ll specify which SSL protocols and ciphers should be used to ensure 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, the most secure protocols available. 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

Finally, we’ll enable OCSP stapling, a feature that improves the performance of SSL negotiation 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.

Validate and Apply the Changes

Once you’re done, save and exit the file. It’s vital to validate your Apache configuration to ensure no syntax errors. Run this command to check:

sudo apachectl configtest

You should see:

Syntax OK

If there are no issues, apply the changes by reloading Apache. Using reload instead of restart allows Apache to apply configuration changes without dropping active connections:

sudo systemctl reload apache2

With your Apache server now secured with SSL encryption and optimized security headers, consider implementing additional security layers. You can configure UFW firewall on Debian to restrict port access, install Fail2ban on Debian to protect against brute-force attacks, and implement ModSecurity with Apache for web application firewall protection. To keep your system patched automatically, configure unattended upgrades on Debian. If you are using Nginx instead, see our guide on securing Nginx with Let’s Encrypt on Debian.

Test your SSL configuration using SSL Labs by visiting the following URL in your browser to verify security grade and identify any remaining issues. Replace yourdomain.com with your actual domain:

https://www.ssllabs.com/ssltest/analyze.html?d=yourdomain.com

A properly configured server 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

If 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 typically means port 80 is blocked or Apache is not serving the validation file. Check if Apache is listening on port 80:

sudo ss -tlnp | grep :80

You should see output showing Apache listening:

LISTEN 0      511          0.0.0.0:80        0.0.0.0:*    users:(("apache2",pid=1234,fd=4))

If nothing is listening on port 80, start Apache. If a firewall is blocking the port, check firewall status first:

sudo ufw status

If 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 means the SSL module is not enabled. Enable it 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

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 --staging to your Certbot commands
  • Consolidate multiple domains into a single certificate using -d flags

Mixed Content Warnings in Browser

If your website shows a mixed content warning, some resources are still loading over HTTP instead of HTTPS. Check your browser’s developer console for details. 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. Look for the line Verify return code: 0 (ok) in the output, which confirms the certificate chain is valid and trusted by your system. Any other return code indicates chain validation failed.

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. To further harden your server, consider setting up Fail2ban for brute-force protection, configuring UFW firewall rules, or deploying ModSecurity as a web application firewall. If you plan to host a CMS, our guide on installing WordPress with Apache on Debian walks through the complete setup.

Leave a Comment