Test your contact form before launch
What breaks contact and lead forms, ranked — and the one pre-launch check no external tool can do: confirm the email actually arrived.
Your contact form is often the only conversion path on the page — no signup, no checkout, just “get in touch” — and when it breaks, it breaks 100% silently. Nothing errors. The visitor fills it in, sees “Thanks, we’ll be in touch,” and closes the tab. The message never reaches you. There’s no bounce, no log line, no angry email — because the angry email is the thing that didn’t send. A dead contact form is a lost-lead funnel you can’t even see, and it’s the flow every free pre-launch checker skips: static audits confirm the page loads and returns 200; none of them fills the form, submits it, or checks that anything happened. In a June 2026 audit of 49 fresh web launches, 38 (78%) shipped with at least one critical verified finding on day one. (That audit walks public pages, so it can’t tell you how many contact forms specifically broke — but it tells you launch-day code ships broken far more often than founders expect.)
The safe way to test a contact form before launch is to do two things in order. First, walk the form the way a visitor would — fill it, submit it, confirm it succeeded — which an external tool can automate. Second, send one real test message and confirm it actually lands in your inbox, which no external tool can do for you. This piece is the list of what breaks, ranked, and how to verify each one.
Why static checkers and a quick self-test miss this
A static audit confirms the page exists and the form’s HTML is present. It does not type into the fields, press submit, watch the network request, or check that a success state rendered. A contact form is a small flow — one screen, a submit, a confirmation — and flow testing is a different problem from page testing.
Testing it yourself misses the other half. You submit from your own browser, on your own fast connection, to your own warm inbox — the one address that’s already whitelisted, already not spam-foldered, already receiving. Real submissions hit the cold path: the brand-new sending domain with no reputation, the recipient’s aggressive spam filter, the host that quietly throttled outbound mail an hour ago. The good news is the same as with signup flows, checkout flows, and password reset flows: the surface is small and bounded. A handful of things break contact forms. You can know all of them.
The things that break contact and lead forms
In rough order of how often they show up in founder reports and in our own audits:
-
The form submits, but the email never arrives. This is the big one, by a wide margin. The visitor sees the success message, the submit request returns 200 — and the notification email never reaches you. Why it happens: the host disabled the default mail function (a common anti-spam move that no one tells you about), the domain is missing valid SPF/DKIM/DMARC records so the message is rejected or spam-foldered, or the host throttled outbound mail after sensing a traffic spike. AI coding tools cheerfully generate a
sendMail()call that compiles and “works” in the demo but was never wired to a real authenticated mail provider. The Prufa check: the functional walk verifies the form submits and the success state renders — but, to be honest about the boundary, an external scan cannot confirm the email left the building (more on that below). 5-minute manual check: send one real message through the live form, then check the destination inbox and the spam folder. If it’s in spam, fix your SPF/DKIM/DMARC. If it’s nowhere, your send path is broken. -
The form has no working submit path. The
<form>has no submit button (a<div onclick>that didn’t wire up), or it posts to an endpoint that doesn’t exist or returns the wrong thing. The form looks complete and does nothing. Why it happens: the LLM generated the markup and the handler separately and they drifted — the same wrong-endpoint failure that hits signup flows. The Prufa check: the forms analyzer parses the page’s HTML deterministically and flags a<form>with no submit control as a verified finding — because a form you can’t submit is a broken funnel, full stop. 5-minute manual check: in DevTools → Network, submit the form and watch. There should be exactly one request, to the endpoint you expect, returning a 2xx. No request means no working submit. -
Validation is broken in one of two directions. Either the form accepts anything — empty required fields, “asdf” in the email field — and you get junk leads you can’t reply to; or it’s too strict and silently rejects valid input, then shows a generic “Something went wrong.” Why it happens: the email input was never typed
type="email", so neither the browser nor the form validates it; or the required-field logic lives only client-side and the server never re-checks. The Prufa check: the forms analyzer flags an email field that isn’ttype="email"(you lose the mobile email keyboard and built-in validation) and fields with no associated label (browsers can’t autofill what they can’t identify, and the field is invisible to screen readers). 5-minute manual check: submit the form empty — it should reject with a clear inline message. Then submit withnotanemailin the email field — it should reject that too. Then submit a valid message and confirm it goes through. -
No spam protection — or spam protection that eats real leads. With nothing in place, a public contact form gets scraped and drowned in bot submissions within days, and the real lead is buried. With a too-aggressive honeypot or a misconfigured filter, legitimate submissions get silently dropped. Why it happens: spam protection is the boring part that gets added last or copy-pasted wrong. 5-minute manual check: submit a normal, human-looking message and confirm it gets through cleanly. If you’ve added a honeypot field, confirm it’s actually hidden (a visible “leave this blank” field will eat real submissions from people who fill it in).
-
No success state, so the visitor doesn’t know it worked. The form submits but the page just sits there, or quietly resets. The visitor can’t tell if it sent, so they submit again (now you have duplicates) or assume it failed and leave. Why it happens: the happy path was wired to send but never to confirm — the UI feedback got skipped. This is the contact-form version of the success state that updates the UI but not the server, inverted: here the server may succeed while the UI says nothing. 5-minute manual check: submit the form and watch the screen. There must be an unambiguous “we got it” state — not a silent reset, not a blank page.
-
The diagnostic one: the check hits a CAPTCHA wall. If your form is behind reCAPTCHA or hCaptcha, an automated walk can’t pass it — and the right behavior is to report BLOCKED, not to fake a pass. That’s not “your form is broken”; it’s “an external test can’t go further without solving a human challenge.” The manual test message is the workaround.
How Prufa walks a contact form
Prufa runs the same pattern here it runs everywhere: an LLM does the navigation, plain code does the verification. The LLM reads the page like a visitor — finds the contact form, fills the name, email, and message, presses submit — and plain code, against captured browser evidence, verifies what actually happened: the submit request returned a 2xx, the expected endpoint was hit, the success state rendered. The LLM never grades the result, so its blind spots can’t become the verifier’s blind spots. (The full walkthrough is in How Prufa verifies a signup flow; a contact form is the same machinery pointed at a simpler flow.)
Underneath the flow walk, a deterministic forms check parses the page’s HTML directly — no LLM involved — because the most basic form failures are mechanical and don’t need a model to catch. As the check’s own source puts it, forms are where flows begin — a broken form is a broken funnel. It flags, at verified tier:
- a
<form>with no submit control (you can’t submit it); - an email field not typed
type="email"(lost validation + mobile keyboard); - unlabeled fields (no autofill, invisible to screen readers);
- name/email/phone fields missing
autocomplete(slower for every visitor on mobile); - a password field served over HTTP (rare on a contact form, but caught if present).
What the free 60-second audit does for contact forms
The free audit on prufa.dev opens your entry URL on a real browser, finds and walks the contact form, submits it, and verifies the success state — plus it runs the deterministic forms check on the page’s markup. No signup, no card, public pages only. It catches the failures a static checker can’t: a form with no working submit, an untyped email field, a missing success state. If you’re a solo founder shipping fast, the quickest pre-launch move is to run the free form audit on your entry URL — it’s the kind of launch QA for indie hackers that catches a dead contact form before your first real visitor ever hits it.
If you want the form watched continuously rather than once, Starter ($29/mo) re-verifies your core flows daily and on every deploy and alerts you the moment one breaks — useful because a contact form that works today can stop sending the day your host changes its mail policy or a DNS record expires, exactly the kind of silent rot that hits an app after launch.
For the rest of the pre-launch surface — sitemaps, meta tags, broken links, console errors — see Website QA checklist before launch. The contact form is one of the flows that list points at and static checkers don’t walk.
What the audit doesn’t do (honest limits)
The free audit cannot confirm your notification email arrived. It can verify the form submitted and the page said “thanks,” but the email leaving the server, passing SPF/DKIM/DMARC, clearing the recipient’s spam filter, and landing in the inbox all happen in places an external black-box check can’t see. That’s the single most common way a contact form fails, and it’s the one step you have to test yourself. The audit also can’t pass a CAPTCHA and can’t read your inbox. An external walk verifies the form submits and confirms; the test message you send yourself verifies the part that actually matters — that you received it.
What to do before you launch
In order of impact:
- Send one real test message and confirm it lands in the inbox. Check the destination inbox and the spam folder. In spam → fix your SPF, DKIM, and DMARC records. Nowhere at all → your send path is broken (host mail disabled, wrong credentials, unverified sender). If you send an auto-reply to the visitor, test that it arrives too.
- Submit the form empty, then with a junk email. It should reject both with clear inline messages — not silently accept them, and not 500. Then submit a valid message and confirm it goes through.
- Watch the submit in DevTools → Network. Exactly one request, to the endpoint you expect, returning a 2xx. The success state must render after it — no silent reset, no blank page.
- Test on a real phone. The email field should bring up the email
keyboard (that’s
type="email"doing its job), every field should be tappable and labeled, and the form should submit and confirm. - Confirm your spam protection works both ways. A normal human message gets through; obvious bot spam doesn’t. If you use a honeypot, make sure it’s truly hidden so it doesn’t eat real submissions.
Get those five right and you’ve closed the failure modes that turn a working-looking contact form into a silent lost-lead funnel on launch day.
Frequently asked questions
How do I test my contact form before launch?
Do two things. First, walk the form on a real browser — fill it, submit it, and confirm the success state renders and the submit request returned 2xx; a free 60-second audit does this automatically. Second — and this is the part no external tool can do for you — send one real test message and confirm it lands in the destination inbox, not the spam folder. The form submitting and the email arriving are two separate things, and the second is where most contact forms silently fail.
Why am I not receiving my contact form submissions?
Almost always because the form submits fine but the notification email never leaves the server or never passes spam filters. The usual causes: the host disabled the default mail function, the domain is missing valid SPF, DKIM, or DMARC records, the host throttled outbound mail after sensing a traffic spike, or the message is being spam-foldered. The form looks like it worked — the success message shows — but nothing reached you. Send a test message and trace it: if it's in spam, fix your email authentication records; if it's nowhere, your send path is broken.
Can an automated tool test a contact form?
Yes, up to a point. An external tool can open the form on a real browser, fill it, submit it, and verify the request succeeded and the confirmation state rendered — and deterministic checks can catch a form with no submit button, an email field that isn't typed for validation, or unlabeled fields that browsers can't autofill. What an external black-box tool cannot do is confirm the notification email actually arrived in your inbox, because that happens server-side and in a mailbox it can't see. That last step is yours.
What should a contact form do when a user submits it?
Three things, all observable: validate the input and show a clear inline error if something's wrong, post to a working endpoint that returns a 2xx, and render an explicit success state so the visitor knows it worked. Behind the scenes a notification email (and ideally an auto-reply) should fire. If any of the visible three is missing, visitors double-submit or assume failure and leave; if the email half is missing, you lose the lead without ever knowing it existed.