On this page
01 Why stateful applications need persistence
A load balancer chooses a server per new connection, and a web session is many connections. If the shopping cart lives in the memory of the server that handled request one, request two must reach the same server or the state is gone. Persistence guarantees that. It is a workaround; the section on statelessness below is the real fix.
02 Source IP persistence and where it fails
The balancer keeps a table of client IP to real server and consults it before the selection method. It works at Layer 4, needs no application change and costs almost nothing. It fails in two directions. Behind carrier NAT, corporate proxies or campus gateways, thousands of users share one address and all land on one server. Mobile clients change address mid-session and lose their server. Use source IP only for non-HTTP protocols where nothing better exists.
03 Cookie insert, cookie passive and cookie hash
For HTTP the balancer can use cookies, which identify a browser rather than an address.
| Method | Who sets the cookie | Balancer action | Trade-off |
|---|---|---|---|
| Cookie insert | The balancer | Adds a cookie naming the chosen server on the first response; reads it afterwards | No application change; needs Layer 7 |
| Cookie passive | The application | Learns an existing application cookie and maps it to the server that issued it | No extra cookie; table entry per session |
| Cookie hash | The application | Hashes an existing cookie value to pick a server deterministically | No table; a pool change reshuffles some clients |
Obscure the server identifier in an inserted cookie; a plain internal address is an information leak. Mark it HttpOnly and Secure. Cookie methods need the balancer to see HTTP, which means Layer 7 processing and, for HTTPS, TLS termination.
04 Session persistence worked example: cookie insert across two virtual ports
Illustrative values. VIP 192.0.2.20 serves TCP 80 and 443 for a shop whose cart lives in server memory. The application session expires after 20 minutes idle, so the persistence timeout is 30 minutes. The inserted cookie SRV carries an opaque token rather than an address and is marked Secure and HttpOnly. Both virtual ports share one persistence group, so a client that arrives on 80 and is redirected to 443 still reaches the same real server.
$ curl -sk -D - -o /dev/null https://192.0.2.20/cart HTTP/1.1 200 OK Set-Cookie: SRV=a1f9c0; Path=/; Secure; HttpOnly; Max-Age=1800 X-Served-By: web2 $ curl -sk -D - -o /dev/null -H 'Cookie: SRV=a1f9c0' https://192.0.2.20/cart X-Served-By: web2 lb1# show server persistence cookie SRV token a1f9c0 real web2 idle 00:00:04 expires 00:29:56
The token maps to web2 in the balancer's table or, in the stateless variant, encodes web2 under a key only the balancer holds. Cookie Max-Age and table timeout are both 30 minutes so browser and balancer forget together. When web2 fails its check the entry is invalidated, the next request is balanced afresh and the cart is gone; the application shows an empty cart rather than an error, which is the documented fallback.
05 Persistence methods compared
| Method | Layer | TLS passthrough | Survives shared NAT | Survives address change | Balancer state |
|---|---|---|---|---|---|
| Source IP | 4 | Yes | No | No | Table |
| Cookie insert | 7 | No | Yes | Yes | None (token) or table |
| Cookie passive or hash | 7 | No | Yes | Yes | Table or none |
| SSL session ID | 4 | TLS 1.2 and older only | Yes | Yes | Table |
| URL parameter | 7 | No | Yes | Yes | Table or none |
06 SSL session ID persistence and why TLS 1.3 broke it
Balancers once persisted encrypted traffic without decrypting it. Under SSLv3 and TLS 1.0 through 1.2 the client sends a session ID in the clear during the handshake and reuses it on resumption, so a Layer 4 balancer could key its table on that value. Session tickets (RFC 5077) had already weakened it. TLS 1.3 (RFC 8446) removed the mechanism. Resumption now uses pre-shared keys in tickets that are opaque, encrypted and ideally single-use, so nothing stable is visible in the path. Treat it as legacy; the replacements are TLS termination plus cookie persistence, or a stateless back end.
07 URL and parameter persistence
Some applications carry a session identifier in the URL, such as a ;jsessionid= path parameter or a query string token, for clients that refuse cookies. The balancer reads the token and maps it to a server by table (learned from the issuing server) or by hash. It requires Layer 7 inspection; the first request carries no token and falls through to the normal method.
08 Timeouts, and persistence versus true statelessness
Table-based methods need an idle timeout so entries expire. Set it slightly longer than the application session timeout; shorter, and a user returning after a break lands on a server that never met them; much longer, and the table fills with dead entries.
The clean alternative removes the need. Put session state in a shared store (a replicated cache or database) every real server reads, or sign the state into the client token so the server holds nothing local. Then any server takes any request, persistence is off and failover is instant. Persistence is a bridge to that design, not a substitute.
Persistence and health checks interact. When a pinned server fails the balancer must reassign the client, which the application sees as a lost session. Decide in advance whether the fallback is "reassign silently" or "return an error", and test it.
09 Testing persistence
Verify with a client that stores cookies and repeats requests, with each real server returning its identity in a response header for the test.
curl -s -c jar.txt -b jar.txt -D - https://www.example.com/ -o /dev/null | grep -i x-served-by curl -s -c jar.txt -b jar.txt -D - https://www.example.com/ -o /dev/null | grep -i x-served-by # repeat; expect the same server rm jar.txt # fresh client; expect the pool to rotate again
Three calls with one cookie jar should name the same server; a fresh jar may land elsewhere. Repeat from a second source address to prove the pin is per cookie, not per IP. Then stop the pinned server and confirm the documented fallback. In multi-site designs, GSLB adds a second layer of stickiness at the DNS level. Persistence is one piece of the design covered in the load balancing section, and it interacts with per-crawler request limits on the VIP: a client pinned to one real server also concentrates its whole request budget on that server.
10 Failure modes and pitfalls
- Unsynchronised tables across a pair. Table-based methods lose every pin at failover unless the table is replicated with the flow table. Token-in-cookie methods survive because the mapping travels with the client.
- HTTP/2 connection reuse. Under RFC 9113 a browser multiplexes many requests on one connection and may reuse it for several hostnames sharing an address and certificate. A Layer 4 device pins the whole connection; a Layer 7 device must apply persistence per request.
- Clients that never return cookies. Scripted fetchers discard cookies, so each request earns a fresh table entry that is never used again. Let such clients rotate, using the signals that mark a client as scripted to identify them.
11 Expert tips
- Prefer a token that encodes the server under a balancer-held key over a lookup table; it needs no replication and cannot be exhausted.
- Group every virtual port of one application into one persistence scope, so a redirect from 80 to 443 stays on the same real server.
- Test in a 2 by 2 grid of two source addresses and two cookie jars; only the same jar should share a server.
Session persistence is the right tool while the application keeps state in memory and the wrong tool the day it stops. Until then, pair the table timeout with the application's own, group the ports, and know what the pair does with its pins when the VIP moves between paired units.
Inference servers have a modern equivalent of persistence, prefix affinity, covered in what changes when balancing model inference.
12 Questions
What is the difference between persistence and affinity?
Nothing in practice. Persistence, affinity, stickiness and sticky sessions all describe a rule that returns a client to the real server it used before. Vendors pick different words; the mechanisms are the same.
Why does source IP persistence overload one server?
Because many users can share one public address through NAT or a proxy. The balancer sees a single client and pins all of them to one server. Cookie-based methods identify each browser individually and avoid the problem for HTTP.
Does TLS 1.3 really prevent SSL session ID persistence?
Yes. TLS 1.3 removed session ID resumption and uses encrypted, ideally single-use pre-shared key tickets. A device without the key sees no stable value to persist on. Terminate TLS on the balancer and use cookies, or make the back end stateless.
How long should a persistence timeout be?
Slightly longer than the application session timeout, so a user who returns before the application logs them out still reaches the server holding their state. Far longer values waste table capacity and let the pool become unbalanced as entries linger.
Is persistence needed if sessions are in a shared store?
No. If every real server can read the session from a shared cache or database, or the state is carried in a signed client token, any server can answer any request. Disable persistence; failover loses nothing because no server holds unique state.
Should persistence cover port 80 and port 443 together?
Yes, when both ports serve one application. Group the virtual ports so the balancer keys the pin on the client rather than on the port; otherwise a user redirected from HTTP to HTTPS lands on a different real server and loses the cart or login the first server held.