08 July 2025

Keeping Web Server SSL Settings Secure

Topics: Security, Web Servers
Tags: apache, nginx, kali
As Ciphers and Curves are compromised and the SSL/TLS protocols themselves evolve, regular reviews of your configurations are essential for keeping your web assets secure.

Recently I was updating my certificate issuance automation (which uses certbot to get certificates from Let’s Encrypt), and decided it was time to review my SSL web server settings. SSL Labs was rating my sites ‘A’, so nothing was urgent.

Kali Linux includes several tools for checking SSL, I decided to see what they turned up.

  • sslyze
  • sslscan
  • openssl

openssl

This utility is likely to already be installed on any Linux distribution. It can easily be used to read an SSL certificate from a remote host: openssl s_client -connect parkpin.me:443. It can also read a locally installed certificate: openssl x509 -text -noout -in /etc/ssl/mycerts/parkpin/cert.pem. It has options for forcing protocol versions and ciphers. I use this utility in my personal toolkit wrapped in scripts to parse the most important fields from certificates, as the raw output is several screenfuls.

sslyze

Is Python-based and very informative. It will tell you which protocols, ciphers and curves are in use, and also check compliance with the ssl-config.mozilla.org recommendations. It complained about two minor issues: a negative serial number for a certificate, and an older curve in my certificate chain.

sslscan

Provides less information than sslyze and does not check compliance with any recommendation sets. Compared to openssl it offers an easier cli syntax for simple queries and more concise output.

Fixing The Issues

The sslyze warning about the serial number comes on STDERR while everything else is on STDOUT. The complaint is originating within a Python library sslyze is using, the sslyze developers don’t consider it worth repeating in the main output. The library may be interpreting certain leading bytes as a sign. The specification calls for an integer, without excluding negative integers. The openssl s_client utility shows an integer without a sign. My conclusion is that this is not an issue — if it exists at all and isn’t a false positive.

There is an option to request a stronger curve in /etc/letsencrypt/cli.ini, and that clears up the curve issue when I reissue the certificate.

key-type = ecdsa
elliptic-curve = secp384r1

moz://a SSL Configuration Generator

This tool will generate an example configuration for many popular servers with three profiles: Modern, Intermediate, Old. Intermediate still allows TLS 1.2, Modern does not. At this time I see no reason to support TLS 1.2 and older protocols on general websites.

Apache config suggestion

# this configuration requires mod_ssl, mod_rewrite, mod_headers, and mod_socache_shmcb
<VirtualHost *:80>
    RewriteEngine On
    RewriteCond %{REQUEST_URI} !^/.well-known/acme-challenge/
    RewriteRule ^.*$ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,QSA,L]
</VirtualHost>

<VirtualHost *:443>
    SSLEngine on
    SSLCertificateFile      /path/to/signed_cert_and_intermediate_certs
    SSLCertificateKeyFile   /path/to/private_key
    # enable HTTP/2, if available
    Protocols h2 http/1.1
    # HTTP Strict Transport Security (mod_headers is required) (63072000 seconds)
    Header always set Strict-Transport-Security "max-age=63072000"
</VirtualHost>

# modern configuration
SSLProtocol             -all +TLSv1.3
SSLOpenSSLConfCmd       Curves X25519:prime256v1:secp384r1
SSLHonorCipherOrder     off
SSLSessionTickets       off

SSLUseStapling On
SSLStaplingCache "shmcb:logs/ssl_stapling(32768)"

When applying the recommendation, pay attention to the VirtualHost sections and code outside them. The code outside should be added to the global server configuration. On Debian/Ubuntu, create /etc/apache2/conf-available/sslserveroptions.conf and enable it for the global configuration. On RedHat and others that use httpd.conf, add the server-wide config there. Ignore the port 80 VirtualHost if you’re still allowing insecure HTTP traffic or have other logic for redirecting HTTP requests.

nginx config suggestion

http {

    server {
        listen 443 ssl;
        listen [::]:443 ssl;
        http2 on;
        ssl_certificate /path/to/signed_cert_plus_intermediates;
        ssl_certificate_key /path/to/private_key;

        # HSTS (ngx_http_headers_module is required) (63072000 seconds)
        add_header Strict-Transport-Security "max-age=63072000" always;
    }

    # modern configuration
    ssl_protocols TLSv1.3;
    ssl_ecdh_curve X25519:prime256v1:secp384r1;
    ssl_prefer_server_ciphers off;

    # OCSP stapling
    ssl_stapling on;
    ssl_stapling_verify on;
    # verify chain of trust of OCSP response using Root CA and Intermediate certs
    ssl_trusted_certificate /path/to/root_CA_cert_plus_intermediates;

    # replace with the IP address of your resolver;
    # async 'resolver' is important for proper operation of OCSP stapling
    resolver 127.0.0.1;

    server {
        listen 80 default_server;
        listen [::]:80 default_server;
        return 301 https://$host$request_uri;
    }
}

In nginx, the ‘server {‘ blocks are equivalent to Apache’s VirtualHost. HSTS and other ssl directives need to be in the ‘server { ssl’ blocks for each of your sites and the other directives need to be in the main body of your config.

Stapling and HSTS

OCSP stapling allows the server to include a signed response from a CA that the certificate isn’t revoked, but depending on the CA, these responses can be up to a week old. Stapling reduces CRL checks, saving browsers time and CA resources, at the cost of slowing propagation of revocations.

HSTS tells the browser the site is HTTPS only, the maxage directives mean that once a browser sees it on a site it will remember that forever and always require ssl for that site.