# How to Block Disposable Emails in Supabase, Clerk & Next.js Auth (Stop Free Trial Abuse)

> Step-by-step developer guide to blocking 100,000+ disposable burner email domains and Gmail alias multi-account abuse in Supabase Auth Hooks, Clerk, and Next.js Server Actions.

- **Author**: Alex, Co-Founder
- **Published**: 2026-07-23 (Updated: 2026-10-30)
- **Source**: https://mailveri.com/blog/how-to-block-disposable-emails-supabase-clerk-nextjs
- **AI Reference**: https://mailveri.com/llms-full.txt

---

Modern authentication platforms like **Supabase Auth**, **Clerk**, and **Auth.js (NextAuth)** make it effortless to ship magic links, OAuth, and OTP registration in a single afternoon. Yet the moment you launch a SaaS product with a generous free tier or starter AI credits, you discover a glaring security gap: **none of these auth providers stop sophisticated burner domains or Gmail alias trial farming out of the box.**

Whether you are granting $5 in OpenAI / Anthropic tokens, 100 free scraper runs, or a 14-day pro trial without requiring a credit card, bad actors automate account creation using fresh disposable domains and sub-addressed webmail aliases. Left unchecked, fake signups drain your cloud compute budget, pollute your PostgreSQL user tables, and trigger hard bounces that get your transactional email provider (Resend, Postmark, or AWS SES) suspended.

In this implementation guide, we compare what Supabase Auth and Clerk block natively versus what slips through, and provide copy-pasteable production code to block **100,000+ disposable email providers** and deduplicate Gmail aliases using [MailVeri’s real-time verification API](https://mailveri.com/docs/api).

## 1. Why Default Auth Provider Settings Fail Against Modern Trial Abuse

Developers often assume that turning on email confirmation links (OTP) or flipping a basic “Block disposable emails” switch in their auth dashboard solves fake signups. In practice, three attack vectors bypass standard auth configurations completely:

- **Ephemeral Burner Domains Receive OTP Codes Instantly:** Services like Temp-Mail, Guerrilla Mail, Mohmal, and 10MinuteMail provide live, functional web inboxes. When Supabase or Clerk sends a 6-digit verification code, the user copies the OTP from the temporary inbox in 3 seconds and activates the account.
- **Custom Domain Rotation (100,000+ Active Burners):** Open-source static blocklists and basic auth toggles only track 3,000 to 5,000 legacy domains. Modern disposable mail networks register hundreds of fresh `.xyz`, `.top`, `.site`, and `.cfd` domains every week pointed at wildcard MX clusters.
- **Gmail Dot & Plus-Tag Multi-Account Farming:** Even if you block every disposable domain on earth, an abuser with a single `alexsmith@gmail.com` inbox can register `alex.smith@gmail.com`, `a.lex.smith@gmail.com`, and `alexsmith+trial1@gmail.com`. Standard auth providers treat each string as a brand-new user.

| Protection Layer | Supabase Auth (Default) | Clerk (Built-In Toggle) | Auth Hook + MailVeri API |
| --- | --- | --- | --- |
| Known Legacy Burners (e.g. Mailinator) | Allowed unless custom hook added | Blocked (Paid plan add-on) | Blocked (100,000+ domains) |
| Rotating MX & Fresh Burner Domains | Not detected | Slips past static blocklist | Blocked via live MX & threat intel |
| Dead / Typo Domains (Bounce Protection) | Sends OTP → Hard Bounce | Sends OTP → Hard Bounce | Blocked before OTP email is sent |
| Gmail/ProtonMail Dot & Plus Normalization | No (Treated as distinct users) | Partial (+ subaddress toggle only) | Full canonical address returned (mail) |
| Cost per Verification | $0No protection | Requires Clerk Pro + Security Add-on | $0.0002 – $0.0009Lifetime credits |

## 2. Integration #1: Blocking Disposable Emails in Supabase Auth (Before User Created Hook)

The cleanest place to stop disposable and invalid emails in **Supabase** is the **Before User Created Auth Hook**. Because this hook executes synchronously *before* Supabase inserts a record into `auth.users` or dispatches a confirmation email via Resend/SendGrid, rejecting a burner email here costs zero transactional email quota and leaves zero junk rows in your database.

### Step-by-Step Supabase Setup:

1. Create a new Supabase Edge Function: `supabase functions new before-user-created`.
2. Set your MailVeri API key in Supabase Vault/Secrets: `supabase secrets set MAILVERI_API_KEY=your_api_key`.
3. Deploy the Edge Function below and register it in your Supabase Dashboard under **Authentication → Hooks (Beta) → Before User Created**.

```typescript
// supabase/functions/before-user-created/index.ts
// Supabase Auth Hook: Block disposable & invalid emails before creating auth.users row
import { serve } from "https://deno.land/std@0.177.0/http/server.ts";

const MAILVERI_API_KEY = Deno.env.get("MAILVERI_API_KEY")!;

serve(async (req: Request) => {
  const payload = await req.json();
  const rawEmail: string | undefined = payload?.user?.email;

  if (!rawEmail) {
    return new Response(
      JSON.stringify({
        error: { http_code: 400, message: "Email address is required." },
      }),
      { status: 400, headers: { "Content-Type": "application/json" } }
    );
  }

  const verifyRes = await fetch("https://api.mailveri.com/v1/verify-mail/", {
    method: "POST",
    headers: {
      "Authorization": `Bearer ${MAILVERI_API_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ email: rawEmail }),
  });

  const result = await verifyRes.json();

  // Reject disposable burner domains, invalid addresses, and over-quota inboxes
  if (
    result.status === "DISPOSABLE" ||
    result.status?.startsWith("INVALID") ||
    result.status === "FULL_INBOX"
  ) {
    return new Response(
      JSON.stringify({
        error: {
          http_code: 403,
          message: "Please sign up with an active work or personal email address. Temporary email providers are not allowed.",
        },
      }),
      { status: 403, headers: { "Content-Type": "application/json" } }
    );
  }

  // Allow signup to proceed in Supabase Auth
  return new Response(JSON.stringify({}), {
    status: 200,
    headers: { "Content-Type": "application/json" },
  });
});
```

When a user attempts to sign up with `temp99@guerrillamail.com` or a mistyped address like `founder@gmai.com`, MailVeri returns `DISPOSABLE` or `INVALID-MX`, and the Supabase Auth Hook immediately returns a clean `403` error message to your frontend signup form before any confirmation email is ever sent.

## 3. Integration #2: Blocking Burner Emails & Alias Farming in Clerk + Next.js App Router

If your application uses **Clerk** with Next.js 15 App Router, you can run a pre-signup Server Action (either in a custom Clerk `useSignUp()` flow or right before provisioning free-trial credits in your onboarding step) that validates the email with MailVeri and checks the canonical address (`data.mail`) against your database.

```typescript
// app/actions/verify-before-clerk-signup.ts
'use server';

