Technology

Nginx and Apache TLS 1.3, HSTS and OCSP Setup

A slow first HTTPS request can have several causes, including network latency and TLS negotiation. Check the negotiated protocol and certificate chain before attributing the delay to TLS settings. OCSP stapling is relevant only where the certificate authority supports it; HSTS preload protects initial HTTP navigation rather than speeding up an HTTPS handshake.

This guide configures TLS 1.3 and modern TLS 1.2 on Nginx and Apache, explains forward-secrecy limits, and introduces HSTS with a short lifetime. Let’s Encrypt examples keep OCSP stapling off; separate fragments cover certificates from CAs that still support it. Preloading is an optional long-term decision, not a required deployment step. Adapt domain names, paths and modules to the existing application.

TLS 1.3, Cipher Suites and Forward Secrecy

TLS 1.3 reduces full-handshake round trips and removes legacy cipher suites. Its cipher suites select symmetric encryption and hash algorithms; key exchange is negotiated separately. Certificate-authenticated handshakes use ephemeral key exchange, but PSK-only resumption and 0-RTT data do not offer the same guarantees. TLS 1.3 cipher suites remain configurable: the Nginx example leaves library defaults, while Apache uses its documented TLSv1.3 selector.

Keep appropriate TLS 1.2 ECDHE/AEAD suites for supported clients and verify the certificate chain. Forward secrecy protects recorded sessions against later theft of the server’s long-term key when ephemeral secrets have been erased; it does not protect compromised active-session secrets. HSTS controls HTTPS use and must be deployed separately from these cryptographic settings.

TLS 1.3 does not repair an incomplete certificate chain. Missing intermediates can cause validation failures or extra fetching, depending on the client. Where OCSP is supported, responder access and validation matter; the effect of a missing staple depends on the client’s revocation policy.

Certificates, Chains and OCSP Applicability

Use the certificate file containing the leaf and required intermediate certificates plus its matching private key. Let’s Encrypt commonly provides fullchain.pem and privkey.pem. Modern Apache can load intermediates from SSLCertificateFile. The server-sent chain normally omits the root; an OCSP verification trust bundle may need that root and is a different file.

Verify which certificates the server actually sends. Some clients can fetch missing intermediates, while others fail; serve the required chain rather than relying on that fallback. A displayed leaf issuer or expiry date alone does not validate the entire chain.

Third, check whether OCSP applies to your certificate. Let’s Encrypt ended its OCSP service in August 2025, so its current certificates cannot use these stapling examples. For certificates from a CA that still supports OCSP, Nginx needs DNS resolution and network access to the responder. Configure a resolver reachable from the Nginx process; a reliable local resolver is also a valid choice.

Check clock synchronization because certificate and OCSP validity depend on time. Inspect the time service and its synchronization state when troubleshooting validity errors.

Nginx TLS 1.3 and HSTS Configuration

Version and Module Requirements

The modern http2 on syntax requires Nginx 1.25.1 or later with SSL and HTTP/2 modules; TLS 1.3 requires a compatible TLS library, at least OpenSSL 1.1.1 for that implementation. These are feature minimums, not recommendations to install old releases. Use maintained packages and verify their build options. Match configured curves to client and ECDSA certificate requirements. Session-cache size and timeout below are examples to size for the workload.

Example Nginx server block

Verify working HTTPS, certificate renewal and same-host HTTP-to-HTTPS redirects before enabling HSTS. This example begins at max-age=300 for the current host, without includeSubDomains or preload. Configure existing HTTP listeners separately; the HTTPS block is not a complete replacement for the application’s routing. Let’s Encrypt has retired OCSP, so the baseline explicitly disables stapling.

server {
    listen 443 ssl;
    http2 on;
    server_name example.com www.example.com;

    # Certificates (Let’s Encrypt example)
    ssl_certificate     /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

    # Protocols: prefer TLSv1.3, keep 1.2 for older clients
    ssl_protocols TLSv1.2 TLSv1.3;

    # TLS 1.2 cipher list; TLS 1.3 uses library defaults here
    ssl_ciphers 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;

    # Cipher preference policy; verify negotiated results with target clients
    ssl_prefer_server_ciphers off;

    # Modern curves for PFS
    ssl_ecdh_curve X25519:prime256v1:secp384r1;

    # Sessions
    ssl_session_cache shared:SSL:50m;
    ssl_session_timeout 1d;
    ssl_session_tickets off;

    # Let’s Encrypt: OCSP service retired in August 2025.
    ssl_stapling off;

    # HSTS: enable only after HTTPS is verified; short initial lifetime
    add_header Strict-Transport-Security "max-age=300" always;

    # Security niceties
    add_header X-Content-Type-Options nosniff always;
    add_header X-Frame-Options SAMEORIGIN always;
    add_header Referrer-Policy no-referrer-when-downgrade always;

    root /var/www/example.com/public;
    index index.html index.htm;

    location / {
        try_files $uri $uri/ =404;
    }
}

