Zum Inhalt springen
Integration

Blockieren Sie Einweg-E-Mails in Next.js mit einer Middleware-Datei

| 6 min read

Eine 40-zeilige Next.js-Middleware, die die Botoi-API aufruft, um Anmeldungen von temporären E-Mail-Adressen abzulehnen. Kopieren, einfügen, bereitstellen.

Email inbox interface on a laptop
Photo by Stephen Phillips on Unsplash

Ein Benutzer meldet sich bei an test92847@mailinator.com, verbrennt Ihre kostenlose Testversion und verschwindet. Sie kommen morgen mit zurück test92848@mailinator.com und mach es noch einmal. Ihre Support-Warteschlange füllt sich mit Phantomkonten. Ihre Analysen zeigen überhöhte Benutzerzahlen, die jedoch keine Bedeutung haben. Ihre Missbrauchserkennung wird zu spät ausgelöst, da das Konto bereits Ressourcen verbraucht hat.

Die Lösung: Blockieren Sie Wegwerf-E-Mails an der Tür, bevor die Anmeldeanfrage Ihre Datenbank erreicht. Diese Anleitung zeigt, wie Sie dies in Next.js mit einer einzigen Middleware-Datei und null Abhängigkeiten über einen Abrufaufruf hinaus tun.

Was Sie bauen werden

Eine Next.js-Middleware, die POST-Anfragen an Ihren Anmeldeendpunkt abfängt, Extrahiert die E-Mail aus dem Anfragetext und vergleicht sie mit dem botoi Einweg-E-Mail-API, und gibt eine 422-Antwort zurück, wenn die E-Mail zu einem Wegwerfdienst gehört. Die gesamte Datei umfasst weniger als 50 Zeilen.

Die Middleware

Erstellen middleware.ts im Stammverzeichnis Ihres Projekts (bzw src/middleware.ts wenn Sie das verwenden src Verzeichnis):

import { NextRequest, NextResponse } from 'next/server';

const BOTOI_URL = 'https://api.botoi.com/v1/disposable-email/check';

export async function middleware(req: NextRequest) {
  // Only intercept POST requests to the signup route
  if (req.method !== 'POST') {
    return NextResponse.next();
  }

  let body: { email?: string };
  try {
    body = await req.json();
  } catch {
    return NextResponse.json(
      { error: 'Invalid request body' },
      { status: 400 }
    );
  }

  const email = body.email?.trim().toLowerCase();
  if (!email) {
    return NextResponse.next();
  }

  try {
    const res = await fetch(BOTOI_URL, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ email }),
    });

    const data = await res.json();

    if (data.success && data.data.is_disposable) {
      return NextResponse.json(
        { error: 'Disposable email addresses are not allowed. Please use a permanent email.' },
        { status: 422 }
      );
    }
  } catch {
    // API unreachable; fail open so real users aren't blocked
    console.warn('botoi disposable-email check failed, allowing request through');
  }

  return NextResponse.next();
}

export const config = {
  matcher: ['/api/auth/signup', '/api/register'],
};

Wie es funktioniert

Routenanpassung

Der config.matcher Das Array teilt Next.js mit, welche Routen diese Middleware auslösen. Ändern Sie diese Pfade entsprechend Ihren Anmeldeendpunkten. Die Middleware wird am Edge ausgeführt, bevor Ihr Routenhandler ausgeführt wird. Daher berühren abgelehnte Anfragen niemals Ihre Datenbank oder Ihren Authentifizierungsanbieter.

E-Mail-Extraktion

Die Middleware liest den Anfragetext mit req.json() und zieht die email Feld. Wenn die Analyse fehlschlägt oder keine E-Mail vorhanden ist, wird die Anfrage unverändert weitergeleitet. Dadurch bleibt die Middleware für Nicht-Anmelderouten unsichtbar.

Der API-Aufruf

Ein einzelner POST an https://api.botoi.com/v1/disposable-email/check mit der E-Mail im Textkörper. Die Antwort umfasst:

