What Are Sticky Sessions?
Sticky sessions (also called session affinity) tell a load balancer to route every request from a given client to the same backend server for the duration of a session. The balancer typically tracks affinity via a cookie it injects (like AWSALB on ALB or SERVERID on HAProxy) or by hashing the client IP address.
Without stickiness, a round-robin or least-connections balancer can send request #1 to server A and request #2 to server B. That's fine until your application assumes both requests land on the same machine.
When You Actually Need Them
Sticky sessions exist to paper over stateful server-side architecture. They're the right tool when:
- In-process session state is unavoidable. Legacy apps that store session data in memory or on local disk genuinely break if requests scatter across nodes.
- Stateful WebSocket or long-poll connections. Once a WebSocket handshake is complete, all frames must go to the same process. A load balancer without affinity will drop the connection on the next HTTP request.
- Expensive per-request warm-up. Some ML inference servers or JVM apps cache large models or JIT-compiled code in memory. Pinning a client reduces repeated cold-path execution.
- Third-party software you can't modify. Some commercial or embedded systems store session tokens locally and aren't designed for distributed deployment.
A Concrete Example
A Django app using the default database-backed session backend doesn't need sticky sessions—session data lives in the DB. Switch to django.contrib.sessions.backends.cache with a local in-memory cache (not Redis), and suddenly you do.
When Sticky Sessions Hurt You
Affinity trades availability and load distribution for locality. The costs are real:
Uneven load distribution. If one client generates 10× the traffic of others—a bulk API consumer, a crawler, a CI pipeline—that client's designated server bears the entire load while others sit idle. Your p99 latency climbs on one node while metrics look fine in aggregate.
Node failures are more disruptive. When a sticky server dies, every session pinned to it either breaks immediately or requires the balancer to failover and re-establish affinity. Users notice. Without stickiness, a failed node just stops receiving new connections and the impact is proportional to its share of traffic.
Draining nodes for deploys is harder. Zero-downtime rolling deploys depend on graceful connection draining. Sticky sessions mean long-lived clients stay on the old node until their session expires or they're forcibly migrated.
IP-hash affinity breaks behind shared NAT or proxies. A corporate network or mobile carrier may funnel thousands of users through a single IP. All of them land on one server.
Masks the real problem. Reaching for sticky sessions when you should be externalizing session state is technical debt. It limits horizontal scaling and complicates disaster recovery.
How to Decide
Work through this checklist before enabling affinity:
- Identify what state lives on the server. Session tokens? Uploaded file buffers? WebSocket state? In-memory cache?
- Ask whether the state can move. Sessions → Redis or a database. File uploads → object storage with a pre-signed URL. Most state can move.
- Check if your LB supports connection-level affinity for WebSockets. Many do (NGINX, HAProxy, AWS ALB). This is narrower than full cookie-based affinity and less harmful.
- Measure your traffic distribution. If a small number of clients dominate, IP-hash or cookie affinity will cause hotspots. Least-connections without affinity may serve you better.
- Plan for failure. Define what happens to an active session when its pinned node goes down. If the answer is "users get an error," affinity is hiding a reliability gap.
Monitoring Considerations
Sticky sessions make anomalies harder to catch because problems can be isolated to a single node. A server quietly degrading under a hot-spotted client may not surface in your aggregate error rate until it's bad.
Multi-region uptime monitoring—checking each of your nodes or regional endpoints independently—helps here. If Pingy is polling your /health endpoint from multiple locations and one regional cluster starts timing out, you'll see the divergence before users escalate. The same logic applies internally: monitor per-node response time, not just the balancer's aggregate.
Key Takeaways
- Sticky sessions solve a real problem—stateful servers—but they don't fix the underlying architecture.
- Cookie-based affinity is more reliable than IP-hash; avoid IP-hash if clients share NAT.
- Node failures hurt more when sessions are pinned; always test your failover path.
- WebSocket affinity is often the only legitimate long-term use case; for HTTP sessions, externalizing state is almost always worth the effort.
- Monitor individual nodes, not just the load balancer's aggregate view—affinity makes per-node anomalies easy to miss.