# Contact form security: spam, bots and data leaks

> Contact form security in practice: how to stop spam and bots, block injection and abuse, and keep personal data from leaking, without hurting real leads.

Canonical page: https://kyvrel.com/blog/contact-form-security
Published: 2026-09-16 · Updated: 2026-10-07 · 6 min read · Kyvrel

Contact form security means three jobs: keep bots out, stop the form from being abused, and protect the personal data people send you. No single trick does all three. A honeypot, server-side validation, rate limiting and careful handling of where submissions go will stop most problems, while real leads never notice.

The contact form is often the only part of a marketing site that accepts input from strangers. That makes it the most exposed surface you have, and the one most likely to fill your inbox with junk. This guide walks through the layers we build into every form, with examples you can hand to your developer.

## What contact form security actually covers

When teams say "the form is getting spam," they usually mean one of several different problems. It helps to name them, because each needs a different fix.

- **Spam and bots.** Automated scripts submit ads, links and junk, sometimes thousands of times a day.
- **Abuse of your form.** Attackers try to use it to send mail to other people, flood your inbox or probe your server.
- **Injection.** Input crafted to break out of its field: into email headers, into an admin dashboard or into a database query.
- **Data leaks.** Submissions end up somewhere they should not: an unprotected inbox, a log file, a third-party tool without an agreement.

A secure form handles all four. Most broken forms we review handle only the first, usually with a CAPTCHA that annoys real people.

## Why do contact forms attract so much spam?

Forms are easy to find and cheap to attack. Bots crawl the web looking for `<form>` elements and submit them blindly. They do not care what your company does. A form that emails on every submission also tempts anyone who wants to send mail in your name.

The good news is that most bots are simple. They fill every field, submit instantly and repeat. That behavior is easy to detect without asking humans to solve puzzles.

![Layers that a form submission passes through on the server: size limit, rate limit, validation, honeypot check, safe delivery](https://kyvrel.com/blog/inline/contact-form-security-1.png)

## Stop bots without punishing real people

We layer cheap checks first and only add friction when a site really needs it.

### Add a honeypot field

A honeypot is a field that humans never see but bots fill in. If it has a value, the submission is a bot. The trick is to respond with a normal success message, so the bot learns nothing.

```
<!-- Visually hidden, skipped by keyboard and screen readers -->
<div class="visually-hidden" aria-hidden="true">
  <label for="website">Website</label>
  <input id="website" name="website" tabindex="-1" autocomplete="off">
</div>
```

On the server, a non-empty `website` field returns the same success response as a real lead. Nothing is stored and nobody is notified.

### Rate-limit by IP, without storing IPs

Limit how many submissions one source can send in a window, for example a handful per ten minutes. Store a salted hash of the IP, not the IP itself. That gives you abuse protection without keeping extra personal data.

### Use a challenge only when you need one

If a site is targeted by smarter bots, add a low-friction challenge such as Cloudflare Turnstile or hCaptcha. Run it in invisible or managed mode where possible, and check that the fallback works with a keyboard and a screen reader. Never make a puzzle the first line of defense.

## Validate everything on the server

Client-side validation is for the user's convenience. It is not security, because a bot can send any request it likes. The server must check every field again, as the [OWASP input validation cheat sheet](https://cheatsheetseries.owasp.org/cheatsheets/Input_Validation_Cheat_Sheet.html) recommends.

Good validation is strict and boring:

- **Allowlist fields.** Reject unknown keys instead of passing them along.
- **Limit length.** A name does not need 5,000 characters. A message rarely needs more than 2,000.
- **Check formats.** Emails look like emails. Select fields accept only listed values.
- **Cap the request size** before parsing, so nobody can send a huge body.

```
// Server-side schema, shared with the client form
import { z } from "zod";

export const ContactSchema = z.object({
  name: z.string().trim().min(1).max(120),
  email: z.string().trim().email().max(254),
  company: z.string().trim().max(160).optional(),
  budget: z.enum(["5k-10k", "10k-25k", "25k-50k", "50k-plus", "not-sure"]),
  message: z.string().trim().max(2000).optional(),
  consent: z.literal(true),
  website: z.string().max(200), // honeypot: checked after validation
}).strict();
```

Using the same schema in the browser and on the server keeps error messages consistent. The server copy is the one that counts.

## Block injection and abuse of your form

Validation limits what comes in. Safe handling decides what that input can do once it is inside.

- **Never put user input into email headers.** A newline in a "name" field can add extra recipients if it reaches a `To` or `Subject` header. Send notifications to a fixed address and put user text only in the body.
- **Send plain-text notifications.** If submissions go to Slack, Telegram or email, send them as plain text. Rendering user input as HTML or markdown invites injected links and scripts.
- **Escape on output.** Any admin page that lists submissions must escape content, so a script in a message never runs in your browser.
- **Use parameterized queries.** Store submissions through your ORM or prepared statements, never by building SQL strings.
- **Check the origin.** Accept submissions only from your own site's origin and expected content type. The [OWASP CSRF cheat sheet](https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html) covers the options.

These are the same principles as in our [website security checklist](https://kyvrel.com/blog/website-security-checklist), applied to the one part of the site that accepts input.

## Keep submitted data from leaking

A form is a data pipeline. Each place a submission lands is a place it can leak from.

1. **Collect less.** Every optional field is data you must protect. Ask for what you need to reply.
2. **Know every destination.** Database, inbox, CRM, chat notification. List them, and check who can access each one.
3. **Keep secrets on the server.** API keys for your CRM or notification service never belong in browser code.
4. **Do not log bodies.** Error logs that capture full requests end up storing personal data in a tool nobody audits.
5. **Set a retention period.** Delete or archive old submissions on a schedule.
6. **Check your processors.** Third-party form services and CRMs process personal data on your behalf, so you need agreements with them.

The consent and privacy side, including what to put next to the submit button, is covered in our post on [GDPR for marketing websites](https://kyvrel.com/blog/gdpr-website-compliance).

![Where a form submission travels after it passes validation: database, team notification and CRM, each with its own access control](https://kyvrel.com/blog/inline/contact-form-security-2.png)

## How we build forms at Kyvrel

Our own lead form follows the order above, and so do the forms we build for clients. In short, the server handler does this:

1. Rejects anything but `POST`.
2. Rejects bodies over 16 KB before parsing.
3. Applies a rate limit keyed on a salted IP hash.
4. Parses JSON and validates it against the shared schema.
5. Returns a normal success response for honeypot hits, with nothing stored or sent.
6. Stores the lead, then sends a plain-text notification.

Each step has a test. We also test the form with a keyboard and a screen reader. A secure form that some people cannot use is still broken.

## Ship a form that only real leads get through

If your current form fills your inbox with spam, or nobody knows where submissions go, it is worth fixing before your next campaign. Secure forms are standard in every site we deliver. A chatbot is another input surface, with its own spam, prompt abuse and personal data to protect. The same rules apply if you add an [AI chatbot to your website](https://kyvrel.com/products/ai-chatbot). To price a build with your scope, languages and add-ons, use our [website cost calculator](https://kyvrel.com/website-cost-calculator).
