This guide walks you through installing an SSL certificate on Apache HTTP Server using mod_ssl — from uploading files to hardened TLS settings and troubleshooting common errors. Works with certificates from GetHTTPS, Certbot, or any CA.
If you don’t have a certificate yet, get one for free in 5 minutes.
Prerequisites
- Apache 2.4+ installed and serving your site on HTTP (port 80)
- Root/sudo access to the server
- Certificate files — Apache needs three separate files:
cert.pem— your certificate (end-entity only)privkey.pem— your private keychain.pem— intermediate CA certificate chain
Why three files? Unlike Nginx (which uses a single
fullchain.pem), Apache traditionally expects the certificate and chain as separate files. Apache 2.4.8+ can usefullchain.peminSSLCertificateFileand skipSSLCertificateChainFile. Format details →
Step 1: Upload certificate files
# Create a secure directory
sudo mkdir -p /etc/ssl/gethttps
# Copy files to the server
sudo cp cert.pem chain.pem privkey.pem /etc/ssl/gethttps/
# Lock down the private key
sudo chmod 600 /etc/ssl/gethttps/privkey.pem
sudo chmod 644 /etc/ssl/gethttps/cert.pem /etc/ssl/gethttps/chain.pem
sudo chown root:root /etc/ssl/gethttps/*
If transferring via SCP:
scp cert.pem chain.pem privkey.pem user@your-server:/tmp/
# On the server:
sudo mv /tmp/{cert,chain,privkey}.pem /etc/ssl/gethttps/
Step 2: Enable required Apache modules
# Debian/Ubuntu
sudo a2enmod ssl # Core SSL module
sudo a2enmod headers # For HSTS and security headers
sudo a2enmod rewrite # For HTTP→HTTPS redirect (if using .htaccess)
sudo systemctl restart apache2
# CentOS/RHEL/Amazon Linux
sudo yum install mod_ssl # Usually installs and enables in one step
sudo systemctl restart httpd
Verify mod_ssl is loaded:
apachectl -M | grep ssl
# Expected: ssl_module (shared)
Step 3: Configure the HTTPS VirtualHost
Create or edit your site’s SSL config. On Debian/Ubuntu, this is typically /etc/apache2/sites-available/yourdomain-ssl.conf:
# ─── HTTPS server ────────────────────────────
<VirtualHost *:443>
ServerName example.com
ServerAlias www.example.com
# Certificate files
SSLEngine on
SSLCertificateFile /etc/ssl/gethttps/cert.pem
SSLCertificateKeyFile /etc/ssl/gethttps/privkey.pem
SSLCertificateChainFile /etc/ssl/gethttps/chain.pem
# TLS protocol — only 1.2 and 1.3
SSLProtocol all -SSLv3 -TLSv1 -TLSv1.1
SSLHonorCipherOrder off
SSLCompression off
# OCSP stapling (faster cert verification for clients)
SSLUseStapling on
SSLStaplingCache "shmcb:/var/run/apache2/ssl_stapling(128000)"
# Security headers
Header always set Strict-Transport-Security "max-age=63072000; includeSubDomains"
Header always set X-Content-Type-Options "nosniff"
Header always set X-Frame-Options "SAMEORIGIN"
# Your site
DocumentRoot /var/www/html
<Directory /var/www/html>
AllowOverride All
</Directory>
</VirtualHost>
# ─── HTTP redirect ───────────────────────────
<VirtualHost *:80>
ServerName example.com
ServerAlias www.example.com
Redirect permanent / https://example.com/
</VirtualHost>
What each directive does:
| Directive | Purpose |
|---|---|
SSLEngine on | Enables SSL/TLS for this VirtualHost |
SSLCertificateFile | Path to your certificate |
SSLCertificateKeyFile | Path to your private key |
SSLCertificateChainFile | Path to intermediate CA certificate |
SSLProtocol | Only TLS 1.2/1.3 — older versions deprecated |
SSLHonorCipherOrder off | Let the client pick the best cipher |
SSLCompression off | Prevents CRIME attack |
SSLUseStapling | Server pre-fetches OCSP response |
Strict-Transport-Security | HSTS — browsers remember to always use HTTPS |
Redirect permanent | 301 redirect HTTP → HTTPS |
Step 4: Enable the site and test
# Debian/Ubuntu
sudo a2ensite yourdomain-ssl.conf
sudo apachectl configtest # Must say "Syntax OK"
sudo systemctl reload apache2
# CentOS/RHEL
sudo apachectl configtest
sudo systemctl reload httpd
If configtest reports an error, it tells you the exact line — fix it before reloading.
Step 5: Verify
Browser: Visit https://yourdomain.com. Click the padlock → verify “Let’s Encrypt” as issuer.
Command line:
echo | openssl s_client -connect yourdomain.com:443 -servername yourdomain.com 2>/dev/null \
| openssl x509 -noout -subject -issuer -dates
Online: Use SSL Labs Server Test for a comprehensive grade. Target: A or A+.
Apache vs Nginx: certificate file differences
| Apache | Nginx | |
|---|---|---|
| Certificate | SSLCertificateFile cert.pem | ssl_certificate fullchain.pem |
| Private key | SSLCertificateKeyFile privkey.pem | ssl_certificate_key privkey.pem |
| Chain | SSLCertificateChainFile chain.pem | Included in fullchain.pem |
| Files needed | 3 separate files | 2 files (chain combined) |
Apache 2.4.8+ can also use the combined fullchain.pem in SSLCertificateFile — then you can omit SSLCertificateChainFile.
Renewal
When your certificate approaches expiry (check here):
# Replace files
sudo cp new-cert.pem /etc/ssl/gethttps/cert.pem
sudo cp new-chain.pem /etc/ssl/gethttps/chain.pem
sudo cp new-privkey.pem /etc/ssl/gethttps/privkey.pem
# Reload (not restart) — zero downtime
sudo systemctl reload apache2 # Debian/Ubuntu
sudo systemctl reload httpd # CentOS/RHEL
Full renewal guide → | Automate with Certbot →
Troubleshooting
”AH02572: Failed to configure certificate” / key mismatch
The certificate and private key don’t correspond. Verify they match:
# These two hashes must be identical
openssl x509 -noout -modulus -in /etc/ssl/gethttps/cert.pem | md5sum
openssl rsa -noout -modulus -in /etc/ssl/gethttps/privkey.pem | md5sum
If they differ, you’re using files from different GetHTTPS sessions. Re-download both from the same session.
”AH01909: server certificate does NOT include an ID”
Your ServerName doesn’t match any domain in the certificate’s SAN field:
openssl x509 -noout -text -in /etc/ssl/gethttps/cert.pem | grep -A1 "Subject Alternative Name"
Make sure your Apache ServerName matches one of the listed DNS names exactly.
Browser shows “certificate not trusted”
Missing the intermediate chain. Verify SSLCertificateChainFile points to chain.pem (the intermediate), not cert.pem. Test the chain:
echo | openssl s_client -connect yourdomain.com:443 2>/dev/null | grep "Verify return code"
# "Verify return code: 0 (ok)" = good
# "Verify return code: 21 (unable to verify)" = chain incomplete
“AH00526: Syntax error on line X”
Apache config syntax error. Common causes:
- Missing closing
</VirtualHost>tag SSLCertificateChainFilemisspelled- Missing quotes around header values
- File path doesn’t exist
HTTPS works but shows “Not Secure”
Mixed content — your page loads resources over HTTP. Open DevTools → Console → fix the http:// URLs.
Frequently asked questions
Can I use fullchain.pem instead of separate cert + chain?
Yes, on Apache 2.4.8+. Use SSLCertificateFile /path/to/fullchain.pem and remove the SSLCertificateChainFile directive entirely. On older Apache, you need separate files.
How do I check which Apache version I have?
apachectl -v
# or
httpd -v
Do I need to restart Apache or just reload?
Reload (systemctl reload) is sufficient and causes zero downtime. Restart (systemctl restart) drops all active connections. Always reload for certificate changes.
Can Apache serve different certificates for different domains?
Yes. Create separate <VirtualHost *:443> blocks with different ServerName and SSLCertificate* directives. Apache uses SNI (Server Name Indication) to select the correct certificate.
Does Apache support ECDSA certificates?
Yes. Apache 2.4+ handles ECDSA the same way as RSA — same directives, same file format. GetHTTPS generates ECDSA P-256 by default.
Where are Apache’s SSL error logs?
# Debian/Ubuntu
tail -f /var/log/apache2/error.log
# CentOS/RHEL
tail -f /var/log/httpd/ssl_error_log