Back to Guides
Web ServersApache

Apache SSL Certificate Configuration: The Complete Guide

Set up HTTPS on Apache with mod_ssl - from your first certificate to production-hardened configurations.

12-15 min readConfiguration Guide
Apache SSL Certificate Configuration Guide

1. PKI Pro Quick Scan

Already know Apache? Here's the quick reference:

  • Module: mod_ssl - enable with a2enmod ssl (Debian/Ubuntu) or LoadModule (RHEL/CentOS)
  • Cert paths: SSLCertificateFile, SSLCertificateKeyFile, SSLCertificateChainFile (deprecated in 2.4.8+, use SSLCertificateFile with full chain)
  • Hardened ciphers: SSLProtocol all -SSLv3 -TLSv1 -TLSv1.1 + SSLCipherSuite from Mozilla config generator
  • Test config: apachectl configtest before reload
  • Common mistake: Wrong file permissions on private key (should be 600, owned by root)

2. Apache SSL Fundamentals

Apache uses mod_ssl to handle TLS/SSL connections. It's been the workhorse of HTTPS on the web since the late 1990s.

What You'll Need:

  • Apache 2.4.x (2.2 is EOL - upgrade if you're still on it)
  • mod_ssl enabled
  • A certificate (from Let's Encrypt, commercial CA, or self-signed for testing)
  • The private key that matches the certificate
  • Intermediate certificate(s) / chain file

Apache 2.4 vs 2.2 - Key Differences:

FeatureApache 2.2Apache 2.4
Chain fileSeparate SSLCertificateChainFileInclude in SSLCertificateFile
OCSP StaplingSupported with additional configSimpler SSLUseStapling on + SSLStaplingCache
SNILimitedFull support
HTTP/2Not supportedmod_http2

This guide assumes Apache 2.4. If you're on 2.2, seriously - upgrade first.

3. Enabling mod_ssl

Debian/Ubuntu:

# Enable the module
sudo a2enmod ssl

# Enable the default SSL site (optional)
sudo a2ensite default-ssl

# Restart Apache
sudo systemctl restart apache2

RHEL/CentOS/Rocky/Alma:

# Install mod_ssl if not present
sudo dnf install mod_ssl

# It auto-loads, but verify in httpd.conf or ssl.conf
grep -r "LoadModule ssl_module" /etc/httpd/

# Restart Apache
sudo systemctl restart httpd

Verify mod_ssl is loaded:

apachectl -M | grep ssl
# Should show: ssl_module (shared)

4. Basic SSL Virtual Host

Here's a minimal working configuration:

<VirtualHost *:443>
    ServerName www.example.com
    DocumentRoot /var/www/html

    SSLEngine on
    SSLCertificateFile /etc/ssl/certs/example.com.crt
    SSLCertificateKeyFile /etc/ssl/private/example.com.key

    # For Apache 2.4.8+, include chain in certificate file
    # For older versions, use SSLCertificateChainFile
</VirtualHost>

File Locations (Conventions):

DistroCertificatesPrivate KeysConfig
Debian/Ubuntu/etc/ssl/certs//etc/ssl/private//etc/apache2/sites-available/
RHEL/CentOS/etc/pki/tls/certs//etc/pki/tls/private//etc/httpd/conf.d/

Test Before Reload:

# Always test config before applying
apachectl configtest
# Should show: Syntax OK

# Then reload (not restart - keeps connections alive)
sudo systemctl reload apache2  # or httpd

5. Certificate Chain Configuration

The Apache 2.4.8+ Way (Recommended):

Concatenate your certificate and chain into one file:

cat example.com.crt intermediate.crt > example.com-fullchain.crt

Then reference just one file:

SSLCertificateFile /etc/ssl/certs/example.com-fullchain.crt
SSLCertificateKeyFile /etc/ssl/private/example.com.key

Order matters: Your certificate first, then intermediates, root last (or omit root - browsers have it).

The Legacy Way (Apache < 2.4.8):

SSLCertificateFile /etc/ssl/certs/example.com.crt
SSLCertificateKeyFile /etc/ssl/private/example.com.key
SSLCertificateChainFile /etc/ssl/certs/intermediate.crt

Verify Chain is Correct:

openssl s_client -connect www.example.com:443 -servername www.example.com
# Look for "Certificate chain" section
# Should show your cert, then intermediate(s)

Learn more about certificate chains → | OpenSSL s_client guide →

6. Production-Hardened Configuration

Don't just get HTTPS working - get it secure.

Mozilla SSL Configuration Generator:

Use ssl-config.mozilla.org to generate configs. Select:

  • • Apache
  • • Modern (TLS 1.3 only), Intermediate (TLS 1.2+), or Old (legacy support)
  • • Your Apache and OpenSSL versions

Intermediate Profile Example:

Always regenerate from Mozilla's tool for your exact Apache/OpenSSL versions rather than copy-pasting blindly. The example below is a starting point.

<VirtualHost *:443>
    ServerName www.example.com
    DocumentRoot /var/www/html

    SSLEngine on
    SSLCertificateFile /etc/ssl/certs/example.com-fullchain.crt
    SSLCertificateKeyFile /etc/ssl/private/example.com.key

    # TLS 1.2 and 1.3 only (if your OpenSSL build supports TLS 1.3)
    SSLProtocol all -SSLv3 -TLSv1 -TLSv1.1

    # Strong cipher suites
    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

    SSLHonorCipherOrder off
    SSLSessionTickets off

    # OCSP Stapling
    SSLUseStapling on
    SSLStaplingResponderTimeout 5
    SSLStaplingReturnResponderErrors off

    # HSTS (be careful - commits you to HTTPS)
    Header always set Strict-Transport-Security "max-age=63072000"
    # Optional: Header always set Strict-Transport-Security "max-age=63072000; includeSubDomains"
    # Only add includeSubDomains after testing all subdomains work with HTTPS
</VirtualHost>

# OCSP Stapling Cache (put outside VirtualHost)
SSLStaplingCache shmcb:/var/run/apache2/ssl_stapling(128000)

# Note: Verify your CA actually supports OCSP; some misconfigured
# responders can cause startup warnings or stapling failures

What Each Directive Does:

DirectivePurpose
SSLProtocol all -SSLv3 -TLSv1 -TLSv1.1Only TLS 1.2 and 1.3
SSLCipherSuiteWhich encryption algorithms to allow
SSLHonorCipherOrder offLet client choose cipher (modern approach)
SSLSessionTickets offImproves forward secrecy
SSLUseStapling onOCSP stapling for faster validation
Header ... Strict-Transport-SecurityHSTS - enforces HTTPS (verify A/A+ SSL Labs grade before enabling)

Learn about HSTS → | Forward secrecy explained →

7. HTTP to HTTPS Redirect

Don't leave port 80 hanging - redirect it.

<VirtualHost *:80>
    ServerName www.example.com
    ServerAlias example.com

    # Redirect all HTTP to HTTPS
    Redirect permanent / https://www.example.com/
</VirtualHost>

Or using mod_rewrite (more flexible):

<VirtualHost *:80>
    ServerName www.example.com

    RewriteEngine On
    RewriteCond %{HTTPS} off
    RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
</VirtualHost>

8. SNI - Multiple SSL Sites on One IP

Server Name Indication lets you host multiple HTTPS sites on a single IP address.

# Site 1
<VirtualHost *:443>
    ServerName www.site1.com
    SSLEngine on
    SSLCertificateFile /etc/ssl/certs/site1.com-fullchain.crt
    SSLCertificateKeyFile /etc/ssl/private/site1.com.key
    DocumentRoot /var/www/site1
</VirtualHost>

# Site 2
<VirtualHost *:443>
    ServerName www.site2.com
    SSLEngine on
    SSLCertificateFile /etc/ssl/certs/site2.com-fullchain.crt
    SSLCertificateKeyFile /etc/ssl/private/site2.com.key
    DocumentRoot /var/www/site2
</VirtualHost>

SNI Requirements:

  • Apache 2.2.12+ with OpenSSL 0.9.8j+ (Apache 2.4+ preferred)
  • All modern browsers support SNI
  • Very old clients (IE on Windows XP, early Android) lack SNI and will see the default cert - usually acceptable in 2026
  • On older distros, ensure NameVirtualHost *:443 is correct; mis-ordering vhosts can serve the wrong cert

Learn about SAN certificates →

9. Let's Encrypt with Certbot

The easiest way to get certificates on Apache:

Install Certbot:

Debian/Ubuntu:

sudo apt install certbot python3-certbot-apache

RHEL/CentOS:

sudo dnf install certbot python3-certbot-apache

Get Certificate and Auto-Configure:

Backup first: If you have existing hand-crafted SSL vhosts, back up the config files before letting Certbot auto-modify them so you can revert if needed.

sudo certbot --apache -d www.example.com -d example.com

Certbot will:

  1. Obtain the certificate
  2. Modify your Apache config to use it
  3. Set up auto-renewal

Manual Renewal Test:

sudo certbot renew --dry-run

Verify Auto-Renewal:

systemctl list-timers | grep certbot

Learn about ACME protocol → | Let's Encrypt troubleshooting →

10. File Permissions

Wrong permissions = Apache won't start, or worse, security vulnerability.

Required Permissions:

FileOwnerPermissionsNotes
Private keyroot:root600Only root can read
Certificateroot:root644World-readable is fine
Chain fileroot:root644World-readable is fine
Config filesroot:root644World-readable is fine

Fix Permissions:

sudo chown root:root /etc/ssl/private/example.com.key
sudo chmod 600 /etc/ssl/private/example.com.key

sudo chown root:root /etc/ssl/certs/example.com-fullchain.crt
sudo chmod 644 /etc/ssl/certs/example.com-fullchain.crt

Never make private keys group- or world-readable (640/644) just to "fix" Apache startup errors. Fix ownership and paths instead.

Common Error:

AH02572: Failed to configure at least one certificate and key

Often caused by: wrong permissions, wrong file path, or key/cert mismatch.

11. Troubleshooting

Problem: Apache Won't Start After SSL Config

# Check syntax
apachectl configtest

# Check error log
tail -50 /var/log/apache2/error.log  # Debian/Ubuntu
tail -50 /var/log/httpd/error_log    # RHEL/CentOS

Problem: Certificate/Key Mismatch

# Compare modulus of cert and key - they must match
openssl x509 -noout -modulus -in cert.crt | openssl md5
openssl rsa -noout -modulus -in key.key | openssl md5

Problem: Chain Not Sent

openssl s_client -connect example.com:443 -servername example.com
# Look for "Certificate chain" - should show multiple certs

Problem: SSL Labs Grade Below A

Common causes:

  • TLS 1.0/1.1 still enabled
  • Weak ciphers in list
  • No HSTS
  • Missing OCSP stapling

Use ssllabs.com/ssltest and address each issue. Re-run after each change - results may be cached for a few minutes, so wait or use "Clear cache" when available.

Check your SSL configuration with our tool →

12. Apache SSL Checklist

Before going live:

  • apachectl configtest returns "Syntax OK"
  • Private key permissions are 600
  • Certificate chain is complete (test with openssl s_client)
  • HTTP redirects to HTTPS
  • TLS 1.0 and 1.1 are disabled
  • HSTS header is set (if ready to commit)
  • OCSP stapling is enabled
  • SSL Labs grade is A or A+
  • Auto-renewal is configured (if using Let's Encrypt)
  • Certificate expiry monitoring is in place

13. Frequently Asked Questions

14. Key Takeaways

1

Use Apache 2.4+

2.2 is EOL and missing modern TLS features

2

Concatenate chains

In 2.4.8+, put cert + chain in one file

3

Test before reload

apachectl configtest saves headaches

4

Harden by default

Use Mozilla's config generator

5

Let's Encrypt + Certbot

Free, automated, no excuses

6

Permissions matter

600 on private keys, always