Choosing Between Cloudflare Workers KV and Durable Objects for Per‑User Session Storage
Decide whether Cloudflare Workers KV or Durable Objects is better for per‑user session storage based on latency, consistency, and operational trade‑offs.
17 Jan 2026, 13:43 UTC

Decision: Store per‑user session data in Cloudflare Workers
Problem: Your Worker must read a user’s session in under 10 ms, guarantee that a write is immediately visible to the next request, and keep each session record ≤ 128 KB.
Takeaway: If you can tolerate a brief window of eventual consistency, use the simpler KV binding; otherwise choose Durable Objects for strong consistency at the cost of higher per‑object compute overhead.
Constraints
- Read latency ≤ 10 ms (edge‑level).
- Write must be strongly consistent for the same user‑id.
- Maximum session payload 128 KB.
- Minimize operational complexity and cost.
Option comparison
| Feature | KV Binding | Durable Objects |
|---|---|---|
| Consistency | Eventual (propagation typically seconds) | Strong (single‑object) |
| Read latency | ~5‑15 ms edge | ~5‑15 ms + object hop |
| Write throughput | High, partitioned across keys | Limited per object (single‑threaded) |
| Size limit per value | 128 KB | 128 KB |
| Cost model | Requests + storage | Requests + compute‑time |
| Operational complexity | Low (namespace binding) | Higher (object lifecycle, alarm, storage) |
Trade‑offs
KV offers a simple API and high write throughput, but its eventual consistency means a session written in one request may not be immediately visible to a subsequent request handled by a different Cloudflare edge node, potentially causing an authentication gap. Durable Objects provide strong consistency and built‑in locking because each object runs a single‑threaded event loop, yet they incur additional compute‑time charges for that loop and are confined to a single region per object class, which can add latency for globally distributed users.
Implementation example – KV binding
# wrangler.toml (add the KV namespace binding)
name = "session-worker"
main = "src/index.js"
compatibility_date = "2024-01-01"
[[kv_namespaces]]
binding = "SESSION_STORE"
id = ""
// src/index.js
addEventListener('fetch', event => {
event.respondWith(handleRequest(event.request))
})
async function handleRequest(request) {
const { pathname, method } = new URL(request.url)
const userId = request.headers.get('X-User-ID') // supplied by caller
if (method === 'POST' && pathname === '/session') {
const newSession = { userId, created: Date.now() }
await SESSION_STORE.put(userId, JSON.stringify(newSession), { expirationTtl: 1800 })
return new Response(JSON.stringify(newSession), { headers: { 'Content-Type': 'application/json' } })
}
if (method === 'GET' && pathname === '/session') {
const raw = await SESSION_STORE.get(userId, { type: 'text' })
if (raw === null) {
return new Response('{}', { status: 404, headers: { 'Content-Type': 'application/json' } })
}
return new Response(raw, { headers: { 'Content-Type': 'application/json' } })
}
return new Response('Not found', { status: 404 })
}
Validation steps
- Run
wrangler devin the project directory. This requires read/write access to the local filesystem and the ability to bind the KV namespace defined inwrangler.toml. - Send a POST request to create a session, e.g.:
curl -X POST http://localhost:8787/session -H "X-User-ID: alice123". Verify the response is a 200 OK containing JSON with the expected fields. - Immediately send a GET request for the same user:
curl http://localhost:8787/session -H "X-User-ID: alice123". Check that the returned JSON matches the POST payload. - To observe KV’s eventual consistency, repeat the GET after a short pause (e.g., 2 seconds) from a different terminal or using a tool that may route to a different edge node in a simulated environment. If the GET returns an empty or stale value, you have seen the consistency window.
- Optionally inspect the stored key via the Cloudflare dashboard or run
wrangler kv:key list --binding=SESSION_STOREafter the request to confirm the key exists with the expected value and TTL.
Expected check: the second GET returns the same JSON as the first POST; if it differs, you are observing KV’s eventual consistency.
Practical way to check result: inspect the KV namespace via the Cloudflare dashboard or run wrangler kv:key list --binding=SESSION_STORE after the request to confirm the key exists with the expected value and TTL.
Limitations
- KV’s eventual consistency can cause a stale session read if the second request lands on a different edge node within the propagation window (typically a few seconds).
- Durable Objects add compute‑time cost and are scoped to a single region per object class, which may increase latency for users far from that region.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.