Zum Hauptinhalt springen
tsecurity.de LIVE
Echtzeit-Radar & Feeds
Alle RSS Feeds
👥 Community & Social
Sichere ProgrammierungRate Limiting: The Traffic Cop Your API Needs(20.09.2026 um 14:51 Uhr)
Sichere ProgrammierungI Built the MVP First. Then I Wrote the README.(20.09.2026 um 14:51 Uhr)
Sichere ProgrammierungHow to add a loading screen with a progress bar in Godot 4(20.09.2026 um 14:52 Uhr)
Sichere ProgrammierungCanada is about to break your scheduler(20.09.2026 um 14:59 Uhr)
Sichere ProgrammierungWhat Does an AI Automation Agency Actually Do?(20.09.2026 um 15:00 Uhr)
Sichere ProgrammierungRate Limiting: The Traffic Cop Your API Needs(20.09.2026 um 14:51 Uhr)
Sichere ProgrammierungI Built the MVP First. Then I Wrote the README.(20.09.2026 um 14:51 Uhr)
Sichere ProgrammierungHow to add a loading screen with a progress bar in Godot 4(20.09.2026 um 14:52 Uhr)
Sichere ProgrammierungCanada is about to break your scheduler(20.09.2026 um 14:59 Uhr)
Sichere ProgrammierungWhat Does an AI Automation Agency Actually Do?(20.09.2026 um 15:00 Uhr)
Intelligence View
⚡ tsecurity.de Intelligence

When My Contact Form Got 300+ Spam Messages in 2 Seconds (And How I Fixed It)

Reagiere als Erste:r — dein Feedback zählt!

My Portfolio Contact Form Got Hit by 400 Spam Emails in 3 Minutes

A few days ago I learned an uncomfortable lesson about leaving public forms unprotected on the internet.

My portfolio contact form got absolutely destroyed by bots.

Not exaggerating — ~400 spam emails in about 3 minutes.

My inbox looked like a denial-of-service attack, but for email. Every message was some variation of:

"Johndoe..."
"Hello..."

You know the type.

At first I thought: "Okay… maybe just a few bots."

Then the notifications kept coming.

So I did what every developer eventually does — I built a fix.

Why Does This Happen?

Any public form on the internet will eventually be targeted. Bots continuously crawl the web looking for:

  • Contact forms
  • Email endpoints
  • Comment sections
  • Signup pages

If your form has no protection between the user and your email server, you're essentially leaving the front door wide open.

The fix? Layer multiple lightweight protections. No single check is bulletproof, but together they make your form extremely hard to abuse.

Layer 1 — Honeypot 🍯

A honeypot is a hidden form field that real users never see or interact with. Bots, however, tend to fill out every field they find — so if it has a value, you know it's a bot.

Frontend (React/JSX):

<input
  type="text"
  name="honeypot"
  style={{ display: "none" }}
  tabIndex={-1}
  autoComplete="off"
/>

Backend check:

export function isBot(honeypot: unknown): boolean {
  return typeof honeypot === "string" && honeypot.trim().length > 0;
}

The trick: if a bot triggers it, return a fake success instead of an error. Bots retry on errors — silence keeps them away.

if (isBot(honeypot)) {
  // Don't tell the bot it failed
  return Response.json({ success: true }, { status: 200 });
}

Layer 2 — Rate Limiting 🚦

Even if a bot slips past the honeypot, it might hammer your endpoint with dozens of requests per second. Rate limiting caps how many requests a single IP can make in a given window.

const rateLimitMap = new Map<string, { count: number; resetAt: number }>();

export function isRateLimited(
  ip: string,
  maxRequests = 3,
  windowMs = 60_000, // 1 minute
): boolean {
  const now = Date.now();
  const entry = rateLimitMap.get(ip);

  if (!entry || now > entry.resetAt) {
    rateLimitMap.set(ip, { count: 1, resetAt: now + windowMs });
    return false;
  }

  if (entry.count >= maxRequests) return true;

  entry.count++;
  return false;
}

This allows 3 requests per IP per minute. If exceeded, return a 429:

if (isRateLimited(ip)) {
  return Response.json(
    { success: false, error: "Too many requests. Please wait a minute." },
    { status: 429 }
  );
}

⚠️ Note: This in-memory approach resets on server restart. For production, use a distributed store like Redis or Upstash.

Layer 3 — Google reCAPTCHA 🤖

The final gate. Even if a bot bypasses the honeypot and rotates IPs to beat rate limiting, it still needs to pass Google's reCAPTCHA verification.

Frontend — load the reCAPTCHA script and get a token on submit:

// Add to your <head>
<script src="https://www.google.com/recaptcha/api.js?render=YOUR_SITE_KEY" />

