Configuring the TLS cipher policy in Apache limits which encryption suites clients can negotiate, which helps remove weak legacy choices, align HTTPS endpoints with current scanner baselines, and make certificate and proxy rollouts more predictable.
HTTPS in Apache is handled by mod_ssl on top of OpenSSL. On current Apache HTTP Server 2.4 builds, SSLCipherSuite can set the cipher list for TLS 1.2 and earlier and, when Apache is linked against OpenSSL 1.1.1 or later, a separate SSLCipherSuite TLSv1.3 … line can set the TLS 1.3 suites directly.
Examples below use the Debian and Ubuntu layout with /etc/apache2/, the apache2 systemd unit, and the apache2ctl wrapper. RHEL-family systems usually use /etc/httpd/ plus apachectl or httpd instead. Restricting protocols or ciphers can block older clients and upstream devices, and the negotiated suite still depends on the server certificate type, so syntax tests plus live TLS handshakes are part of the change.
Related: How to test Apache configuration
Related: How to enable or disable Apache modules
Steps to configure TLS ciphers in Apache:
- Show the active HTTPS virtual host file before editing it.
$ sudo apache2ctl -S VirtualHost configuration: *:443 host.example.net (/etc/apache2/sites-enabled/host.example.net.conf:1) ServerRoot: "/etc/apache2" Main DocumentRoot: "/var/www/html" Main ErrorLog: "/var/log/apache2/error.log" Mutex ssl-stapling-refresh: using_defaults Mutex ssl-stapling: using_defaults Mutex ssl-cache: using_defaults Mutex default: dir="/var/run/apache2/" mechanism=default PidFile: "/var/run/apache2/apache2.pid" ##### snipped #####
Use sudo apachectl -S or sudo httpd -S on platforms that do not ship apache2ctl.
- List the TLS 1.2 cipher names supported by the local OpenSSL build so the configured list matches real library names.
$ openssl ciphers -s -v -tls1_2 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256' ECDHE-ECDSA-AES256-GCM-SHA384 TLSv1.2 Kx=ECDH Au=ECDSA Enc=AESGCM(256) Mac=AEAD ECDHE-RSA-AES256-GCM-SHA384 TLSv1.2 Kx=ECDH Au=RSA Enc=AESGCM(256) Mac=AEAD ECDHE-ECDSA-CHACHA20-POLY1305 TLSv1.2 Kx=ECDH Au=ECDSA Enc=CHACHA20/POLY1305(256) Mac=AEAD ECDHE-RSA-CHACHA20-POLY1305 TLSv1.2 Kx=ECDH Au=RSA Enc=CHACHA20/POLY1305(256) Mac=AEAD ECDHE-ECDSA-AES128-GCM-SHA256 TLSv1.2 Kx=ECDH Au=ECDSA Enc=AESGCM(128) Mac=AEAD ECDHE-RSA-AES128-GCM-SHA256 TLSv1.2 Kx=ECDH Au=RSA Enc=AESGCM(128) Mac=AEAD
The -tls1_2 filter keeps this check aligned with the SSLCipherSuite SSL … line used later. TLS 1.3 uses separate suite names.
The negotiated TLS 1.2 suite must match the server certificate type. A host using an RSA certificate will negotiate the RSA entries from the list, not the ECDSA entries.
- Open the active HTTPS virtual host file.
$ sudo vi /etc/apache2/sites-available/host.example.net.conf
Global policy can live in /etc/apache2/mods-available/ssl.conf or the equivalent httpd SSL config when the same cipher rules must apply to every HTTPS virtual host.
- Set the protocol and cipher policy inside the `<VirtualHost *:443>` block.
<IfModule mod_ssl.c> <VirtualHost *:443> ServerName host.example.net SSLEngine On SSLProtocol -all +TLSv1.2 +TLSv1.3 SSLHonorCipherOrder On SSLCipherSuite SSL ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256 SSLCipherSuite TLSv1.3 TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256 SSLCertificateFile /etc/apache2/ssl/apache.crt SSLCertificateKeyFile /etc/apache2/ssl/apache.key </VirtualHost> </IfModule>Removing TLS 1.0 or TLS 1.1 support and narrowing the cipher list can break older browsers, older Java runtimes, load balancers, or API clients.
If apache2ctl configtest reports an unknown protocol or rejects the TLS 1.3 line, remove +TLSv1.3 and the SSLCipherSuite TLSv1.3 … directive and keep the TLS 1.2 policy only.
Current Apache 2.4 builds can configure TLS 1.3 suites directly with SSLCipherSuite TLSv1.3 …. Older examples often use SSLOpenSSLConfCmd Ciphersuites instead.
An “Invalid command 'SSLCipherSuite'” error usually means mod_ssl is not loaded.
- Validate the updated Apache configuration.
$ sudo apache2ctl configtest Syntax OK
sudo apache2ctl -t performs the same syntax check. Use sudo apachectl -t or sudo httpd -t on other platforms.
Related: How to test Apache configuration
- Reload Apache so new connections use the updated cipher policy.
$ sudo systemctl reload apache2
When systemd is not managing the service, use sudo apache2ctl graceful or the platform-equivalent reload command.
- Confirm a TLS 1.2 handshake negotiates one of the configured TLS 1.2 suites.
$ echo | openssl s_client -connect host.example.net:443 -servername host.example.net -tls1_2 2>/dev/null | sed -n '/New, /p;/Protocol :/p;/Cipher :/p' New, TLSv1.2, Cipher is ECDHE-RSA-AES256-GCM-SHA384 Protocol : TLSv1.2 Cipher : ECDHE-RSA-AES256-GCM-SHA384With an RSA certificate, seeing an ECDHE-RSA-… suite here is expected even when ECDSA suites appear earlier in the configured list.
- Confirm a TLS 1.3 handshake negotiates one of the configured TLS 1.3 suites.
$ echo | openssl s_client -connect host.example.net:443 -servername host.example.net -tls1_3 2>/dev/null | sed -n '/New, /p;/Protocol :/p;/Cipher :/p' New, TLSv1.3, Cipher is TLS_AES_256_GCM_SHA384 Protocol : TLSv1.3 Cipher : TLS_AES_256_GCM_SHA384Available TLS 1.3 suites are limited to the standard names exposed by the linked OpenSSL library.
- Scan the public endpoint from another host to confirm the offered suites and server-side order.
$ nmap --script ssl-enum-ciphers -p 443 host.example.net Starting Nmap 7.94SVN ( https://nmap.org ) at 2026-04-08 04:34 UTC Nmap scan report for host.example.net (192.0.2.40) Host is up (0.00017s latency). PORT STATE SERVICE 443/tcp open https | ssl-enum-ciphers: | TLSv1.2: | ciphers: | TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (ecdh_x25519) - A | TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 (ecdh_x25519) - A | TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (ecdh_x25519) - A | compressors: | NULL | cipher preference: server | TLSv1.3: | ciphers: | TLS_AKE_WITH_AES_256_GCM_SHA384 (ecdh_x25519) - A | TLS_AKE_WITH_CHACHA20_POLY1305_SHA256 (ecdh_x25519) - A | TLS_AKE_WITH_AES_128_GCM_SHA256 (ecdh_x25519) - A | cipher preference: server |_ least strength: A
The cipher preference: server line confirms the server is choosing the preferred suite order for the negotiated set.
nmap uses TLS_AKE_WITH_* labels for TLS 1.3 suites, which correspond to the configured TLS_AES_* and TLS_CHACHA20_* names.
Mohd Shakir Zakaria is a cloud architect with deep roots in software development and open-source advocacy. Certified in AWS, Red Hat, VMware, ITIL, and Linux, he specializes in designing and managing robust cloud and on-premises infrastructures.
