FlashTemp
← All articles
Engineering 2026-03-18 · 8 min read

How FlashTemp Works: Building a Disposable Email Service on Cloudflare Workers

A technical deep-dive into how FlashTemp processes inbound email using Cloudflare Email Routing, Workers, D1, and R2 — with no traditional mail server anywhere in the stack.

Most disposable email services run on a fairly traditional stack: a VPS somewhere, an SMTP server like Postfix, a database, and a web frontend. It works, but it has real operational costs — server maintenance, IP reputation management, scaling bottlenecks, and a single point of failure.

FlashTemp is built differently. There's no traditional server anywhere in the stack. Here's how it works.

The core constraint

The design goal was: can you build a fully functional disposable email service using only Cloudflare primitives? No VPS, no Postfix, no managed database service outside of Cloudflare's own offerings.

The answer turned out to be yes — and the result is faster, cheaper, and more reliable than the traditional approach.

Receiving email: Cloudflare Email Routing

The first challenge with any email service is actually receiving mail. Normally, this means running an SMTP server, managing MX records, and handling the full SMTP protocol including TLS negotiation, SPF/DKIM verification, and bounce handling.

Cloudflare Email Routing handles all of that. You set a catch-all rule that routes all *@flashtemp.email inbound mail to a Worker, and Cloudflare does the rest — MX records, TLS, spam filtering, the lot.

The Worker receives the raw message as a ReadableStream via the email() handler:

export default {
  async email(message, env) {
    const parsed = await PostalMime.parse(message.raw);
    // store to D1...
  }
};

Parsing: postal-mime

Raw email is surprisingly complex. A single message can contain plain text, HTML, multiple MIME parts, base64-encoded attachments, and dozens of headers — all in a format that's been accumulating quirks since 1982.

postal-mime handles the parsing. It runs entirely in the Worker runtime (no native dependencies), accepts a ReadableStream directly, and returns a clean object with from, subject, text, html, headers, and attachments.

One edge case worth noting: very large HTML emails (some newsletters exceed 500KB of HTML) can push against the Worker's CPU time limit. The fix is to truncate html_body at a sensible limit (FlashTemp uses ~200KB) before storing.

Storage: Cloudflare D1

D1 is Cloudflare's serverless SQLite. Each Worker region gets its own replica, reads are local (fast), and writes replicate globally. For a service where each inbox is isolated and queries are simple lookups by inbox ID or address, it's essentially ideal.

The schema has four tables: users, inboxes, messages, and attachments. All foreign key relationships use ON DELETE CASCADE, which means deleting an inbox cascades through messages and attachments automatically — important for the cleanup cron.

The most performance-sensitive query is the inbox lookup on inbound email:

SELECT * FROM inboxes 
WHERE address = ? 
AND expires_at > datetime('now')

This runs on every inbound email. With an index on address, it's a single B-tree lookup — fast regardless of how many inboxes exist.

File storage: Cloudflare R2

Email attachments go to R2 (Cloudflare's S3-compatible object storage) under a key structured as att/{inbox_id}/{message_id}/{attachment_id}/{filename}. This hierarchy means you can list or delete all attachments for a given message or inbox with prefix queries.

R2 has no egress fees, which matters for a service where users download attachments — one of the hidden costs that makes attachment support expensive on S3-backed services.

Authentication: JWTs with Web Crypto

The Worker runtime includes the Web Crypto API, which means you can do HMAC-SHA256 JWT signing and verification without any external library:

const key = await crypto.subtle.importKey(
  'raw',
  new TextEncoder().encode(secret),
  { name: 'HMAC', hash: 'SHA-256' },
  false,
  ['sign', 'verify']
);

Passwords are hashed with PBKDF2-SHA256 at 100,000 iterations, also via Web Crypto. No bcrypt dependency needed.

Cleanup: Cloudflare Cron Triggers

Expired inboxes, messages, and attachments are deleted by a scheduled Worker that runs at 02:00 UTC daily. The cleanup sequence matters: R2 objects must be deleted before the database rows that reference them, otherwise you get orphaned files.

async scheduled(event, env) {
  // 1. Find expired messages
  // 2. Delete their R2 attachments  
  // 3. Delete expired messages from D1
  // 4. Delete expired inboxes from D1
  // 5. Prune inactive users (90 days)
}

The frontend: a single Worker response

The entire frontend is a single HTML string returned directly from the Worker. There's no CDN, no separate asset hosting, no build step. Tailwind is loaded from CDN. Font Awesome is loaded from CDN. Everything else is inline.

This is unconventional but works extremely well for a single-page app of this size. Cold start latency is essentially zero because there's nothing to fetch — the Worker returns the full HTML in the first response.

What this costs to run

Cloudflare's pricing for this stack (as of early 2026) on the Workers paid plan ($5/month):

Total cost at moderate traffic: roughly $5–15/month. Compare that to a VPS + managed database + S3 equivalent, which would run $50–100/month minimum.

What doesn't work well

In the spirit of honesty: a few things are harder on this stack than a traditional one.

Large attachment handling is tricky. Workers have a 128MB memory limit and a CPU time limit. A 50MB attachment in a multipart email that needs parsing and R2 upload can push against these limits. The workaround is streaming the attachment directly to R2 rather than buffering it in memory — which postal-mime now supports but requires careful implementation.

Debugging is harder than on a traditional server. wrangler tail gives you real-time logs, but there's no interactive debugger or shell access. Structured logging with console.log becomes important early.

D1 write latency on the free tier is higher than on the paid tier. For a service where email delivery latency matters, the paid Workers plan is worth it purely for the D1 consistency improvements.

Try FlashTemp free

Instant disposable email. No signup, no tracking, auto-deletes in 24h.

Generate free address