Product Docs Pricing Changelog
Start free Sign in
Guide

Auth on Cloudflare Workers — the complete guide.

Authentication on Cloudflare Workers is mostly the same as anywhere — until you hit the edge-specific gotchas: where sessions live, a cookie trap that silently breaks Safari, a CPU budget that won't run a normal password hash, and OAuth callbacks that have to point at exactly the right URL. This guide covers each, the standard fixes, and where Flarelink does it for you.

Your options

Three ways to do auth on Workers.

Roll your own

Use a library like BetterAuth or Lucia directly on a Worker, wire up D1/KV yourself. Maximum control; you own every gotcha below.

Hosted auth

Clerk, Auth0, WorkOS — call a third-party service from your Worker. Fast, but your users live on their infrastructure and pricing is per-MAU.

Provisioned on your account

Flarelink deploys a source-available auth Worker to your Cloudflare account with the gotchas handled. No per-MAU fee; you own the users.

All three are legitimate. If you enjoy owning the details, rolling your own is very doable — the rest of this guide is the map. If you'd rather skip to a working setup, that's what Flarelink is.

Gotcha 1

Put sessions in KV, not D1.

Every authenticated request checks the session. If sessions live in D1, that's a row read on the hot path of every request — and D1 bills per row read. Cloudflare KV is purpose-built for this: globally cached, sub-millisecond reads, negligible cost.

The trade-off is consistency: KV is eventually consistent, so a sign-out or revocation propagates within KV's window (seconds) rather than instantly at every edge. For session tokens that's almost always the right call; just don't assume global revocation is synchronous.

Flarelink stores sessions in KV by default. More on the consistency window in Limits & semantics.

Gotcha 2

The Safari cross-site cookie trap.

The default Workers setup puts your app on one domain and the auth Worker on *.workers.dev — a different registrable domain. The session cookie is then a third-party cookie (SameSite=None; Secure; Partitioned), and Safari and iOS block third-party cookies. Sign-in looks like it works, then the session check returns null because the cookie was never stored. Chrome currently allows it via the Partitioned (CHIPS) attribute; Safari doesn't.

The fix: put the auth Worker on a subdomain of your app's own domain (app on myapp.com → auth on auth.myapp.com), which makes the cookie same-site. There's a second, dev-only variant: Safari also refuses to store a Secure cookie over http://localhost, so serve local dev over HTTPS.

Full breakdown + a per-browser matrix: Cookies & Safari. Flarelink ships a one-click custom-domain attach and flags at-risk deployments.

Gotcha 3

Password hashing inside the CPU budget.

The Workers free plan caps CPU at ~10 ms per request. A properly tuned scrypt or a 600k-iteration PBKDF2 (the OWASP baseline) blows straight past that, so naive auth code either errors or you're tempted to under-hash silently.

The honest approach: use PBKDF2-SHA256, pick an iteration count you can defend, publish it, and raise it on the paid plan. Store the count with each hash so you can rehash-on-login as you increase it — no bulk migration, no forced reset.

Flarelink uses PBKDF2-SHA256 with a per-deployment count (100k default, 600k on the Workers paid plan), rehash-on-login, and publishes the exact parameters on the trust page.

Gotcha 4

OAuth callbacks have to be exact.

OAuth providers redirect back to a single, exact callback URL. On Workers that URL is on your auth Worker's host — and if you later move the Worker to a custom domain, the callback URL changes and every provider needs updating, or sign-in fails with redirect_uri_mismatch. The host, scheme, and path must match character-for-character.

Get the redirect URI from one source of truth and paste it verbatim. Keep dev and prod hosts as separate OAuth apps so their callbacks don't collide.

Step-by-step Google + GitHub setup with the exact URIs: OAuth provider setup.

Gotcha 5

Local development that actually matches prod.

Two things bite locally: the same cross-site cookie problem (your local app on localhost:5173, the auth Worker on workers.dev), and Safari refusing Secure cookies over plain http://localhost. Reverse-proxy /api/auth/* through your dev server to make the cookie first-party, and serve dev over HTTPS (e.g. vite-plugin-mkcert). Add every local origin to your trusted-origins list.

The full local setup: Local development.

The shortcut

Or: skip all five.

Flarelink provisions an auth Worker to your Cloudflare account with every gotcha above handled — sessions in KV, the right cookie attributes, defensible hashing, OAuth wiring, and a local-dev story. The difference from hosted auth is that the Worker runs on your account, the users live in your D1, and the Worker is source-available — you can rebuild it from source and diff it against what's deployed.

npm install @flarelink/client
// browser — sessions, OAuth, magic links import { createFlarelink } from "@flarelink/client" const flarelink = createFlarelink({ url: process.env.FLARELINK_AUTH_URL! }) await flarelink.auth.signIn({ email, password }) const me = await flarelink.auth.getMe() // User | null

Start with the quickstart, or read why database + storage are server-first by design. Prefer to verify before trusting? The trust page shows how.

Auth on Cloudflare, without the footguns.

Free during beta. A working, verifiable auth Worker on your own account in about a minute.