Spam protection

A public form endpoint attracts bots. Here is exactly what mailcontact blocks today, the honeypot pattern we recommend adding to your form, and the per-inbox rules coming with the launch.

What the endpoint enforces today

Every form endpoint applies these limits at the edge, before anything reaches your inbox:

  • Rate limiting: 20 requests per minute per IP. Flooding a form from one machine stops working almost immediately.
  • Payload caps: request bodies are limited to 64 KB. Oversized junk is rejected outright.
  • Required, valid email: submissions without a syntactically valid email field (or without a message) are rejected.
  • Honeypot handling: any submission with a filled _gotcha field is silently dropped: the bot sees a success response, and nothing reaches your inbox.

Be clear about what this is: transport-level hygiene plus a bot trap, not a content filter. A bot that sends one well-formed submission without touching the honeypot will still land in your inbox. That's why the field below is worth adding to every form.

Add a honeypot field

A honeypot is an extra input that is hidden from humans with CSS but visible to bots, which auto-fill every field they find. A human submission leaves it empty; a filled honeypot is a near-certain bot. Name the field _gotcha:

contact-form.html
<form action="https://mailcontact.app/api/forms/your-form-id" method="POST">
  <input type="email" name="email" placeholder="you@email.com" required />
  <textarea name="message" placeholder="Your message" required></textarea>

  <!-- Honeypot: visually hidden, humans never fill it, bots do -->
  <div class="hp" aria-hidden="true">
    <label>
      Leave this field empty
      <input type="text" name="_gotcha" tabindex="-1" autocomplete="off" />
    </label>
  </div>

  <button type="submit">Send</button>
</form>

<style>
  .hp {
    position: absolute;
    left: -9999px;
    width: 1px;
    height: 1px;
    overflow: hidden;
  }
</style>

Three details make it work:

  • Hide it with CSS, not type="hidden": most bots skip hidden inputs but happily fill visible-to-the-DOM text fields.
  • tabindex="-1" and autocomplete="off" keep keyboard users and browser autofill from touching it by accident; aria-hidden keeps it out of screen readers.
  • If you submit via fetch, you can also act on it yourself: check the _gotcha value in your submit handler and skip the request entirely when it's filled.

The endpoint handles the rest server-side: any submission that arrives with a filled _gotcha is dropped before it reaches your inbox. Add the field and it protects you immediately.

Keep your endpoint id unguessable

Form ids are random and unguessable, and there's no directory to enumerate them. The id does ship in your page's HTML, so it isn't a secret (the limits above assume that), but don't widen the audience for free: avoid pasting your live endpoint into public gists, forum posts or documentation that doesn't need it. Most form spam comes from crawlers harvesting endpoints, so the fewer places yours appears, the quieter your inbox stays.

Coming with the launch

  • Sender rules and blocklists per inbox: block an email address or a whole domain from a specific inbox, for both form submissions and messages to your project email address.

There is no CAPTCHA integration or machine-learning content filter today, and we won't pretend otherwise. In practice, the endpoint limits plus a honeypot handle the large majority of form spam. If your project is a heavier target and you need more, tell us what's hitting you.