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.

Updated August 19, 2026 Verified against OpenSSL 3.5.7

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 #

/etc/apache2/sites-available/example.com-ssl.conf
<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 #

Complete chain?
openssl s_client -connect example.com:443 -servername example.com -showcerts </dev/null 2>/dev/null \
| grep -c "BEGIN CERTIFICATE"
Expected output
2
Validates?
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 -v on Debian and Ubuntu, httpd -v on 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 -S to 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/script supplies it programmatically if you must keep the passphrase.

References