For a different CA certificate that still supports OCSP, use the fragment below in place of the existing ssl_stapling off; line inside its server block; do not leave both settings in that block. Replace the file with a verified PEM bundle containing the issuer, all other intermediates and the trusted root. Replace the documentation-only 192.0.2.53 address with a trusted resolver reachable from Nginx. Let’s Encrypt fullchain.pem omits the root and its current certificates do not need this fragment. The OCSP trust bundle is not sent to clients as the server chain.

# Only inside a server block using an OCSP-capable CA certificate.
ssl_stapling on;
ssl_stapling_verify on;
ssl_trusted_certificate /path/to/issuer-intermediates-root.pem;
resolver 192.0.2.53 valid=300s;
resolver_timeout 5s;

For related Nginx performance considerations, see the guide to TLS 1.3, supported OCSP stapling and Brotli. Verify each recommendation against the deployed version and certificate authority.

Validate Nginx TLS and Headers

Run nginx -t before reloading through the existing service workflow; it checks syntax and referenced files. The following command displays a staple only where the CA and certificate support OCSP. Keep the full error output available when diagnosing failures:

openssl s_client -connect example.com:443 -status -servername example.com < /dev/null | sed -n '/OCSP response:/,/^[[:space:]]*$/p'

For a certificate that supports OCSP, look for a successful response with certificate status “good” and check its validity times: This Update should not be in the future, and Next Update, when present, should not have passed. This command displays the response; it does not establish cryptographic validity of the staple. Check certificate matching and the response signature separately. No stapled response is expected for current Let’s Encrypt certificates. Then confirm HSTS is present if you have enabled it:

curl -I https://example.com | grep -i strict-transport-security

Use SSL Labs for an external view of protocol and chain configuration, together with representative client tests. A grade does not prove every application or client path works.

Apache TLS 1.3 and HSTS Configuration

Apache uses mod_ssl for TLS, mod_headers for response headers and mod_http2 for the HTTP/2 example. HTTP/2 support also depends on the libnghttp2 build and a compatible MPM/application combination. Prefork has significant concurrency limitations; do not switch MPMs without checking the application. The optional shmcb OCSP fragment additionally needs mod_socache_shmcb.

Apache VirtualHost Example

Use a maintained Apache 2.4 build with TLS 1.3 support and compatible OpenSSL; TLS 1.3 requires OpenSSL 1.1.1 or later. Verify modules and the existing HTTP listener. The example uses documented SSLCipherSuite TLSv1.3 syntax and enables short HSTS only after HTTPS has been verified.

<VirtualHost *:443>
    ServerName example.com
    ServerAlias www.example.com

    DocumentRoot /var/www/example.com/public

    Protocols h2 http/1.1
    SSLEngine on

    # Certificates (Let’s Encrypt example). Many modern builds include the chain in SSLCertificateFile.
    SSLCertificateFile /etc/letsencrypt/live/example.com/fullchain.pem
    SSLCertificateKeyFile /etc/letsencrypt/live/example.com/privkey.pem

    # Protocols
    SSLProtocol -all +TLSv1.2 +TLSv1.3

    # TLS 1.2 ciphers
    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

    SSLHonorCipherOrder off

    # TLS 1.3 suites (if supported by your Apache/OpenSSL)
    SSLCipherSuite TLSv1.3 TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256

    # Modern curves for PFS
    SSLOpenSSLConfCmd Curves X25519:prime256v1:secp384r1

    # Let’s Encrypt: OCSP service retired in August 2025.
    SSLUseStapling Off

    # HSTS: short initial lifetime after HTTPS verification
    Header always set Strict-Transport-Security "max-age=300"

    # Security niceties
    Header always set X-Content-Type-Options "nosniff"
    Header always set X-Frame-Options "SAMEORIGIN"
    Header always set Referrer-Policy "no-referrer-when-downgrade"

    <Directory /var/www/example.com/public>
        AllowOverride None
        Require all granted
    </Directory>
</VirtualHost>

For an OCSP-capable CA certificate, the separate fragment below enables caching and stapling. Place SSLStaplingCache in global server configuration, outside every VirtualHost. Load mod_socache_shmcb and replace the path with an existing runtime directory having appropriate Apache permissions. Add the remaining four directives only inside the applicable certificate’s VirtualHost. Do not apply this to current Let’s Encrypt certificates.

# Global server configuration, outside every VirtualHost.
# Requires loaded mod_socache_shmcb and an existing writable runtime path.
SSLStaplingCache shmcb:/path/to/apache-run/ocsp(128000)

