How to Configure Apache for Session Stickiness

How to configure Apache for session stickiness

How to configure Apache for session stickiness

I ran into session stickiness the hard way — users kept getting logged out randomly on a load-balanced application, and it took me an embarrassingly long time to realize the issue was that each request was landing on a different backend server, each with its own separate in-memory session store. Session stickiness (also called “sticky sessions”) solves this by making sure a given user consistently reaches the same backend server for the duration of their session. Here’s how I configure it in Apache.

What Is Session Stickiness and When Do You Need It?

Session stickiness ensures that once a client’s first request is routed to a particular backend server, all subsequent requests from that same client are routed to the same server, rather than being distributed according to the normal load balancing algorithm.

You need this when:

You generally don’t need this if your application already uses a shared session store like Redis or a database — in that case, any backend server can serve any request without issue, and true stateless load balancing works better.

I’ll say upfront: where possible, I actually prefer solving this with a shared session store rather than sticky sessions, since stickiness can create uneven load distribution and complicates failover. But sticky sessions are sometimes the pragmatic short-term fix, so here’s how to do it properly.

Prerequisites

Step 1: Enable Required Modules

sudo a2enmod proxy proxy_http proxy_balancer lbmethod_byrequests headers
sudo systemctl restart apache2

Step 2: Assign Route Identifiers to Balancer Members

Each backend server needs a unique route identifier that Apache uses to track which server a session belongs to:

<Proxy "balancer://webcluster">
    BalancerMember "http://192.168.1.10:8080" route=node1
    BalancerMember "http://192.168.1.11:8080" route=node2
    BalancerMember "http://192.168.1.12:8080" route=node3
    ProxySet lbmethod=byrequests
    ProxySet stickysession=ROUTEID
</Proxy>

ProxyPass "/" "balancer://webcluster/"
ProxyPassReverse "/" "balancer://webcluster/"

stickysession=ROUTEID tells Apache to look for a cookie or URL parameter named ROUTEID and use its value to determine which backend to route to.

Step 3: Configure Your Application to Embed the Route ID

This is the part that trips people up — Apache doesn’t automatically embed the route ID into your session cookie. Your application (or a small piece of middleware) needs to do this itself, typically by appending the route to the session ID.

For example, with PHP sessions, you might configure the session ID format so it includes the backend’s route ID:

// A common pattern: append route to session ID
session_id(session_id() . '.node1');

For Java/Tomcat applications, this is typically handled automatically via jvmRoute in server.xml:

<Engine name="Catalina" defaultHost="localhost" jvmRoute="node1">

Tomcat automatically appends .node1 to the JSESSIONID cookie, which Apache’s stickysession=ROUTEID (commonly set to JSESSIONID for Tomcat setups) can then parse.

ProxySet stickysession=JSESSIONID

Step 4: Cookie-Based vs. URL-Based Stickiness

Apache supports stickiness via cookies or URL parameters:

# Cookie-based (most common)
ProxySet stickysession=ROUTEID|domain

# Using a specific cookie path and domain
Header add Set-Cookie "ROUTEID=.%{BALANCER_WORKER_ROUTE}e; path=/" env=BALANCER_ROUTE_CHANGED

I stick with cookie-based stickiness in almost all cases — URL-based approaches are messier and can break bookmarking or link sharing.

Step 5: Handle Failover Gracefully with Stickiness Enabled

A key question with sticky sessions: what happens when the “sticky” backend goes down? By default, Apache will fail over to another available member, but the user’s session data on the original server is lost unless it’s shared.

BalancerMember "http://192.168.1.10:8080" route=node1 retry=60

If you want strict stickiness (never failover, to avoid inconsistent state), you can disable failover for sticky sessions, though I rarely recommend this since it means a user’s session simply breaks entirely if their assigned server goes down:

ProxySet stickysessionsep=off

In most cases, I’d rather have a user re-authenticate after failover than see a hard error — so I leave default failover behavior enabled.

Step 6: Verify Stickiness Is Working

Test with multiple requests and inspect the routing cookie:

curl -c cookies.txt -b cookies.txt -I http://example.com/
cat cookies.txt

Then make repeated requests using the saved cookie and confirm (via response headers or backend-specific identifying data) that they consistently hit the same backend:

for i in {1..5}; do curl -s -b cookies.txt http://example.com/ | grep "served-by"; done

Real-World Use Cases

Troubleshooting Common Issues

Stickiness Not Working At All — verify your application is actually embedding the route ID in the session cookie; Apache can’t create stickiness out of thin air if the cookie value never changes per backend.

Users Randomly Losing Sessions — check if a load balancer or CDN in front of Apache is stripping cookies, or if stickysession and your actual cookie name (ROUTEID vs JSESSIONID, etc.) don’t match.

Uneven Load Distribution — this is an inherent tradeoff with sticky sessions; if a disproportionate number of long-lived sessions land on one server, that server will carry more load regardless of overall request-based balancing. Monitor and consider migrating to a shared session store if this becomes a recurring problem.

Security Best Practices

Performance Optimization Tips

FAQs

Is session stickiness the same as a persistent connection? No. Persistent connections (keep-alive) are about reusing a single TCP connection for multiple requests; session stickiness is about routing all of a user’s requests — potentially across many separate connections — to the same backend server.

Does sticky sessions work well with autoscaling? Not particularly well. When a “sticky” backend is removed during scale-down, any users pinned to it lose their session. If you’re autoscaling frequently, a shared session store is generally the better architectural choice.

Can I use sticky sessions with HTTPS? Yes, stickiness works the same way regardless of whether SSL is terminated at Apache or passed through to the backend — the routing decision is based on the cookie value, not the encryption layer.

Summary and Key Takeaways

Session stickiness in Apache, configured via stickysession and per-member route identifiers, solves the problem of in-memory session state getting lost across a load-balanced pool. It’s a practical solution, especially for legacy applications, but it comes with tradeoffs in load distribution evenness and failover behavior. Where possible, I’d still recommend moving toward a shared session store as the more scalable long-term architecture — but sticky sessions remain a solid, well-supported option when that’s not immediately feasible.

References

Exit mobile version