Intermediate9 min readlive prototype

IP Hash

Hash the client's address to a server so the same client always lands on the same backend — sticky sessions, no shared store.

Overview

What this concept solves

Every algorithm so far spreads load without caring who the request comes from. IP hash does the opposite: it deliberately sends the same client to the same server every time, by hashing the client's IP address and mapping that to a backend. This is session affinity (a.k.a. sticky sessions) achieved with zero shared state — no session store, no cookies, just arithmetic.

Why pin a client to a server? Because some servers hold per-client state that's expensive to move: an in-memory session, a warmed local cache, an open websocket, a partially-uploaded file. If request N+1 lands on a different server than request N, that state is gone. Hashing the client identity guarantees stickiness deterministically, the same way on every load balancer in the fleet — no coordination required.

Mechanics

How it works

hash(client) mod N

  1. Take a stable client identifier — typically the source IP (or a header like a session ID).
  2. Run it through a hash function to get a large integer.
  3. Map to a server with hash mod N, where N is the number of backends.
  4. Forward there. The same input always yields the same server, so the client sticks.

The prototype uses a small djb2-style string hash on the IP and takes it mod 4. Click any client and the 'Latest hash calculation' strip shows the exact numbers: hash("10.0.0.7") = … → mod 4 = 2 → Server 3. The routing table makes the determinism obvious — each client has one fixed destination, forever.

Two real problems

  • Uneven distribution. Balance now depends on the clients, not the algorithm. A few heavy clients hashing to the same server creates a hotspot the LB can't fix — watch the 'Spread' stat stay stubbornly nonzero.
  • The resharding catastrophe. mod N means changing N remaps almost everyone. Add or remove a single server and most clients suddenly hash to a different backend — every sticky session breaks at once.

Plain mod-N hashing is brittle — use consistent hashing

Because hash mod N reshuffles nearly all keys when N changes, production systems use consistent hashing (a hash ring) or Google's Maglev hashing instead. Those remap only ~1/N of clients when a server joins or leaves, keeping the rest of the sticky sessions intact. Same goal — stickiness — far gentler under churn.

Interactive prototype

Run it. Break it. Tune it.

Sandboxed simulation embedded right in the page. No setup, no install.

About this simulation

Five clients, each with a fixed IP. The balancer hashes the IP and takes it mod 4 — so a given client always lands on the same server, shown in the routing table. Click a client to watch its hash computed live; notice the distribution can be lumpy because it depends on the IPs, not on balance.

Hands-on

Try these on your own

Open the prototype above, run each experiment, predict the answer, then verify.

try 01

Confirm the stickiness

Click 'Client C' several times. Every request follows the same line to the same server, and the 'Latest hash calculation' strip shows the identical computation each time. Check the routing table: Client C's row never changes destination — that's deterministic affinity.

try 02

Watch the distribution go lumpy

Hit 'Send 20 random'. Because routing depends on which IPs exist (not on balancing), the per-server counts come out uneven and the 'Spread (max − min)' stat stays well above zero. No amount of traffic evens it out — the hash, not the load, decides.

try 03

Reason about resharding

The routing table shows each client's server for N = 4. Now imagine N drops to 3: every hash mod 4 becomes hash mod 3, so almost every client remaps to a different server and all their sessions break at once. That fragility is exactly why production systems reach for consistent hashing instead of plain mod-N.

In practice

When to use it — and what you give up

When to reach for it

  • Stateful sessions without a shared store — server-local session data, when you can't or won't centralize it in Redis.
  • Cache locality — keep a client on the server that already has its data warm, raising hit rates.
  • Sticky long-lived connections — websockets, SSE, gRPC streams that must stay on one backend.
  • Sharding by key — the same idea routes a given user/tenant ID to its owning shard.

Real-world example

NGINX exposes ip_hash and a general hash $key consistent directive; HAProxy has balance source and balance hdr. For large dynamic fleets, Google's Maglev and ring-based consistent hashing are the production-grade variants that survive backends coming and going.

Pros

  • Deterministic session affinity with no shared session store or cookies.
  • Same mapping on every load balancer — no coordination across the fleet.
  • Improves cache hit rates and keeps client-local state intact.
  • O(1) per request: one hash, one modulo.

Cons

  • Distribution depends on the client population — heavy or clustered clients create hotspots the LB can't smooth.
  • Blind to load, capacity, and latency — it optimizes stickiness, not balance.
  • Plain mod N remaps almost all clients when the server count changes; needs consistent hashing to be safe under churn.
  • Clients behind a shared NAT/proxy all hash to one server, concentrating load.

Reference

Code & further reading

A minimal reference implementation and pointers worth bookmarking.

ip-hash.ts
// IP hash: deterministic session affinity via hash(client) mod N.
function hashIP(ip: string): number {
  let h = 5381;                 // djb2
  for (let i = 0; i < ip.length; i++) {
    h = ((h << 5) + h + ip.charCodeAt(i)) | 0;
  }
  return Math.abs(h);
}

class IpHashBalancer {
  constructor(private servers: string[]) {}
  pick(clientIp: string): string {
    return this.servers[hashIP(clientIp) % this.servers.length];
  }
}

// The fragility: 'mod N' remaps almost everyone when N changes.
// Production systems use consistent hashing (a ring) instead, which
// remaps only ~1/N of clients when a server is added or removed:
//
//   ring.addServer("s5");      // only keys between s5 and its
//   const s = ring.locate(ip); // predecessor move — the rest stay put

References & further reading

6 sources

Knowledge check

Did the prototype land?

Quick questions, answers revealed on submit. No scoring saved.

question 01 / 03

What is the primary purpose of IP hash load balancing?

question 02 / 03

Why can IP hash produce an uneven load distribution?

question 03 / 03

Why do production systems prefer consistent hashing over plain `hash mod N`?

0/3 answered

Was this concept helpful?

Tell us what worked, or what to improve. We read every note.