Newsletter Double Opt-In Without a Database Token Table
2 min read
Double opt-in is non-negotiable for email lists. But most implementations store a token in a database table, then match it on confirmation. There’s a simpler way.
The HMAC approach
Instead of storing tokens, sign the email address with a secret key:
token = HMAC-SHA256(secret, email + "|" + timestamp)
confirm_url = /api/confirm?email=user@example.com&t=1715500800&sig=abc123
On confirmation:
- Recompute
HMAC-SHA256(secret, email + "|" + timestamp) - Compare with
sig— if they match, the token is valid - Check that
timestampis within 24 hours - Flip subscriber status to
active
No database reads for verification. No token cleanup cron. No leaked tokens table.
Implementation in Cloudflare Workers
const encoder = new TextEncoder();
async function sign(email: string, timestamp: number, secret: string): Promise<string> {
const key = await crypto.subtle.importKey(
"raw",
encoder.encode(secret),
{ name: "HMAC", hash: "SHA-256" },
false,
["sign"],
);
const data = encoder.encode(`${email}|${timestamp}`);
const sig = await crypto.subtle.sign("HMAC", key, data);
return btoa(String.fromCharCode(...new Uint8Array(sig)));
}
Cloudflare Workers have the Web Crypto API built in. No dependencies needed.
Unsubscribe: RFC 8058 one-click
The List-Unsubscribe header with List-Unsubscribe-Post: List-Unsubscribe=One-Click tells email clients to send a POST on behalf of the user. No clicking, no confirming.
List-Unsubscribe: <https://yoursite.com/api/unsubscribe?email=...&sig=...>
List-Unsubscribe-Post: List-Unsubscribe=One-Click
Same HMAC verification. Same stateless approach. One POST, one database update, done.
Security considerations
- Secret rotation: Store the signing key in Workers Secrets. Rotate by accepting both old and new keys for 24 hours.
- Rate limiting: Use KV counters to limit subscribe/confirm attempts per IP.
- Timing attacks: Use
crypto.subtle.timingSafeEqualfor signature comparison — or just compare the full strings (btoa output is constant-length for SHA-256).
Why not just use a token table?
Token tables work. But they add:
- A migration
- A cleanup cron job
- A database read on every confirmation
- A potential data leak if the table is exposed
HMAC tokens add zero database operations for verification. The secret lives in Workers Secrets. The math is simple.
This pattern works for any verification flow: email confirmation, password reset, magic links. Stateless, secure, and free.
Ready to scale your content?
Book a free strategy session and get a personalized cross-platform distribution plan.
Book a Call