{
  "success": true,
  "data": {
    "email": "throwaway@mailinator.com",
    "domain": "mailinator.com",
    "is_disposable": true,
    "is_free": false,
    "provider": "Mailinator"
  }
}

Der is_disposable Flagge ist das Tor. Wenn es so ist true, gibt die Middleware eine 422 mit einer eindeutigen Nachricht zurück. Wenn es so ist false, NextResponse.next() Lässt die Anfrage an Ihren Anmelde-Handler weiterleiten.

Fail-Open-Design

Der catch Blockieren um den Abrufaufruf bedeutet, dass Netzwerkausfälle, Zeitüberschreitungen oder API-Ausfälle die Anmeldungen nicht unterbrechen. Die Middleware protokolliert eine Warnung und lässt die Anfrage durch. Ihre Benutzer sehen nie einen Fehler, der durch einen Ausfall eines Drittanbieters verursacht wurde.

Umgang mit Randfällen

Auszeiten

Die Botoi-API antwortet auf die meisten Anfragen in weniger als 50 ms, da sie eine speicherinterne Domänenliste verwendet. Wenn Sie eine harte Auszeit wünschen, schließen Sie den Abruf ein AbortSignal.timeout():

const res = await fetch(BOTOI_URL, {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ email }),
  signal: AbortSignal.timeout(3000), // 3 second timeout
});

Tarifbegrenzungen

Die kostenlose Stufe erlaubt 5 Anfragen pro Minute. Wenn Ihre App mehr Anmeldungen verarbeitet, Holen Sie sich einen API-Schlüssel und übergeben Sie es als Bearer-Token:

const res = await fetch(BOTOI_URL, {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': \`Bearer \${process.env.BOTOI_API_KEY}\`,
  },
  body: JSON.stringify({ email }),
});

Speichern BOTOI_API_KEY in deinem .env.local Datei. Übergeben Sie es niemals der Versionskontrolle.

Doppelte Schecks

Wenn dieselbe E-Mail Ihren Anmeldeendpunkt zweimal schnell hintereinander erreicht (Doppelklick, Wiederholungslogik), Sie führen zwei API-Aufrufe für dieselbe Domäne durch. Für die meisten Apps ist das in Ordnung. Wenn es wichtig ist, fügen Sie einen kurzlebigen Cache hinzu (siehe unten).

Produktionshärten

Fügen Sie einen In-Memory-Cache hinzu

Zwischenspeichern Sie das Ergebnis der Einwegprüfung pro Domain 5 Minuten lang. Dadurch werden API-Aufrufe reduziert und wiederholte Prüfungen für dieselbe Domain beschleunigt:

const cache = new Map<string, { isDisposable: boolean; expires: number }>();
const CACHE_TTL = 5 * 60 * 1000; // 5 minutes

async function isDisposableEmail(email: string): Promise<boolean> {
  const domain = email.split('@')[1];
  const cached = cache.get(domain);

  if (cached && cached.expires > Date.now()) {
    return cached.isDisposable;
  }

  try {
    const res = await fetch(BOTOI_URL, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ email }),
      signal: AbortSignal.timeout(3000),
    });

    const data = await res.json();
    const isDisposable = data.success && data.data.is_disposable;

    cache.set(domain, {
      isDisposable,
      expires: Date.now() + CACHE_TTL,
    });

    return isDisposable;
  } catch {
    return false; // fail open
  }
}

Dieser kartenbasierte Cache funktioniert in serverlosen und Edge-Laufzeiten. Tauschen Sie es für Bereitstellungen mit mehreren Instanzen gegen Redis oder Upstash aus:

import { Redis } from '@upstash/redis';

const redis = Redis.fromEnv();

