← Blog/Best Practices

Forms inside email: what survives, and what trips spam filters

October 15, 2026·6 min read·By MailPilot

Do not put a working <form> in an email. Gmail, Outlook and most webmail clients strip or neutralise form markup, and the clients that keep it will often warn the recipient before submitting. The pattern that works in every client is a one-click link per answer, landing on a hosted page on your own domain that records the response and shows a thank-you — a "one-click survey". It is simpler to build, it is measurable, and it does not make your message look like a credential harvester.

Why clients strip forms

A form inside an email is an unauthenticated POST from a context the user did not navigate to, in a window with no address bar. Every mail client has concluded that is unsafe. So:

  • Gmail (web and apps) removes <form>, <input>, <button type="submit"> and associated attributes.
  • Outlook on Windows renders via the Word engine and has no form support at all.
  • Outlook.com and Yahoo Mail sanitise form elements out of the DOM.
  • Apple Mail renders forms, but submitting opens a browser; the inline experience is partial.
  • Some clients that keep the markup display a security prompt before the request is sent.

Net effect: build for forms and the majority of your audience sees a broken block of orphaned labels.

Why form markup raises spam risk

This is the part most guides omit. Filters weight structural signals, and a form is a strong one:

  • Credential-harvesting phishing almost always contains a form or an input field. Shape matters more than intent.
  • A POST target on a domain other than the From domain is a classic phishing pattern.
  • Hidden inputs, which every real form has, overlap with hidden-content heuristics.
  • Stripped markup leaves malformed HTML behind, and malformed HTML is itself scored.
  • A high ratio of markup to readable text pushes you toward the same scoring band as image-only mail.

You can have a perfectly honest form and still lose placement for all five reasons at once.

The pattern that works: one link per answer

Instead of asking the reader to fill something in, give them one click per possible answer, each a link that encodes the answer:

<p style="font:16px/1.5 Arial,sans-serif;">How did the migration go?</p>
<p>
  <a href="https://go.example.com/r/9fbc1?a=great">Great</a> &nbsp;·&nbsp;
  <a href="https://go.example.com/r/9fbc1?a=ok">OK</a> &nbsp;·&nbsp;
  <a href="https://go.example.com/r/9fbc1?a=bad">Not great</a>
</p>

Rules that make this safe and reliable:

  1. Use `GET`, and treat the token as the identity. 9fbc1 is an opaque, single-purpose, expiring token mapped server-side to one recipient and one question. Never put an email address, a user ID or anything guessable in the URL — mail passes through logs, proxies and forwards.
  2. Make the link idempotent. Mail scanners and link-preview bots will fetch your URLs. The first GET must not be destructive, and a repeat fetch must not double-count. Record the answer, then show a page where the reader confirms if the answer changed.
  3. Host it on your own domain. go.example.com with its own CNAME, same registrable domain as your From address.
  4. Finish the interaction on the page. Prefill the rest of the form with the answer already selected, so the reader completes a two-field form instead of starting a blank one.

If you genuinely need a form in the body

Only for an internal audience on a known client, or as a progressive enhancement with a complete fallback:

<form action="https://go.example.com/subscribe" method="POST">
  <input type="hidden" name="src" value="newsletter-oct" />
  <input type="email" name="email" placeholder="you@example.com" required />
  <input type="text" name="confirm_url" value="" style="display:none" tabindex="-1" aria-hidden="true" />
  <button type="submit">Subscribe</button>
</form>
<p><a href="https://go.example.com/subscribe?src=newsletter-oct">Or subscribe on our site</a></p>

Notes on that markup. POST is the correct method for a state change, and it is also the method most likely to be blocked — which is why the link fallback below it is mandatory, not optional. The hidden confirm_url field is a honeypot: automated scanners fill every field, humans never see it, so a submission with that field populated is discarded server-side. Do not use display:none as your only honeypot defence if you are worried about content scoring; a position:absolute;left:-9999px label is equivalent and less likely to be read as hidden text.

AMP for Email

AMP for Email supports genuinely interactive components — forms, carousels, live content — in a separate text/x-amp-html MIME part alongside your HTML and plain-text parts. Gmail renders it, and sending requires registering your sending domain with Google. Support outside Gmail has narrowed over time rather than widened, so verify current support for your audience's clients before investing engineering time.

Practical position: AMP is worth it if a large share of your list is on Gmail, you already have the engineering capacity, and the interaction is genuinely better inline. For everyone else, the hosted one-click pattern delivers most of the benefit for a fraction of the work, and it degrades gracefully by construction.

Fallback strategy

Build in this order, so every layer stands alone:

  1. Plain text part with the question and a URL per answer.
  2. HTML part with the one-click links — this is what most people see.
  3. AMP part, if you have it, for the inline experience in Gmail.

Never let a layer depend on a layer above it rendering.

Reducing spam risk when you do use forms

  • Keep the form's action domain identical to your From registrable domain.
  • Keep hidden fields to one, and keep them off-screen rather than display:none where you can.
  • Keep readable text well above markup weight; total HTML under about 100KB so Gmail does not clip.
  • Keep the List-Unsubscribe and List-Unsubscribe-Post headers on bulk sends regardless of what is in the body.
  • Do not combine a form with an image-only layout and a shortened link. Each of those is survivable; together they are not.

Pre-send checklist

  • Rendered in Gmail web, Gmail mobile, Outlook Windows, Outlook.com and Apple Mail
  • Rendered with images blocked
  • Tokens opaque, expiring, and not derivable from an email address
  • Repeat GET from a scanner does not corrupt the data
  • Link fallback present in the HTML and in the plain-text part
  • All link hostnames on your own domain
  • A real test send to seed mailboxes, read as delivered — not a preview tool

MailPilot covers the part a render test cannot: it warms the sending mailbox, verifies SPF, DKIM and DMARC on the delivered message, and measures real per-provider inbox placement, so an interactive template that filters badly shows up before you send it to the whole list.

MailPilot

Ready to reach the inbox every time?

Automated email warmup across real peer mailboxes. Live inbox placement monitoring. Free 7-day trial - no credit card required.

Start free trial
Reach the Inbox, Every Time
Connect unlimited inboxes for warmup. Free 7-day trial, no credit card.
Try for Free
WORKS WITH
OZOHO
inbox placement99.2%
free trial7-day
companies trust us340+

Start for free - no credit card required.

Get started freeBook a demo