Controlling TLS cipher suites in Apache reduces exposure to weak cryptography, helps meet compliance baselines, and makes scanner results consistent across environments.

HTTPS in Apache is provided by mod_ssl, which uses OpenSSL to negotiate a protocol version and cipher suite during the TLS handshake. SSLCipherSuite controls the cipher list for TLS 1.2 and earlier, while TLS 1.3 cipher suites are configured separately through SSLOpenSSLConfCmd Ciphersuites when the OpenSSL build supports it.

Cipher hardening is safest when applied at the TLS virtual host level (inside `<VirtualHost *:443>`) and rolled out with syntax checks plus live negotiation tests. A too-strict policy can block legacy clients and upstream proxies, and a syntax error can prevent the apache2 service from reloading, so a rollback path and console access reduce the impact of accidental lockouts.

Steps to configure TLS ciphers in Apache:

  1. Show the active virtual host file that serves HTTPS on port 443.
    $ sudo apache2ctl -S
    VirtualHost configuration:
    *:80                   is a NameVirtualHost
             default server example.com (/etc/apache2/sites-enabled/000-default.conf:1)
    *:443                  is a NameVirtualHost
             default server example.com (/etc/apache2/sites-enabled/default-ssl.conf:2)
    ##### snipped #####

    On RHEL-family systems, use apachectl -S and expect paths under /etc/httpd/.

  2. List the cipher names supported by the local OpenSSL build to avoid typos in SSLCipherSuite.
    $ openssl ciphers -v 'ECDHE:!aNULL:!eNULL:!MD5:!3DES'
    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
    ##### snipped #####

    SSLCipherSuite uses OpenSSL cipher string syntax, including colon-separated lists and selection rules.

  3. Add the TLS protocol and cipher policy directives inside the `<VirtualHost *:443>` block for the HTTPS site.
    <IfModule mod_ssl.c>
    <VirtualHost *:443>
        ServerName example.com
    
        SSLEngine On
    
        SSLProtocol             -all +TLSv1.2 +TLSv1.3
        SSLHonorCipherOrder     On
    
        SSLCipherSuite          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
        SSLOpenSSLConfCmd       Ciphersuites TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256
    
        SSLCertificateFile      /etc/letsencrypt/live/example.com/fullchain.pem
        SSLCertificateKeyFile   /etc/letsencrypt/live/example.com/privkey.pem
    </VirtualHost>
    </IfModule>

    Disabling TLS 1.0/1.1 or removing older cipher families can break legacy clients, older JVMs, and some upstream devices.

    If apache2ctl -t reports an unknown protocol for TLSv1.3, remove +TLSv1.3 and keep only +TLSv1.2 for that host.

    SSLOpenSSLConfCmd Ciphersuites is ignored on builds without TLS 1.3 support, and SSLCipherSuite does not affect TLS 1.3.

    An “Invalid command 'SSLCipherSuite'” error usually indicates mod_ssl is not loaded.

    Global defaults can be set in /etc/apache2/mods-available/ssl.conf when consistent policy across sites is preferred.

  4. Validate the Apache configuration syntax.
    $ sudo apache2ctl -t
    Syntax OK

    No output other than Syntax OK indicates the configuration is parseable.

  5. Reload apache2 to apply the new cipher policy.
    $ sudo systemctl reload apache2

    Use a reload for config-only changes, since existing connections typically remain established.

  6. Confirm a TLS 1.2 handshake negotiates an allowed cipher suite.
    $ echo | openssl s_client -connect example.com:443 -servername example.com -tls1_2
    CONNECTED(00000003)
    ##### snipped #####
    SSL-Session:
        Protocol  : TLSv1.2
        Cipher    : ECDHE-RSA-AES128-GCM-SHA256
    ##### snipped #####
  7. Confirm a TLS 1.3 handshake negotiates a cipher suite from Ciphersuites.
    $ echo | openssl s_client -connect example.com:443 -servername example.com -tls1_3
    CONNECTED(00000003)
    ##### snipped #####
    SSL-Session:
        Protocol  : TLSv1.3
        Cipher    : TLS_AES_256_GCM_SHA384
    ##### snipped #####
  8. Enumerate offered ciphers from another host to verify ordering and protocol coverage.
    $ nmap --script ssl-enum-ciphers -p 443 example.com
    Starting Nmap 7.94 ( https://nmap.org ) at 2025-12-13 20:15 UTC
    Nmap scan report for example.com (203.0.113.10)
    PORT    STATE SERVICE
    443/tcp open  https
    | ssl-enum-ciphers:
    |   TLSv1.2:
    |     ciphers:
    |       TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (ecdh_x25519) - A
    |       TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (ecdh_x25519) - A
    |     cipher preference: server
    |   TLSv1.3:
    |     ciphers:
    |       TLS_AES_256_GCM_SHA384 (ecdh_x25519) - A
    |       TLS_CHACHA20_POLY1305_SHA256 (ecdh_x25519) - A
    |       TLS_AES_128_GCM_SHA256 (ecdh_x25519) - A
    |_  least strength: A

    The cipher preference: server line confirms SSLHonorCipherOrder On is being honored for TLS 1.2.

Discuss the article:

Comment anonymously. Login not required.