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 host.example.net (/etc/apache2/sites-enabled/000-default.conf:1)
             port 80 namevhost host.example.net (/etc/apache2/sites-enabled/000-default.conf:1)
             port 80 namevhost host.example.net (/etc/apache2/sites-enabled/host.example.net.conf:1)
    *:443                  host.example.net (/etc/apache2/sites-enabled/host.example.net.conf:6)
    ServerRoot: "/etc/apache2"
    Main DocumentRoot: "/var/www/html"
    Main ErrorLog: "/var/log/apache2/error.log"
    Mutex ssl-stapling: using_defaults
    Mutex ssl-cache: using_defaults
    Mutex default: dir="/var/run/apache2/" mechanism=default 
    ##### 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'
    TLS_AES_256_GCM_SHA384         TLSv1.3 Kx=any      Au=any   Enc=AESGCM(256)            Mac=AEAD
    TLS_CHACHA20_POLY1305_SHA256   TLSv1.3 Kx=any      Au=any   Enc=CHACHA20/POLY1305(256) Mac=AEAD
    TLS_AES_128_GCM_SHA256         TLSv1.3 Kx=any      Au=any   Enc=AESGCM(128)            Mac=AEAD
    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 host.example.net
    
        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/apache2/ssl/apache.crt
        SSLCertificateKeyFile   /etc/apache2/ssl/apache.key
    </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 host.example.net:443 -servername host.example.net -tls1_2
    CONNECTED(00000003)
    ##### snipped #####
    New, TLSv1.2, Cipher is ECDHE-RSA-AES256-GCM-SHA384
    ##### snipped #####
    SSL-Session:
        Protocol  : TLSv1.2
        Cipher    : ECDHE-RSA-AES256-GCM-SHA384
    ##### snipped #####
  7. Confirm a TLS 1.3 handshake negotiates a cipher suite from Ciphersuites.
    $ echo | openssl s_client -connect host.example.net:443 -servername host.example.net -tls1_3
    CONNECTED(00000003)
    ##### snipped #####
    New, TLSv1.3, Cipher is TLS_AES_256_GCM_SHA384
    ##### snipped #####
    Verify return code: 0 (ok)
    ##### snipped #####
  8. Enumerate offered ciphers from another host to verify ordering and protocol coverage.
    $ nmap --script ssl-enum-ciphers -p 443 host.example.net
    Starting Nmap 7.94SVN ( https://nmap.org ) at 2026-01-10 20:19 +08
    Nmap scan report for host.example.net (192.0.2.40)
    Host is up (0.000087s latency).
    rDNS record for 192.0.2.40: host
    
    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 SSLHonorCipherOrder On is being honored for TLS 1.2.