async function isDisposableEmail(email: string): Promise<boolean> {
  const domain = email.split('@')[1];
  const cached = await redis.get<boolean>(\`disposable:\${domain}\`);

  if (cached !== null) {
    return cached;
  }

  try {
    const res = await fetch(BOTOI_URL, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ email }),
      signal: AbortSignal.timeout(3000),
    });

    const data = await res.json();
    const isDisposable = data.success && data.data.is_disposable;

    await redis.set(\`disposable:\${domain}\`, isDisposable, { ex: 300 });

    return isDisposable;
  } catch {
    return false;
  }
}

Unternehmensdomänen auf die Zulassungsliste setzen

Einige Unternehmen verwenden benutzerdefinierte Domänen, die Sie niemals blockieren möchten, selbst wenn sie einem verdächtigen Muster entsprechen. Fügen Sie eine Zulassungsliste hinzu:

const ALLOWED_DOMAINS = new Set([
  'yourcompany.com',
  'partner-corp.io',
  'bigclient.co',
]);

async function isDisposableEmail(email: string): Promise<boolean> {
  const domain = email.split('@')[1];

  if (ALLOWED_DOMAINS.has(domain)) {
    return false;
  }

  // ... rest of the check logic
}

Blockierte Versuche protokollieren

Verfolgen Sie, welche Domains abgelehnt werden, damit Sie Missbrauchsmuster überwachen und Ihre Strategie anpassen können:

if (data.success && data.data.is_disposable) {
  console.log(
    JSON.stringify({
      event: 'disposable_email_blocked',
      domain: data.data.domain,
      provider: data.data.provider,
      timestamp: new Date().toISOString(),
    })
  );

  return NextResponse.json(
    { error: 'Disposable email addresses are not allowed.' },
    { status: 422 }
  );
}

Komplette Middleware mit sämtlicher Härtung

Hier ist die vollständige Datei mit Caching, Zulassungsliste, Zeitüberschreitung und strukturierter Protokollierung:

import { NextRequest, NextResponse } from 'next/server';

const BOTOI_URL = 'https://api.botoi.com/v1/disposable-email/check';
const CACHE_TTL = 5 * 60 * 1000;

const ALLOWED_DOMAINS = new Set([
  // Add your corporate or partner domains here
]);

const cache = new Map<string, { isDisposable: boolean; expires: number }>();

async function checkDisposable(email: string): Promise<{
  isDisposable: boolean;
  domain: string;
  provider: string | null;
}> {
  const domain = email.split('@')[1];

  if (ALLOWED_DOMAINS.has(domain)) {
    return { isDisposable: false, domain, provider: null };
  }

  const cached = cache.get(domain);
  if (cached && cached.expires > Date.now()) {
    return { isDisposable: cached.isDisposable, domain, provider: null };
  }

  try {
    const res = await fetch(BOTOI_URL, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ email }),
      signal: AbortSignal.timeout(3000),
    });

    const data = await res.json();
    const isDisposable = data.success && data.data.is_disposable;
    const provider = data.data?.provider ?? null;

    cache.set(domain, { isDisposable, expires: Date.now() + CACHE_TTL });

    return { isDisposable, domain, provider };
  } catch {
    return { isDisposable: false, domain, provider: null };
  }
}

export async function middleware(req: NextRequest) {
  if (req.method !== 'POST') {
    return NextResponse.next();
  }

  let body: { email?: string };
  try {
    body = await req.json();
  } catch {
    return NextResponse.json(
      { error: 'Invalid request body' },
      { status: 400 }
    );
  }

  const email = body.email?.trim().toLowerCase();
  if (!email || !email.includes('@')) {
    return NextResponse.next();
  }

  const result = await checkDisposable(email);

  if (result.isDisposable) {
    console.log(
      JSON.stringify({
        event: 'disposable_email_blocked',
        domain: result.domain,
        provider: result.provider,
        timestamp: new Date().toISOString(),
      })
    );

    return NextResponse.json(
      { error: 'Disposable email addresses are not allowed. Please use a permanent email.' },
      { status: 422 }
    );
  }

  return NextResponse.next();
}

export const config = {
  matcher: ['/api/auth/signup', '/api/register'],
};

Teste es vor Ort

Starten Sie Ihren Next.js-Entwicklungsserver und senden Sie eine Anfrage mit einer bekannten, verfügbaren E-Mail-Adresse:

curl -X POST http://localhost:3000/api/auth/signup \\
  -H "Content-Type: application/json" \\
  -d '{"email": "test@mailinator.com", "password": "hunter2"}'

Erwartete Antwort:

{
  "error": "Disposable email addresses are not allowed. Please use a permanent email."
}

Versuchen Sie es mit einer legitimen E-Mail, um zu bestätigen, dass sie durchgeht:

curl -X POST http://localhost:3000/api/auth/signup \\
  -H "Content-Type: application/json" \\
  -d '{"email": "dev@acme-corp.com", "password": "hunter2"}'

Diese Anfrage erreicht Ihren Anmelde-Handler wie gewohnt.

Wann sollte auch der Kunde überprüft werden?

Die Middleware fängt Wegwerf-E-Mails auf dem Server ab. Sie können dieselbe API aber auch über Ihr Anmeldeformular aufrufen um einen Inline-Fehler anzuzeigen, *bevor* der Benutzer abschickt. Eine schnelle clientseitige Überprüfung, nachdem das E-Mail-Feld den Fokus verliert erspart dem Benutzer einen Hin- und Rückweg und gibt schnelleres Feedback:

async function validateEmail(email: string): Promise<string | null> {
  const res = await fetch('https://api.botoi.com/v1/disposable-email/check', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ email }),
  });

  const data = await res.json();

  if (data.success && data.data.is_disposable) {
    return 'Please use a permanent email address.';
  }

  return null; // no error
}

Die Middleware fungiert weiterhin als maßgebliches Tor. Die clientseitige Prüfung ist eine UX-Verbesserung, keine Sicherheitsmaßnahme.

FAQ

Funktioniert das mit dem Next.js App Router?
Ja. Die Next.js-Middleware wird vor jedem Route-Handler ausgeführt, unabhängig davon, ob Sie den App Router oder den Pages Router verwenden. Die Datei middleware.ts befindet sich im Projektstamm (oder in src/, wenn Sie das Verzeichnis src verwenden) und fängt Anfragen an die übereinstimmenden Pfade ab, bevor sie Ihre API-Routen oder Serveraktionen erreichen.
Benötige ich einen API-Schlüssel für den Botoi-Einweg-E-Mail-Check?
Nein. Das kostenlose Kontingent erlaubt 5 Anfragen pro Minute ohne API-Schlüssel. Für Produktions-Apps, die mehr Anmeldungen verarbeiten, holen Sie sich einen Schlüssel von der Botoi-API-Dokumentenseite, um höhere Ratenlimits freizuschalten.
Was passiert, wenn die Botoi-API ausfällt?
Die Middleware fängt Netzwerkfehler ab und lässt die Anfrage durch. Dieser Fail-Open-Ansatz bedeutet, dass ein vorübergehender API-Ausfall legitime Benutzer niemals daran hindert, sich anzumelden. Sie können eine Protokollierung hinzufügen, um zu verfolgen, wann ein Fallback-Verhalten einsetzt.
Kann ich dies mit anderen Frameworks wie Remix oder SvelteKit verwenden?
Der API-Aufruf selbst funktioniert in jeder serverseitigen Umgebung. Das hier gezeigte Middleware-Muster ist Next.js-spezifisch, aber die Kernlogik (POST an den Endpunkt, Überprüfung von is_disposable in der Antwort) wird direkt in Remix-Loader, SvelteKit-Hooks oder Express-Middleware übersetzt.
Wie genau ist die Erkennung von Wegwerf-E-Mails?
Der Endpunkt vergleicht eine Liste mit mehr als 700 bekannten verfügbaren Domänen und verwendet Mustervergleiche, um Variationen zu erkennen. Es identifiziert außerdem kostenlose E-Mail-Anbieter (Gmail, Outlook, Yahoo) getrennt von Einweg-E-Mail-Anbietern, sodass Sie zwischen einer persönlichen Gmail- und einer Wegwerf-Mailinator-Adresse unterscheiden können.

Starte mit botoi zu entwickeln

150+ API-Endpunkte für Abfragen, Textverarbeitung, Bildgenerierung und Entwickler-Tools. Kostenloser Tarif, keine Kreditkarte nötig.