2026年11月14日

2026年11月14日

Nginx SSL Session Cache: ssl_session_cache Setup and Resumption Tests

This article is a localized edition of the Japanese original. Read the Japanese original

The Nginx SSL session cache lets a browser that has already completed a TLS connection reconnect in fewer steps by reusing the result of the previous key exchange (session resumption). The configuration centers on two lines, ssl_session_cache and ssl_session_timeout; put them in the http block and they apply to every HTTPS site.

Here is the short answer.

http {
    ssl_session_cache   shared:SSL:10m;
    ssl_session_timeout 1h;
    # ssl_session_tickets is on by default
}
  • shared:SSL:10m is a 10MB cache shared by all worker processes. According to the official documentation, 1MB stores about 4,000 sessions.
  • The default for ssl_session_timeout is 5 minutes. Extend it to match how often visitors return.
  • Since Nginx 1.23.2, when a shared cache is configured, the TLS session ticket keys are also generated, stored and periodically rotated inside it. Reloading the configuration no longer invalidates tickets.
  • To confirm resumption, check that openssl s_client prints Reused, or that $ssl_session_reused in the access log is r.

The behavior in this article was tested with Nginx 1.30.6 (OpenSSL 3.5.7) as of November 2026.

Two ways to resume a session

TLS session resumption comes in two forms: the server keeps the session state, or the client carries it.

Method How it works Nginx setting
Session ID (cache) The server stores session state in a cache, and the client sends the ID to resume ssl_session_cache
Session ticket The server encrypts the session state and hands it to the client, which presents it to resume ssl_session_tickets, ssl_session_ticket_key

In TLS 1.3, resumption data is delivered as a "ticket" the server sends after the handshake. With Nginx (OpenSSL), TLS 1.3 resumption still worked with ssl_session_tickets off as long as a shared cache existed, using tickets that keep the state on the server side (verified in the test steps below).

What each directive means and its default

This is a summary of the official documentation (ngx_http_ssl_module). Each directive can go in the http or server block.

ssl_session_cache

The syntax is ssl_session_cache off | none | [builtin[:size]] [shared:name:size]; and the default is none.

Value Meaning
off Strictly prohibits the cache and tells clients that sessions may not be reused
none (default) Tells clients sessions may be reused but does not actually store them in a cache
builtin[:size] OpenSSL's built-in cache. Separate per worker process; size is in sessions (20480 if omitted). Can cause memory fragmentation
shared:name:size A cache shared by all workers. Size is in bytes; 1MB holds about 4,000 sessions. The same name can be used in several server blocks

The official documentation says builtin and shared can be combined, but using only shared should be more efficient. The cache name (SSL in the example) is any string you like.

ssl_session_timeout

This is how long a client can reuse a session. The default is 5m (5 minutes). A longer value raises the share of returning visitors who can resume, but more sessions stay in the cache, so review the cache size at the same time. When the cache fills up, the oldest entries are discarded.

ssl_session_tickets

This turns resumption through session tickets on or off; the default is on. Even without a shared cache, resumption works while tickets are enabled. In that case, though, the ticket key is generated at random at startup, so the key changes on every configuration reload or restart, and earlier tickets stop working. With a shared cache configured (1.23.2 and later), the key is stored in the cache, rotated periodically and carried over across reloads.

ssl_session_ticket_key

This specifies a file holding the key that encrypts tickets. It is meant for when several servers must share the same key; on a single server you do not need it. The file is 80 bytes (AES256) or 48 bytes (AES128) of random data, created like this.

sudo openssl rand 80 | sudo tee /etc/nginx/ticket_current.key > /dev/null
sudo chmod 600 /etc/nginx/ticket_current.key

If you list several keys, only the first is used to encrypt, and the rest are used only to decrypt. Use this to rotate keys.

ssl_session_ticket_key /etc/nginx/ticket_current.key;
ssl_session_ticket_key /etc/nginx/ticket_previous.key;

If this key leaks, the security of connections that used tickets encrypted with it is compromised. Make the key files readable only by root, and if you cannot rotate them on a regular schedule yourself, skip this directive and rely on the automatic rotation of the shared cache.

Recommended configuration

This example is for a single web server running WordPress or similar. Put it in the http block of /etc/nginx/nginx.conf (or at the top of a file in conf.d).

http {
    # Session cache shared by all workers (10MB holds about 40,000 sessions)
    ssl_session_cache   shared:SSL:10m;
    # How long a session can be reused (default 5 minutes)
    ssl_session_timeout 1h;

    server {
        listen 443 ssl;
        server_name example.com;
        ssl_certificate     /etc/letsencrypt/live/example.com/fullchain.pem;
        ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
        # ...
    }
}

After editing, check the syntax and then reload.

sudo nginx -t
sudo systemctl reload nginx

If you write ssl_session_cache in each server block and run several sites on the same IP and port, the value from the default server may be used. To use the same value for all sites, the clearest approach is to write it once in the http block.

