How forms die: seven ways a live contact form silently stops working

April 5, 2026 · 7 min read

Dim security lights — forms failing silently in production

Contact forms do not usually die with a bang. They die with silence. Everything looks fine. The page loads. The form renders. You click submit and the success message appears. The test submission from your team's Slack channel even arrives sometimes, because you clicked through while on the office Wi-Fi, which is the one network where half of these failures don't happen.

Meanwhile, real users — on real networks, from real browsers — are submitting and getting nothing. You find out three months later when a customer emails you from their personal Gmail saying "hey, I tried to reach you in June and never heard back."

Here are seven specific ways it happens. I've seen all of them in the wild. Some of them twice.

1. The inbox that everyone left

A contact form that submits successfully, gets stored properly, sends an email notification to contact@yourcompany.com, and then gets read by nobody because the person who owned that inbox left the company eight months ago. Their account got disabled. Forwarding broke. The emails have been bouncing for the whole quarter.

The check: send a test submission from an unfamiliar email address (not yours, not a teammate's) and confirm a human acknowledges it within your stated response time. Do this once a month. The moment it takes more than a day, something has rotted.

2. The DNS change nobody told you about

Your form's webhook delivers to hooks.yourcompany.com/form, which used to resolve to an internal service. Six months ago, the infrastructure team migrated the internal service and quietly removed the hooks CNAME. The webhook URL now returns a generic NXDOMAIN. Your form backend still accepts submissions and stores them fine. The webhook silently fails, every time, forever.

If your form backend has a retry policy, the retries also fail. If your backend has a dashboard that shows webhook delivery status, you never log in. The submissions are in a table somewhere, unread.

The check: look at your webhook delivery logs at least monthly. If you can't, pick a form backend that emails you when webhooks are failing for more than 24 hours. Silent webhook failure is the most common form death I encounter.

3. The redirect that forgot it was behind a proxy

You added Cloudflare in front of your site. Or you moved from Vercel to Netlify. Or you put a new CDN between the browser and your origin. Somewhere in this migration, your contact form's action attribute — which is a relative URL — now resolves to the wrong path, or the wrong host, or a cached 404 page that looks real enough that nobody noticed.

The form submits. It 404s. The user sees a Cloudflare error page. They close the tab. They don't tell you.

The check: after any infrastructure change, re-test your contact form from an incognito window on a different network. Not from your dev environment. Not from staging. From the live site, on mobile data.

4. The third-party script that broke everything

Your form works because a JavaScript analytics script intercepts the submit event, logs the conversion, and then lets the native submit continue. One morning, the analytics vendor pushes a breaking change. Their script now throws an error on submit. Your form event handler never calls the native submit because the exception interrupted the flow.

From the user's perspective: they click the button, nothing happens, they click it again, nothing happens, they give up.

From your perspective: page loads fine, form renders fine, console shows one ominous red line that nobody looks at.

The check: minimize JavaScript interception on form submit. Native HTML form submission does not rely on JavaScript at all, which is one of its underrated virtues. If you need analytics on form submission, trigger it from the confirmation page, not from a submit handler that could break the submit itself.

5. The email that got reclassified as spam

Your form sends a notification to your inbox via a transactional provider. The provider is fine. The SMTP path is fine. The email arrives at your inbox. Then Gmail's spam filter decides — because you recently marked a bunch of emails with similar formatting as "not interested" — that form notifications belong in spam.

Six weeks later you have 200 unread form notifications in spam, none of which you saw, including one from a customer who later tweeted about how unresponsive you were.

The check: add your own form-notification sender to your email provider's contacts. Set up a Gmail filter that moves form notifications to a dedicated label. Do not rely on your inbox's default ranking to surface them.

Better check: route notifications into a Slack or Discord channel you actually look at. Email as a primary notification surface for anything business-critical has been a bad idea since about 2018. The spam filter is not on your side.

6. The form that broke on Safari only

You ship a form. You test it on Chrome. It works. You deploy. Some of your users — the ones on iOS — open the form and see nothing. A <template> tag you added for a feature was never polyfilled for old Safari. A CSS variable was typoed and the submit button now has color: var(--accent) which falls back to transparent on black. An autocomplete value you set isn't recognized by WebKit and the form fails to render.

Your Chrome users never see the bug. Your analytics tell you the contact page gets visits. The visits just don't convert.

The check: test the form on at least three browsers, two of which you don't use daily. Open it on a real iPhone, not the Chrome dev tools iPhone simulator. Open it on an Android device. Open it on Firefox. Five minutes, covers 99% of the cross-browser silent deaths I've encountered.

7. The success message that lied

The form submits. The success page appears. The user feels good. They go about their day.

Meanwhile, the backend is broken. The success page is being shown regardless of whether the submission actually worked — perhaps because the form posts to a /thanks URL with a 200 response no matter what, or because a JavaScript handler always shows a success toast, or because the "thank you" message is hardcoded and fires on every submit attempt.

The user thinks they contacted you. You never got anything. Both of you are confused a week later.

The check: your success state must be downstream of actual, verified success. If the form backend returned a 500, show an error. If the webhook failed, still show success (the submission was stored), but make sure the submission was actually stored before acknowledging it.

This is one of those "it's only a problem because someone decided to get clever" failures. The fix is to trust the backend's response and stop inventing client-side optimism.

The meta-failure

Every one of these seven has a common pattern: the form appears to work from the perspective of the person running the site, because that person has a specific combination of network, browser, account, and trust in their own infrastructure that real users do not share. The form is fine in your own sandbox. It is not fine for a stranger on a flaky 4G connection using Firefox on Linux at 11 pm.

The antidote is a simple discipline I borrowed from an SRE friend years ago: assume your form is broken until a stranger proves otherwise, every month.

Here's how I do it personally:

  1. Once a month, a calendar event titled "test your own damn contact form" fires.
  2. I open an incognito window on my phone, on mobile data, not on Wi-Fi.
  3. I submit a genuine-looking test message with a unique marker in it (like the date and a nonsense word).
  4. I wait for the notification in Slack. If it takes more than two minutes I open a debug session.
  5. I look for the message in the dashboard. If it's not there, same.
  6. I reply to my own test from the primary inbox. If the reply reaches me, the loop is closed.

Five minutes. Once a month. I promise you this ritual will catch something at least twice a year.

The less-obvious takeaway

If your contact form lives entirely inside systems you wrote and systems you rent, you have a lot of failure modes. Not because the individual systems are bad — each of them is probably fine — but because silent failures concentrate at the seams between systems, and a contact form has a lot of seams.

The reason I eventually stopped rolling my own form infrastructure is not that I couldn't build it. It's that I got tired of the seams. A hosted form backend is one fewer seam between "user filled out a form" and "a human read what they wrote." That's the whole value proposition, stripped of marketing copy.

A quick audit you can do today

Pick your main contact form. Run through this list:

  • The notification email landed in someone's actively-checked inbox in the last 14 days
  • The webhook has successfully delivered in the last 24 hours (check the dashboard)
  • You or a teammate has submitted a test from an unknown IP and browser in the last month
  • You've checked the form on Safari iOS in the last month
  • You've checked what happens when the submission fails (try it with your laptop in airplane mode)
  • The form's action URL resolves correctly from a stranger's network
  • Nobody on your team has left the company whose inbox the form points at
  • The success page only shows after a verified 2xx response from the backend

If you can't tick all eight, one of them is going to bite you eventually. The nice thing about knowing which one is that you can fix it in an afternoon instead of three months after a customer finds the bug for you.


FormTo shows you webhook delivery status, submission search, and per-form notifications in a dashboard that was designed by someone (me) who had been burned by all seven of these failures at least once. The free plan is enough to stress-test your current setup against a hosted alternative.

Also useful: the 3 a.m. incident, which is what these failures look like when they all hit at once.

← All posts