# Add these lines only inside the OCSP-capable certificate VirtualHost.
SSLUseStapling On
SSLStaplingResponderTimeout 5
SSLStaplingReturnResponderErrors Off
SSLStaplingStandardCacheTimeout 3600

Validate Apache TLS and Headers

Run apachectl configtest before applying the configuration through the supported service workflow. Use the following OCSP display command only with a supporting CA, then inspect the HSTS response header:

openssl s_client -connect example.com:443 -status -servername example.com < /dev/null | sed -n '/OCSP response:/,/^[[:space:]]*$/p'
curl -I https://example.com | grep -i strict-transport-security

For OCSP-capable certificates, inspect Apache errors, cache availability, the issuer chain, responder access and clock state. Missing OCSP is expected with current Let’s Encrypt certificates and must not trigger a false failure alarm.

Staged HSTS and Optional Preloading

HSTS is a browser policy learned from valid HTTPS responses and retained for max-age. includeSubDomains extends its scope. Preload adds a browser-shipped policy for first visits; it is separate from ordinary HSTS. The current hstspreload.org guidance recommends HSTS but does not generally recommend preloading, whose incremental benefit is limited and operational commitment is long-lived.

Start with max-age=300 after HTTPS and same-host redirects work. Observe the full stage, then consider 604800 seconds (one week) and 2592000 (one month), monitoring for the full interval at each step. Add includeSubDomains only after all affected public and internal hosts support HTTPS. If you deliberately choose preload, its current header requirements include max-age of at least 31536000, includeSubDomains and preload; it also requires a valid certificate, same-host HTTPS redirect when port 80 is served, and HTTPS across all subdomains.

Check the current requirements and submission status at hstspreload.org. The base domain must serve the HSTS header over HTTPS, including an HTTPS redirect response where applicable; existing www and other subdomains must support HTTPS. The preload token alone does not submit the domain. New entries can take months to reach stable browsers.

Plan recovery before extending the policy. Sending max-age=0 over valid HTTPS removes learned HSTS for clients that receive it; absent clients retain their previous policy until expiry. Removing a preload token does not immediately remove browser-shipped state. Use the preload removal process if needed and account for browser-update delays.

OCSP for Certificates That Support It

For a supporting CA, an intermittent staple after reload can have several causes: responder DNS, network access, cache state, issuer data or clock validity. Inspect logs and test the actual dependency before changing resolver settings. Adding a resolver does not guarantee that every failure disappears.

Validate responder access, trust material and renewal behavior together:

Allow the actual CA OCSP endpoint and required DNS resolution through egress controls. For Nginx verification, include the issuer, intermediates and trusted root in the trust bundle. After renewal, check chain changes and apply the new certificate through the service workflow. Let’s Encrypt no longer supplies OCSP; do not configure its renewal checks to require a staple.

If OCSP is applicable, monitor beyond a single probe. A status display can detect a missing response but must not be described as full cryptographic validation. Check response matching, signature, certificate status and validity times, and choose a monitoring interval appropriate to the responder and certificate policy.

Forward Secrecy and Session Resumption

In TLS 1.2, ECDHE suites provide ephemeral key exchange. Certificate-authenticated TLS 1.3 uses ephemeral exchange, but PSK-only resumption and early data have different guarantees. Keep supported groups compatible with both clients and the certificate. Erasure and protection of session secrets remain necessary; listing curves is not a complete PFS guarantee.

Measure TLS cost separately from application, cache and network delays. Ephemeral key exchange uses CPU, while connection reuse and resumption affect repeated handshakes. Use Mozilla’s SSL configuration generator as a version-specific reference and verify the resulting settings against the actual build.

Session resumption and ticket handling depend on the TLS version and library. Disabling session tickets is not a universal guarantee that every form of resumption is disabled or that every resumed connection has forward secrecy. Review supported resumption modes, ticket-key lifetime and sharing scope before enabling them; do not assume TLS 1.3 automatically makes those decisions safe.

TLS Performance, 0-RTT and CDNs

TLS 1.3 can reduce full-handshake latency, but the observed effect depends on network round trips, client behavior and connection reuse. OCSP stapling may avoid a separate revocation request only for clients that would make one and certificates that support it. Measure representative first and repeat connections rather than promising a universal speed increase.

0-RTT early data has replay risks and weaker secrecy guarantees. These examples do not enable it. If a CDN terminates TLS, review its early-data policy and which application requests can be replayed. Validate client-to-edge and edge-to-origin TLS separately; securing the origin does not configure the edge.

HTTP/2 and a CDN affect different parts of delivery than TLS handshake settings. Confirm the protocol negotiated by the client, measure latency under comparable conditions and test both the edge and origin where relevant. HTTP/3 is not enabled by these examples.

