Back to Blog
TechnicalDecember 5, 202410 min read

How We Built a Serverless Temp Mail Using Cloudflare Workers (Zero Cost)

A deep dive into building a completely serverless temporary email service using Cloudflare Workers, Email Routing, and KV storage - all within the free tier.

The Challenge

Building a temporary email service sounds simple until you realize you need:

- A mail server to receive emails

  • Storage for those emails
  • An API to serve them
  • A frontend for users
  • All of this needs to scale and be reliable

    Traditional approaches require VPS servers, mail transfer agents like Postfix, databases, and ongoing maintenance. Costs add up quickly.

    We built TmpMail entirely on Cloudflare's edge infrastructure—and the free tier covers everything.

    The Architecture

                        ┌─────────────────┐
  • │ Email Sender │ └────────┬────────┘ │ ┌────────▼────────┐ │ Cloudflare │ │ Email Routing │ └────────┬────────┘ │ ┌────────▼────────┐ │ Email Worker │ │ (Processing) │ └────────┬────────┘ │ ┌────────▼────────┐ │ Cloudflare KV │ │ (Storage) │ └────────┬────────┘ │ ┌──────────────┐ ┌────────▼────────┐ │ Frontend │◄───│ API Worker │ │ (Pages) │ │ (REST API) │ └──────────────┘ └─────────────────┘

    Component 1: Email Routing

    Cloudflare Email Routing lets you receive emails at your domain and route them to Workers. Here's the setup:

    1. Add your domain to Cloudflare

  • Enable Email Routing
  • Create a catch-all rule that sends to a Worker

    The magic is that Cloudflare handles all the MX record complexity and spam filtering.

    Component 2: Email Worker

    When an email arrives, our Worker processes it:

    export default {
  • async email(message: EmailMessage, env: Env) { const to = message.to; const from = message.from;

    // Parse the email content const rawEmail = await new Response(message.raw).text(); const parsed = await parseEmail(rawEmail);

    // Extract verification codes const codes = extractCodes(parsed.text || parsed.html); const links = extractLinks(parsed.html);

    // Store in KV const emailData = { id: crypto.randomUUID(), from, subject: parsed.subject, textContent: parsed.text, htmlContent: sanitizeHtml(parsed.html), extracted: { codes, links }, receivedAt: new Date().toISOString(), };

    await env.EMAILS_KV.put( email:${inboxId}:${emailData.id}, JSON.stringify(emailData), { expirationTtl: 86400 } // 24 hours ); } }

    Component 3: Smart Extraction

    The secret sauce is our code extraction logic. We maintain templates for popular services:

    const templates = {
      'github.com': {
        codePattern: /verification code is: (\d{6})/i,
        linkPattern: /https:\/\/github\.com\/.*verify/g,
      },
      'google.com': {
        codePattern: /(\d{6}) is your Google verification code/,
      },
      // ... dozens more
    };

    function extractCodes(text: string, senderDomain: string): string[] { // Try template-specific extraction first const template = templates[senderDomain]; if (template?.codePattern) { const match = text.match(template.codePattern); if (match) return [match[1]]; }

    // Fall back to generic patterns const genericPatterns = [ /\b(\d{4,8})\b.*(?:code|verify|confirm)/i, /(?:code|verify|confirm).*\b(\d{4,8})\b/i, ];

    for (const pattern of genericPatterns) { const match = text.match(pattern); if (match) return [match[1]]; }

    return []; }

    Component 4: KV Storage

    Cloudflare KV is perfect for this use case:

    - Automatic expiration - Set TTL and emails delete themselves

  • Global distribution - Data is replicated to all edge locations
  • Free tier generosity - 100,000 reads/day, 1,000 writes/day

    Our KV schema:

    inbox:{inboxId}          → InboxMetadata
  • token:{token} → { inboxId, domain } email:{inboxId}:{emailId} → EmailData domains → DomainConfig[]

    Component 5: API Worker

    The REST API is straightforward:

    router.post('/api/inbox', async (req, env) => {
      const inboxId = generateInboxId(); // e.g., "alex1234"
      const token = generateToken(); // 32-char random string
      const email = ${inboxId}@get-a-temp-email.xyz;

    await env.KV.put(inbox:${inboxId}, JSON.stringify({ email, createdAt: new Date().toISOString(), expiresAt: new Date(Date.now() + 86400000).toISOString(), }), { expirationTtl: 86400 });

    await env.KV.put(token:${token}, JSON.stringify({ inboxId, domain: 'get-a-temp-email.xyz', }), { expirationTtl: 86400 });

    return { success: true, data: { token, email } }; });

    The Result

    - Zero server costs - Everything runs on Cloudflare free tier

  • Global edge performance - API responds in <50ms worldwide
  • Automatic scaling - Workers handle any traffic spike
  • Zero maintenance - No servers to patch or update

    Limitations

    It's not all perfect:

    1. KV eventual consistency - Emails might take 1-2 seconds to appear

  • Worker CPU limits - Complex email parsing can hit 10ms limit
  • No email sending - Receive only (which is fine for temp mail)

    Try It Yourself

    The architecture is simple enough that you could build it yourself:

    1. Sign up for Cloudflare (free)

  • Add a domain
  • Enable Email Routing
  • Deploy the Workers

    Or just use TmpMail and let us handle it!

    ---

    *Questions about the architecture? Reach out at admin@get-a-temp-email.xyz*

  • Ready to try TmpMail?

    Get a disposable email address in seconds. No signup required.

    Create Temp Email