How this differs from settings seen in older articles

  • OCSP stapling such as ssl_stapling on; is a mechanism for checking certificate revocation and has nothing to do with the session cache. Also, Let's Encrypt shut down its OCSP service in August 2025, so ssl_stapling does not work with its certificates.
  • Connection lists such as ss -tan do not show the state of the session cache. Use the methods in the next section instead.

Verifying session resumption

Check with openssl s_client (TLS 1.2)

With -reconnect, the client reconnects 5 times using the same session. If the first line is New and the rest are Reused, resumption works.

echo | openssl s_client -connect example.com:443 -servername example.com -tls1_2 -reconnect 2>/dev/null | grep -E '^(New|Reused),'
New, TLSv1.2, Cipher is ECDHE-RSA-AES256-GCM-SHA384
Reused, TLSv1.2, Cipher is ECDHE-RSA-AES256-GCM-SHA384
Reused, TLSv1.2, Cipher is ECDHE-RSA-AES256-GCM-SHA384
(remaining lines omitted)

Check with openssl s_client (TLS 1.3)

In TLS 1.3 the resumption ticket arrives after the handshake, so with -reconnect every line showed New. Save the session to a file on the first connection and use it for the second. The sleep 1 | keeps the connection from closing before the ticket is received.

sleep 1 | openssl s_client -connect example.com:443 -servername example.com -sess_out /tmp/tls13.sess > /dev/null 2>&1
sleep 1 | openssl s_client -connect example.com:443 -servername example.com -sess_in /tmp/tls13.sess 2>/dev/null | grep -E '^(New|Reused),'

If you see Reused, TLSv1.3, …, resumption works.

Check in the access log

To see whether real visitors resume sessions, log the variable $ssl_session_reused (r if resumed, . otherwise).

http {
    log_format tls '$remote_addr [$time_local] "$request" $status '
                   '$ssl_protocol $ssl_session_reused';

    server {
        # ...
        access_log /var/log/nginx/tls.log tls;
    }
}
# Get a rough count of resumed connections
awk '{print $NF}' /var/log/nginx/tls.log | sort | uniq -c

Note that browsers reuse the same connection (keep-alive, HTTP/2), so the value becomes r only when a new TLS connection is established.

Behavior confirmed in testing

These are the results of checking resumption for each combination of settings on Nginx 1.30.6.

Settings TLS 1.2 TLS 1.3 Resumption after reload
No cache, tickets default (on) Resumes Resumes Does not resume (key is regenerated)
No cache, ssl_session_tickets off Does not resume Does not resume —
shared:SSL:10m, tickets default (on) Resumes Resumes Resumes
shared:SSL:10m, ssl_session_tickets off Resumes Resumes —

Ticket-based resumption works even with the defaults, but configuring a shared cache keeps resumption from breaking on every reload.

Notes for multi-server setups

When several Nginx servers sit behind a load balancer, each session cache exists only inside its own server.

  • If a client's connection is routed to a different server, neither the cache nor tickets can resume it, and a full handshake takes place (the connection still succeeds, just a little slower).
  • To resume across all servers, place the same key file on every server with ssl_session_ticket_key, and rotate it on all of them at the same time.
  • If the load balancer terminates HTTPS, TLS session resumption is configured on the load balancer, and these Nginx settings do not apply.

FAQ

How large should the cache be?

As a guide, 1MB holds about 4,000 sessions, so estimate from the number of new visitors who connect within ssl_session_timeout. For a typical site, 10m is usually enough, and it uses 10MB of memory.

Should ssl_session_tickets be off?

Some configuration guides recommend off, on the grounds that if the ticket key stays unchanged for a long time, a leaked key would also affect past traffic. With Nginx 1.23.2 or later and a shared cache, the key is rotated automatically. Even with it off, as the tests show, a shared cache lets both TLS 1.2 and 1.3 resume.

Do I need to clear the cache after renewing a certificate?

No. After renewing, run nginx -t and systemctl reload nginx, and new connections will use the new certificate.

Summary

  • The basic setup is ssl_session_cache shared:SSL:10m; plus ssl_session_timeout in the http block; extend the 5-minute default as needed.
  • With a shared cache, Nginx 1.23.2 and later store and rotate ticket keys automatically. Use ssl_session_ticket_key when several servers must share a key.
  • Verify with openssl s_client -reconnect for TLS 1.2, -sess_out and -sess_in for TLS 1.3, and $ssl_session_reused for real visitors.

Related articles:

References: nginx – ngx_http_ssl_module, OpenSSL – openssl-s_client, Let's Encrypt – Ending OCSP Support in 2025

Revision history: 2026-11-10 full revision (removed descriptions of nonexistent uses, ssl_stapling steps unrelated to the session cache, and an incorrect check using ss; replaced them with each directive's default, ticket keys, and tested resumption checks).

Questions about WordPress? The publisher of this site is based in Japan and replies in Japanese. Contact (Japanese)

お気軽にご相談ください

お見積りへ お問い合わせへ
▲