Install a TLS certificate on Apache httpd
On Apache 2.4.8 and later, set `SSLCertificateFile` to a file containing your server certificate followed by its intermediates, and `SSLCertificateKeyFile` to the private key. The old `SSLCertificateChainFile` directive is deprecated and logs a warning — Apache reads the chain from the certificate file now, the same way nginx does. On Apache 2.4.7 and earlier the split directives are still required, which is why so much conflicting documentation exists for this.
Which configuration applies to you #
apache2 -v || httpd -v Expected output
Server version: Apache/2.4.58 (Ubuntu)
| Version | Chain goes in |
|---|---|
| 2.4.8 and later | SSLCertificateFile, concatenated after the leaf |
| 2.4.7 and earlier | A separate SSLCertificateChainFile |
Step 1 — build the chain file #
cat certificate.crt intermediate.crt > /etc/ssl/certs/fullchain.crt Step 2 — the virtual host #
<VirtualHost *:443>
ServerName example.com
ServerAlias www.example.com
DocumentRoot /var/www/example.com
SSLEngine on
SSLCertificateFile /etc/ssl/certs/fullchain.crt
SSLCertificateKeyFile /etc/ssl/private/private.key
SSLProtocol -all +TLSv1.2 +TLSv1.3
SSLHonorCipherOrder off
SSLSessionTickets off
SSLUseStapling on
Header always set Strict-Transport-Security "max-age=63072000"
</VirtualHost>
# Stapling cache belongs at server scope, not inside a VirtualHost.
SSLStaplingCache shmcb:/var/run/ocsp(128000)
<VirtualHost *:80>
ServerName example.com
ServerAlias www.example.com
Redirect permanent / https://example.com/
</VirtualHost> Step 3 — enable the modules #
a2enmod ssl headers
a2ensite example.com-ssl Step 4 — permissions #
chmod 600 /etc/ssl/private/private.key
chown root:root /etc/ssl/private/private.key Step 5 — test and reload #
apachectl configtest Expected output
Syntax OK
systemctl reload apache2 Step 6 — verify from outside #
openssl s_client -connect example.com:443 -servername example.com -showcerts </dev/null 2>/dev/null \
| grep -c "BEGIN CERTIFICATE" Expected output
2
openssl s_client -connect example.com:443 -servername example.com </dev/null 2>/dev/null \
| grep "Verify return code" Expected output
Verify return code: 0 (ok)
Which virtual host is actually serving? #
The most useful Apache debugging command there is:
apachectl -S Expected output
VirtualHost configuration: *:443 example.com (/etc/apache2/sites-enabled/example.com-ssl.conf:1) *:80 example.com (/etc/apache2/sites-enabled/example.com.conf:1)
If a hostname is not listed, requests for it fall through to the first virtual host on that port — which is why the wrong certificate gets served.
Common Apache TLS failures #
| Symptom | Cause | Fix |
|---|---|---|
Unable to configure RSA server private key |
Key does not match the certificate | Check the pair |
SSLCertificateChainFile is deprecated |
Using the old directive on 2.4.8+ | Concatenate into SSLCertificateFile |
| Works in Chrome, fails in curl | Missing intermediate | Build the chain file |
| Apache hangs on start | Encrypted key waiting on a passphrase | Remove it |
| Wrong site’s certificate served | Virtual host mismatch | Check apachectl -S |
SSLStaplingCache not configured |
Directive inside a VirtualHost | Move it to server scope |
Starting from a PFX #
openssl pkcs12 -in certificate.pfx -nocerts -nodes -out /etc/ssl/private/private.key
openssl pkcs12 -in certificate.pfx -clcerts -nokeys -out /tmp/certificate.crt
openssl pkcs12 -in certificate.pfx -cacerts -nokeys -out /tmp/chain.crt
cat /tmp/certificate.crt /tmp/chain.crt > /etc/ssl/certs/fullchain.crt Frequently asked questions
- Should I still use SSLCertificateChainFile?
- Only on Apache 2.4.7 or earlier. From 2.4.8 the directive is deprecated and logs a warning on every start; the chain belongs in SSLCertificateFile instead.
- Where do I check my Apache version?
apache2 -von Debian and Ubuntu,httpd -von RHEL and Fedora. The distinction matters because the correct configuration differs either side of 2.4.8.- Why does Apache serve the wrong certificate?
- Usually because the request reached a different virtual host than you expected. Run
apachectl -Sto see how Apache has resolved the virtual hosts and which one is the default for port 443. - Can Apache use an encrypted private key?
- Yes, but it prompts at startup, which breaks unattended restarts.
SSLPassPhraseDialog exec:/path/to/scriptsupplies it programmatically if you must keep the passphrase.
References
- Apache mod_ssl — SSLCertificateFile and SSLCertificateChainFile