export interface SignupPrecheckResult {
  allowed: boolean;
  canonicalEmail?: string;
  error?: string;
}

export async function verifyEmailBeforeClerkSignup(
  rawEmail: string
): Promise<SignupPrecheckResult> {
  const response = await fetch('https://api.mailveri.com/v1/verify-mail/', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${process.env.MAILVERI_API_KEY}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({ email: rawEmail }),
    cache: 'no-store',
  });

  const data = await response.json();

  if (data.status === 'DISPOSABLE') {
    return {
      allowed: false,
      error: 'Disposable and temporary email addresses are not permitted on free trials.',
    };
  }

  if (data.status?.startsWith('INVALID') || data.status === 'FULL_INBOX') {
    return {
      allowed: false,
      error: 'This email address cannot receive messages. Please check for typos.',
    };
  }

  // data.mail returns the canonical normalized email (stripping Gmail dots & +tags)
  // Check your database to ensure this canonical identity hasn't already claimed a trial
  return {
    allowed: true,
    canonicalEmail: data.mail || rawEmail.toLowerCase().trim(),
  };
}
```

### How Canonical Email Normalization Stops Multi-Account Trial Abuse

Notice the `canonicalEmail: data.mail` field in the snippet above. Different mailbox providers handle dots (`.`) and plus tags (`+`) differently:

- **Gmail & ProtonMail:** Ignore dots in the local-part completely and strip everything after `+`. Thus `a.l.e.x+free1@gmail.com` and `alex@gmail.com` route to the exact same inbox. MailVeri returns `"mail": "alex@gmail.com"` for both.
- **Microsoft Outlook / Hotmail & iCloud:** Strip `+` sub-addressing tags, but treat dots as distinct accounts. MailVeri preserves significant dots while stripping the `+` suffix so you never falsely block a legitimate Outlook user.

By saving a `UNIQUE` index on `canonical_email` in your PostgreSQL `users` or `trial_grants` table, you guarantee that one physical inbox can only claim one free trial—permanently closing the Gmail alias loophole described in our [SaaS Disposable Email & Free Trial Protection Guide](https://mailveri.com/blog/stop-disposable-emails-draining-ai-saas-credits) and [SaaS Signup Use Case Overview](https://mailveri.com/industries/saas).

## 4. Fail-Open Resilience & Zero-Waste Credit Economics

Whenever you place an external API check inside an authentication flow, two engineering rules are mandatory:

1. **Enforce a 3-Second Timeout with Fail-Open Fallback:** Wrap your fetch call in an `AbortSignal.timeout(3000)`. If a transient network hiccup occurs, allow the signup to proceed and queue an asynchronous background check so legitimate users are never locked out.
2. **Never Pay for Inconclusive Checks:** If a corporate mail server temporarily defers verification with a 4xx greylisting code, MailVeri returns `UNKNOWN` and **charges 0 credits**. With pay-as-you-go packages starting at **$9 for 10,000 lifetime credits** ($59 for 100K, down to $0.0002/check), protecting 10,000 signups costs less than a single abused AI trial account.

Ready to secure your authentication pipeline? Explore our [AI Startups & SaaS Solutions](https://mailveri.com/industries/ai-startups), review how burner domains compare to departmental aliases in [Disposable vs. Role-Based Emails Explained](https://mailveri.com/blog/disposable-vs-role-based-emails), or [grab your free API key with 1,000 credits](https://app.mailveri.com/accounts/signup/) to test your Supabase or Clerk hook today.

## Frequently Asked Questions

### Why don't default Supabase or Clerk email settings stop free trial abuse?

Confirmation links only prove a user can read an inbox—burners from Temp-Mail, Guerrilla Mail, custom disposable domains, and Gmail +tag or dot aliases still receive OTP codes effortlessly while draining free AI tokens and trial quotas.

### Where should disposable email checks run in Supabase Auth and Clerk?

In Supabase, attach a Before User Created Auth Hook that calls MailVeri's real-time API and rejects DISPOSABLE or INVALID addresses before row creation; in Clerk + Next.js App Router, validate inside a pre-signup Server Action or route handler with a fail-open timeout.

### How much does blocking disposable signups cost per 10,000 registrations?

At $9 for 10,000 non-expiring credits ($0.0009 per signup, scaling down to $0.0002 at volume), blocking a single abusive AI trial signup saves $1 to $5 in wasted LLM/GPU inference costs while starting with 1,000 free trial credits.