Validation and Renewal Monitoring

After configuration and certificate changes, repeat syntax, chain, protocol, header and representative client checks. Keep the expected result for each test in the deployment record.

The following pipeline displays the leaf certificate’s issuer, subject and dates; it does not validate the full server chain:

openssl s_client -connect example.com:443 -servername example.com < /dev/null | openssl x509 -noout -issuer -subject -dates

To check hostname and chain against the client’s configured trust store, use the following command; supply the appropriate CA file if the default store is unsuitable. The -showcerts list is what the server sent, not a validated chain by itself. Inspect the exit status and verification errors.

openssl s_client -connect example.com:443 -servername example.com -verify_hostname example.com -verify_return_error -showcerts < /dev/null

For an OCSP-capable CA only, display the stapled response and retain error output for diagnosis:

openssl s_client -connect example.com:443 -status -servername example.com < /dev/null | sed -n '/OCSP response:/,/^[[:space:]]*$/p'

Use SSL Labs for an external audit and test real clients as well. Check protocol support, sent intermediates and intended headers; do not use an A/A+ grade as proof of overall security or performance.

If preloading is deliberately selected, use hstspreload.org to check eligibility and status before submitting. Do not use submission itself as a routine TLS test. Continue meeting its requirements while the domain remains listed.

Troubleshoot Chain, DNS and Time Errors

If chain validation fails, compare the sent leaf and intermediate certificates with the intended CA chain. Some clients fetch missing intermediates and others do not. Let’s Encrypt fullchain.pem supplies leaf plus intermediates; the trusted root generally belongs in the client trust store, not the server-sent list.

For OCSP-capable CAs, verify Nginx resolver reachability and outbound access to the actual responder. Check resolution, routing and response errors separately. A resolver setting cannot correct the wrong CA bundle or an unavailable responder.

Check synchronized system time when certificate or OCSP validity appears inconsistent. Confirm the time service is actually synchronized; merely installing it is insufficient.

After renewal, verify the certificate paths, chain and service reload. Check stapling only for CAs that support it. An automated hook may run these checks and report failures, but should not require an OCSP response for current Let’s Encrypt certificates.

Practice TLS and HSTS changes in a suitable test environment before production. Remember that an includeSubDomains policy on a parent can also affect staging or internal hosts beneath it; keeping a staging host out of a separate preload submission does not exempt it from the parent policy.

Roll Out TLS and HSTS Changes

Record the existing configuration and rollback plan, then verify TLS 1.3/TLS 1.2 support and the certificate chain. Apply modern cipher settings with syntax and client tests. Configure OCSP only if the CA supports it. Start HSTS with a short lifetime; increase it through observed stages and add subdomain coverage only after a full inventory. Evaluate preloading as an optional separate decision.

Configuration changes can be rolled back, but previously learned HSTS policies can outlive a server edit. Preload removal additionally depends on browser lists and updates. Account for these delays before increasing policy duration or scope.

Configuration Verification Checklist

Verify negotiated TLS versions and ciphers with supported clients, the sent chain and expected HSTS scope. Disabling TLS 1.0/1.1 excludes clients that depend on them; keeping modern TLS 1.2 does not guarantee compatibility with every older client. Identify affected users and plan upgrades. Include corporate TLS-terminating proxies and inspection devices in client tests rather than weakening public TLS settings to accommodate them. Where OCSP applies, validate the response rather than checking only a status string. Confirm error logs, renewal and reload behavior. Test HTTP/2 negotiation explicitly; the option to request it is not proof it was selected.

When a check fails, retain the exact output and inspect the relevant layer: certificate chain, trust store, resolver, network access, clock or virtual-host selection. Re-run the same check after a targeted change.

Final Checks

A reliable TLS configuration combines maintained software, correct certificate chains and deliberate protocol choices. Forward secrecy depends on key exchange and secret handling. OCSP applies only to supporting CAs, HSTS should be staged, and preloading requires a separate informed choice.

Preserve configuration and test records, monitor certificate renewal, and repeat the same checks after reloads or library upgrades. If a CDN or reverse proxy is present, verify its public behavior separately from the origin.

Frequently Asked Questions

No. Let’s Encrypt retired its OCSP service on August 6, 2025. No staple is expected for its current certificates. The optional OCSP fragments apply only to CAs and certificates that still support it.

No. Verify HTTPS, begin with a short max-age and expand subdomain coverage only after every affected host is ready. Preload is a separate choice with eligibility requirements and slow removal.

Certificate-authenticated handshakes use ephemeral key exchange, but PSK-only resumption and 0-RTT have different guarantees. Protecting and erasing ephemeral secrets also matters.
↑