// In your submit handler
const token = await grecaptcha.execute("YOUR_SITE_KEY", { action: "contact" });

// Send token with your form data
await fetch("/api/contact", {
  method: "POST",
  body: JSON.stringify({ name, email, message, honeypot, recaptchaToken: token }),
});

Backend — verify the token with Google:

export async function verifyRecaptcha(token: string): Promise<boolean> {
  if (!token) return false;

  const res = await fetch(
    `https://www.google.com/recaptcha/api/siteverify?secret=${process.env.RECAPTCHA_SECRET_KEY}&response=${token}`,
    { method: "POST" }
  );

  const data = await res.json();
  return data.success === true;
}
const isHuman = await verifyRecaptcha(recaptchaToken);

if (!isHuman) {
  return Response.json(
    { success: false, error: "reCAPTCHA verification failed." },
    { status: 400 }
  );
}

Putting It All Together (Next.js API Route)

export async function POST(req: NextRequest) {
  // 1. Get IP
  const ip =
    req.headers.get("x-forwarded-for")?.split(",")[0]?.trim() ?? "unknown";

  // 2. Rate limit check
  if (isRateLimited(ip)) {
    return Response.json(
      { success: false, error: "Too many requests." },
      { status: 429 }
    );
  }

  const { name, email, message, honeypot, recaptchaToken } = await req.json();

  // 3. Honeypot check
  if (isBot(honeypot)) {
    return Response.json({ success: true }); // Silent drop
  }

  // 4. reCAPTCHA check
  const isHuman = await verifyRecaptcha(recaptchaToken);
  if (!isHuman) {
    return Response.json(
      { success: false, error: "reCAPTCHA verification failed." },
      { status: 400 }
    );
  }

  // ✅ Safe to send email
}

A request only reaches your email logic if it passes all three checks.

The Result

Before 400 spam emails in ~3 minutes
After 0 spam emails

Not one since.

Going Further in Production

This setup is lightweight and perfect for a portfolio. If you need more:

  • Redis / Upstash — distributed rate limiting that survives server restarts
  • Cloudflare Turnstile — a privacy-friendlier CAPTCHA alternative
  • Arcjet — modern security layer with bot detection built in
  • Vercel KV — great if you're already in the Vercel ecosystem

The Takeaway

Security doesn't need to be complicated.

Layer Purpose
Honeypot Catches dumb bots instantly
Rate Limiting Stops flooding & repeat attempts
reCAPTCHA Verifies a real human is submitting

Stack them together and your contact form becomes very hard to abuse — with zero impact on real users.

Have you dealt with bot spam on your own projects? What approach did you take? Drop it in the comments.

Ähnliche Beiträge
🔍 Verwandte News

Auch interessante Nachrichten When My Contact Form Got 300+ Spam Messages in 2 Seconds (And How I Fixed It)

Thematisch verwandte Begriffe: When, Contact, Form, Spam · 6 Treffer

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Zum Aktualisieren ziehen
ZERO-DAY CVE-2026-93956 | A flaw has been found in olivier-ls PHP-FTS up to 1.1.2. Affected by thi…
Advisory →
TTS Reader • tsecurity.de Voice
tsecurity.de Icon
tsecurity.de App
Offline-Lesen, Eilmeldungen & 0ms Ladezeit

Installiere tsecurity.de direkt auf deinen Home-Bildschirm für das ultimative Vollbild-Magazinerlebnis ohne Browser-Leisten.

Nächster Beitrag
Themen-Radar & Intelligence Matrix
Echtzeit-Taxonomie nach Angriffsvektoren & Plattformen
Community Radar & Live Chat
Sentinel Bot online • Live-Stream
Dein Cluster: Security Explorer
Match:
lädt…
Verbindung zum Community-Stream wird aufgebaut...
Bearbeitungsmodus — Senden überschreibt deine Nachricht
Community-Puls — was gerade passiert
lädt…
Aktivitäten deiner Analysten
lädt…
Neues Thema oder Eilmeldung einreichen

Reiche interessante Links, Zero-Days oder Debatten ein. Die Community entscheidet per Upvote über die Veröffentlichung.

Heiß diskutierte Einreichungen
🔖 Gespeicherte Artikel
📂 Keine gespeicherten Artikel vorhanden.
Zurück Ziehen Vor
Links: vorheriger Artikel Rechts: nächster Artikel unten: schließen
News NIS-2 Frühwarnung Tier-1 Intel ⏱️ 3 Min vor 10 Min
Artikeldaten werden geladen...

Zurück: vorheriger Vor: nächster
↗ Original-Quelle
Social Reaktionen Deine Reaktion zählt
Einstufung & Relevanz-Poll 0 Stimmen
In sozialen Netzwerken teilen 1-Klick