Back to Blog
Web Development

How to Stop Contact Form Spam on Your Website

How do you stop spam on your website contact form? Honeypot fields, timing checks, rate limiting, CAPTCHA options, server-side validation and filtering without losing real enquiries.

Form SpamWeb GüvenliğiCAPTCHAWeb Geliştirme

Spam on a website form is stopped not by a single method but by layers: an invisible trap field (honeypot), a timing check that measures how fast the form was filled in, rate limiting on submissions from the same address, and a CAPTCHA that comes in only when those are not enough. All of the rules are enforced on the server, not in the browser, because bots often post straight to the submission endpoint without ever opening the page. The goal is not zero spam but a readable inbox without losing a single real customer enquiry.

Why form spam is a business problem, not just a security one

Form spam is often brushed off as "annoying", but its real cost lies elsewhere. A team whose inbox fills with dozens of ad and link messages a day stops reading form notifications after a few weeks, and a real quote request gets lost in the pile. And if the form sends an automatic "we received your request" email, bots enter other people’s addresses and turn your server into a spam sender, damaging your domain’s email reputation; the effect on business email delivery is that your own quotes land in spam folders.

A spam filter is measured not by how many messages it blocks but by how often it blocks a real enquiry by mistake; blocked submissions should never be silently deleted, but kept in a separate folder.

Layered protection: what each method stops

  • Honeypot field: a field hidden from human users that bots fill in automatically. If it arrives filled, the submission is treated as spam. It adds no extra step for users and stops most simple bots.
  • Timing check: a timestamp is issued when the form page loads; a submission sent within a few seconds, or from a form opened days ago, is suspicious. The timestamp is signed on the server so it cannot be forged.
  • Rate limiting: the number of submissions from the same IP or email address in a short window is capped. It cuts off bot waves and abuse of the auto-reply email.
  • Content rules: many links in the message, an entirely different alphabet from the form’s language, or known spam patterns raise the score. This layer is used to sort into a suspicious folder, not to block.
  • CAPTCHA: added when the earlier layers are not enough. Invisible or single-click challenges are far less tiring for real users than the old pick-the-images type.

The order is deliberate: the layers that burden the user least come first. Starting with a CAPTCHA moves the problem onto the user, and on mobile especially every extra step lowers form completion. That is why in high-converting landing page designs the CAPTCHA stays a last resort.

A server-side check example

The example below applies a honeypot, a signed timestamp and rate limiting together in a Next.js route handler. A suspicious submission does not return an error to the client; the bot gets a "success" response while the record is stored as suspicious. Returning an error to a bot makes it easier for it to learn which rule it tripped and adjust.

const MIN_FILL_MS = 3_000;
const MAX_AGE_MS = 2 * 60 * 60 * 1000;

export async function POST(req: Request) {
  const form = await req.formData();
  const ip = req.headers.get('x-forwarded-for')?.split(',')[0] ?? 'unknown';

  if (!(await rateLimit.allow('contact:' + ip, { max: 5, windowSec: 600 }))) {
    return Response.json({ ok: false }, { status: 429 });
  }

  const reasons: string[] = [];
  if (form.get('website')) reasons.push('honeypot'); // hidden field

  const issuedAt = verifySignedTimestamp(String(form.get('ts') ?? ''));
  const age = issuedAt ? Date.now() - issuedAt : -1;
  if (age < MIN_FILL_MS || age > MAX_AGE_MS) reasons.push('timing');

  const message = String(form.get('message') ?? '');
  if ((message.match(/https?:\/\//g) ?? []).length > 2) reasons.push('links');

  await saveSubmission({ form, ip, suspicious: reasons.length > 0, reasons });
  if (reasons.length === 0) await notifySalesTeam(form);

  // Same response for bots: do not reveal which rule was hit
  return Response.json({ ok: true });
}

Three details matter here. When the rate limit is exceeded an explicit 429 is returned, because a real user in that situation should be able to retry a minute later. A suspicious record is not deleted; only the notification to the sales team is skipped, and a weekly look at the suspicious folder catches enquiries blocked by mistake. The hidden field is deliberately named something bots like to fill, such as "website"; it must also be hidden from the accessibility tree so screen readers do not announce it, otherwise a visually impaired user fills it in and gets flagged as spam. This is the same hidden-content rule covered in our web accessibility article.

Not just the contact form: which forms need protection?

Spam usually brings the contact form to mind, but bots often target the other forms on a site. Each form is abused in a different way, so protection is tuned per form; applying one rule with the same threshold across the whole site either wears out real users or misses the real gap.

  • Newsletter and sign-up forms: bots enter other people’s email addresses and you send those people a confirmation email they never asked for. Double opt-in (the subscription is not active until the link in the email is clicked) keeps both spam and invalid addresses off your list.
  • Password reset and verification-code forms: every submission costs an email or SMS. Rate limits here also apply per address and phone number; if a code can be sent to the same number every minute, the bill and the user complaints arrive together.
  • Quote and file-upload forms: file type and size are checked on the server, and uploaded files are never served from a public address. Otherwise the form becomes storage for other people’s malicious files.
  • Comment and review fields: submissions containing links go to moderation before publication; spam links appearing on the page send a bad signal to visitors and search engines alike.

A practical method is to ask, for each form, "what does it cost us if this is abused?". If the answer is only a messy inbox, a honeypot and timing check are enough; if the answer is an SMS bill, email reputation or links published on the page, rate limits and an approval step become mandatory. Done once and written into the site’s maintenance notes, this review also prevents the same protection from being forgotten when a new form is added.

Common mistakes

  • Checking only in the browser: JavaScript validation does not exist for a bot, which calls the endpoint directly.
  • Including the message text in the auto-reply sent to the user: bots use that email to carry ads to other people.
  • Permanently deleting suspicious submissions: you will never see a real enquiry that was blocked by mistake.
  • Putting the whole load on a single CAPTCHA: human-powered services that send targeted spam get past CAPTCHAs; rate limits and content rules are still needed.
  • Keeping form data longer than needed or storing IP addresses indefinitely: these are personal data, and the retention period should be set under your data protection compliance.

Form protection is a small but visible part of overall site security; the same rate-limiting and server-side validation principles apply to sign-in screens and search boxes. We covered the broader picture in our website security article. If you track form submissions as conversions, count only non-suspicious submissions as events; otherwise your Google Analytics 4 report shows a spam wave as campaign success.

Conclusion

The way to stop form spam is not a single tool but a layered setup enforced on the server: a honeypot and timing check stop simple bots without burdening users, rate limiting cuts off waves, a CAPTCHA is added only when needed, and suspicious submissions are kept aside rather than deleted. If you want to protect your site’s forms without losing real enquiries, take a look at our web development service or request a quote.

Let's Build Your Project

Get a free consultation for your website, mobile app, or corporate software project.

Get a Free QuoteExplore our Web Development service