Fix · Health checks pass but production is failing silently

Contact form says message sent but no email arrives

The form shows a success message, but nothing reaches your inbox and the host's dashboard shows no submission. Three causes are common: the success message does not depend on the server's answer, the form has no server-side handler, or your email provider has suppressed the recipient address.

Likely causes, most common first

Cause 1 · in 1 of 5 matching cases

The form has no server-side handler behind it

An HTML form is a way to configure an HTTP request. If action is omitted, the data goes to the URL of the page that contains the form. The action value should point to server code that can handle the incoming data.

How to tell: The Network tab shows no request to an endpoint you own, or a request to the page's own URL that returns the same HTML.

Cause 2 · in 1 of 5 matching cases

The provider skips the recipient because it is suppressed

Email providers keep a suppression list of addresses that bounced, complained or were added by hand. Resend skips sending to any address on that list across all your domains and subdomains, and Amazon SES can accept a message for a suppressed address without sending it. Your code sees an accepted request, and the inbox stays empty.

How to tell: Other addresses receive the notification, but the one you set as the recipient does not, and it appears in the provider's suppression list.

Cause 3

The page shows success without checking the response

In browsers, fetch() fulfills its promise when the server answers with an error status such as 404 or 500. If the success message runs after the await without a check on response.ok, a failed request looks the same as a good one. The visitor sees 'Message sent' and the inquiry is lost.

How to tell: Submit the form with the Network tab open. The POST returns a 4xx or 5xx status, or goes to the wrong URL, yet the success message still appears.

Check and fix it, step by step

  1. Replay the request outside the page

    Open the Network tab, submit the form once and note the URL, method and status of the request. Then repeat the request from a terminal to see what the endpoint returns without the page's success message in the way.

    curl -i -X POST https://example.com/api/contact -d "[email protected]&message=test"

    Docs: developer.mozilla.org →

  2. Confirm the form posts to a real handler

    Check the action attribute and the fetch URL. If action is missing, the data goes to the current page. The target should be server code that parses the fields and sends or stores the message.

    Docs: developer.mozilla.org →

  3. Look for the recipient in the provider's suppression list

    In Resend, open Suppressions in the dashboard and search for the address that should receive the notification. In Amazon SES, list the account-level suppression list and search for the address or domain.

    aws sesv2 list-suppressed-destinations | grep -A2 'example.com'

    Docs: docs.aws.amazon.com →

  4. Show success after a 2xx response

    Check response.ok after the fetch and throw if it is false. The catch branch can then show an error and log the status, so a silent drop becomes a visible failure.

    const res = await fetch("/api/contact", { method: "POST", body });
    if (!res.ok) throw new Error(`Response status: ${res.status}`);

    Docs: developer.mozilla.org →

  5. Remove the address once the cause is fixed

    If the recipient is on the list by mistake, remove it in the provider's dashboard or API. Fix the reason it was added first, because Resend suppresses an address again if that reason remains.

    Docs: resend.com →

Quick check: Submit with DevTools Network open: is there a 2xx POST to your endpoint? If so, check the provider's suppression list.

How often this shows up in our data

5 of the 431 verified cases from the last 12 months in our data match this symptom (1.2%). The most common cause was “The form has no server-side handler behind it” (1 of 5); 3 didn't show which cause. How we collect and verify cases.

With Gemmein

On Gemmein your server sends the email with g.notify, and the answer says what happened. A suppressed address comes back sent: false with a notSent reason and a message, a missing sender domain is 409 sender_domain_required, a provider refusal is 502 send_failed, and every send appears in your dashboard Inbox as a conversation.

Questions

Why does my contact form say the message was sent when it failed?

fetch() rejects on network errors but fulfills with a Response when the server returns an error such as 404. Unless the code checks response.ok or response.status, the success branch runs anyway.


Can an email provider accept my send request and still not deliver?

Yes, for suppressed addresses. Amazon SES documents that it accepts the message but does not send it when the address is on a matching suppression list, and Resend skips sending to addresses on its suppression list.


Will removing the address from the suppression list fix it?

Not by itself. Resend states that removal does not guarantee delivery, and it suppresses an address again if the reason for suppression remains.


What happens if my form has no action attribute?

The browser sends the data to the URL of the page containing the form. That page needs server-side code to handle the incoming data.


Sources

Every cause and step above was checked against these pages on 6 Oct 2026.

The broader pattern

This is one symptom of a wider failure pattern: Health checks pass but production is failing silently. The guide covers every cause we see for it, on any stack.

Get a heads-up when your stack breaks something

Leave your email and we'll let you know when something big changes for your stack. Unsubscribe any time by replying. Gemmein Limited. Research terms · Privacy

← All fixes