Apache SSL/TLS Configuration: Complete Guide
Apache HTTPD uses the mod_ssl module for HTTPS. This page covers the core directives — SSLEngine, SSLProtocol, SSLCipherSuite — and builds a complete, secure HTTPS environment.
Install and Enable mod_ssl
# Ubuntu/Debian
sudo apt install libapache2-mod-ssl
sudo a2enmod ssl
sudo a2enmod headers # for security headers
sudo a2enmod rewrite # for HTTP→HTTPS redirect
sudo systemctl restart apache2
# CentOS/RHEL (usually pre-installed)
sudo dnf install mod_ssl
sudo systemctl restart httpd
# Verify activation
apache2ctl -M | grep ssl
# ssl_module (shared)
Basic HTTPS VirtualHost
# /etc/apache2/sites-available/example.com-ssl.conf
<VirtualHost *:443>
ServerName example.com
ServerAlias www.example.com
DocumentRoot /var/www/html
DirectoryIndex index.html index.php
# Enable SSL
SSLEngine On
# Certificate files
SSLCertificateFile /etc/letsencrypt/live/example.com/cert.pem
SSLCertificateKeyFile /etc/letsencrypt/live/example.com/privkey.pem
SSLCertificateChainFile /etc/letsencrypt/live/example.com/chain.pem
<Directory /var/www/html>
AllowOverride All
Require all granted
</Directory>
CustomLog /var/log/apache2/example.com-ssl-access.log combined
ErrorLog /var/log/apache2/example.com-ssl-error.log
</VirtualHost>
# Enable site
sudo a2ensite example.com-ssl.conf
sudo systemctl reload apache2
Global TLS Version and Cipher Suite
Global SSL settings are managed in ssl.conf or apache2.conf.
# /etc/apache2/mods-available/ssl.conf or conf-available/ssl-security.conf
# TLS versions: allow 1.2 and 1.3 only
SSLProtocol all -SSLv3 -TLSv1 -TLSv1.1
# Cipher suites (TLS 1.2)
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
# TLS 1.3 cipher suites
SSLCipherSuite TLSv1.3 TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256
# Server cipher order (off = client preference; recommended for TLS 1.3)
SSLHonorCipherOrder off
# ECDH curves
SSLOpenSSLConfCmd Curves X25519:prime256v1:secp384r1
# Session cache
SSLSessionCache shmcb:/run/apache2/ssl_scache(512000)
SSLSessionCacheTimeout 300
# OCSP Stapling
SSLUseStapling On
SSLStaplingCache shmcb:/run/apache2/ssl_stapling(32768)
SSLStaplingReturnResponderErrors off
HTTP → HTTPS Redirect
# /etc/apache2/sites-available/example.com.conf
<VirtualHost *:80>
ServerName example.com
ServerAlias www.example.com
# Exclude Let's Encrypt challenge path
Alias /.well-known/acme-challenge/ /var/www/html/.well-known/acme-challenge/
<Directory /var/www/html/.well-known/acme-challenge/>
Require all granted
</Directory>
# Redirect everything else to HTTPS
RewriteEngine On
RewriteCond %{REQUEST_URI} !^/.well-known/acme-challenge/
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
</VirtualHost>
Or more simply:
<VirtualHost *:80>
ServerName example.com
Redirect permanent / https://example.com/
</VirtualHost>
HSTS and Security Headers
<VirtualHost *:443>
ServerName example.com
SSLEngine On
# ... certificate config ...
# Security headers (requires mod_headers)
Header always set Strict-Transport-Security "max-age=63072000; includeSubDomains; preload"
Header always set X-Frame-Options "DENY"
Header always set X-Content-Type-Options "nosniff"
Header always set X-XSS-Protection "1; mode=block"
Header always set Referrer-Policy "strict-origin-when-cross-origin"
Header always set Permissions-Policy "geolocation=(), microphone=(), camera=()"
# Hide server info
ServerTokens Prod
ServerSignature Off
</VirtualHost>
Complete Production Apache HTTPS Configuration
# /etc/apache2/sites-available/production-ssl.conf
<VirtualHost *:443>
ServerName example.com
ServerAlias www.example.com
DocumentRoot /var/www/html
# SSL settings
SSLEngine On
SSLCertificateFile /etc/letsencrypt/live/example.com/cert.pem
SSLCertificateKeyFile /etc/letsencrypt/live/example.com/privkey.pem
SSLCertificateChainFile /etc/letsencrypt/live/example.com/chain.pem
# TLS version
SSLProtocol all -SSLv3 -TLSv1 -TLSv1.1
# 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
SSLHonorCipherOrder off
# OCSP Stapling
SSLUseStapling On
# Security headers
Header always set Strict-Transport-Security "max-age=63072000; includeSubDomains; preload"
Header always set X-Frame-Options "SAMEORIGIN"
Header always set X-Content-Type-Options "nosniff"
Header always set Referrer-Policy "strict-origin-when-cross-origin"
Header always set Content-Security-Policy "default-src 'self'; script-src 'self'"
# Reverse proxy to Tomcat
ProxyRequests Off
ProxyPreserveHost On
ProxyPass "/" "http://localhost:8080/"
ProxyPassReverse "/" "http://localhost:8080/"
# Forward HTTPS info to backend
RequestHeader set X-Forwarded-Proto "https"
RequestHeader set X-Forwarded-Port "443"
CustomLog /var/log/apache2/ssl-access.log combined
ErrorLog /var/log/apache2/ssl-error.log
</VirtualHost>
Validation
# Check config syntax
sudo apachectl configtest
# Apply changes
sudo systemctl reload apache2
# Verify SSL config
openssl s_client -connect example.com:443 -tls1_3 2>/dev/null | head -20
# Check Apache SSL status
sudo apachectl -D DUMP_RUN_CFG | grep SSL
# Check ports
sudo ss -tlnp | grep -E '80|443'
Multi-Domain HTTPS (SNI)
Run HTTPS for multiple domains on a single server.
# Domain A
<VirtualHost *:443>
ServerName example.com
SSLEngine On
SSLCertificateFile /etc/ssl/example.com/fullchain.pem
SSLCertificateKeyFile /etc/ssl/example.com/privkey.pem
DocumentRoot /var/www/example.com
</VirtualHost>
# Domain B
<VirtualHost *:443>
ServerName another.com
SSLEngine On
SSLCertificateFile /etc/ssl/another.com/fullchain.pem
SSLCertificateKeyFile /etc/ssl/another.com/privkey.pem
DocumentRoot /var/www/another.com
</VirtualHost>
SNI (Server Name Indication) allows multiple certificates on a single port 443. All modern browsers support SNI.
The next page covers HTTPS offloading (SSL Termination) — terminating TLS at the load balancer and forwarding plain HTTP to the backend.