The minimal integration
Before debugging your markup, post to your endpoint directly. If curl produces a submission and an email, the backend is healthy and the problem is in your page. If curl fails too, the problem is server-side and nothing you change in the form will help.
curl -X POST https://mtform.co/f/your-form-key \
-H "Content-Type: application/json" \
-d '{"name":"Test","email":"test@example.com","message":"Hello from cURL"}'A 201 with "ok": true means the endpoint stored the submission. Anything else carries an error.code that names the problem.
Step by step
- 1
Open the browser network tab and submit
Watch for the POST request. No request at all means a JavaScript error or a button that is not type="submit". A request with a red CORS error means the endpoint rejected your origin. A 2xx response means the submission was accepted and the problem is downstream in delivery.
- 2
Post to the endpoint directly with curl
This removes your page, your framework, and the browser from the equation entirely. A successful curl proves the backend receives and delivers; a failing one localises the fault to the server side.
- 3
Confirm the recipient address is verified
Mail providers will not deliver to an unverified destination, and most form backends refuse to send until you click the confirmation link. Submissions received before verification should still be stored, so check the stored payloads too.
- 4
Check spam, promotions, and any server-side rules
Automated notifications from a new sending domain are routinely filtered for the first few weeks. Search your whole mailbox for the sender rather than only looking at the inbox.
- 5
Verify the submission was stored regardless
Delivery and storage should be separate systems. If the payload is in your inbox but no email arrived, the form is working and the fault is purely in delivery, which is a much smaller problem than a lost submission.
Common pitfalls
A button without type="submit"
Inside a <form>, a <button> defaults to type="submit", but a <div> or an <a> styled to look like a button does not submit anything. If no network request fires at all, check this first.
Assuming no email means no submission
These are separate failures. A well-built form backend stores the payload the moment it is accepted, before attempting any notification, so the data is usually still there even when the email never arrives.
preventDefault() with no fetch call
A very common AI-generated pattern: the handler blocks the native submit and then never posts anywhere. The form appears to work, shows a success message, and sends nothing.
Sending from an address you do not control
Setting the From header to the visitor’s address fails SPF and DMARC and gets the message rejected or spam-foldered. The sender should be your verified domain, with the visitor in Reply-To.
Troubleshooting
| Symptom | What’s happening | Fix |
|---|---|---|
| no request | Nothing appears in the network tab on submit. | The button is not submitting or a JavaScript error broke the handler. Check the console and confirm the button is type="submit" inside the form. |
| CORS | The request is blocked before it reaches the server. | Add the exact posting origin to your form’s allowed domains, including the protocol and both www and non-www variants. |
| 403 | The endpoint rejected the submission. | The origin is not allowed, or the form key is wrong. Compare the key in your markup against the one shown in the dashboard. |
| 422 | The POST arrived with no fields at all (reserved _mtf_ fields do not count). | Give every input a name attribute: the browser never sends an input without one. With fetch, check the body is FormData or JSON built from the form, not empty. |
| 200, no email | The submission is stored but no notification arrives. | The recipient is unverified, the notification is in spam, or the monthly quota is exhausted. All three leave the stored submission intact. |
Frequently asked questions
Why is my contact form not sending emails?
The most common cause is that the form has no backend at all. HTML cannot send email on its own, so a form with no server behind it silently does nothing. The next most common causes are an unverified recipient address, the notification being filtered into spam, and a JavaScript handler that blocks the submit without posting anywhere.
How do I know if my form submission was received?
Open the browser network tab and submit. A 2xx response means the server accepted it. If your form backend stores submissions, check the inbox directly. A stored payload with no email proves the form works and only delivery failed.
My form worked before and stopped. What changed?
Most often an expired SMTP credential, a domain or DNS change that broke SPF and DMARC alignment, a deployment to a new origin that is not on the allowed list, or a monthly submission quota that has been reached.
Why do my contact form emails go to spam?
Usually because the notification claims to come from the visitor’s address, which fails SPF, DKIM and DMARC, or because it is sent from a new domain with no sending reputation. Send from an authenticated domain you control, put the visitor in Reply-To, and mark the first few notifications as not spam.
Why is my HTML form not submitting at all?
If no request appears in the network tab, the browser never sent one. The usual causes are a submit control that is not a real button inside the form, a JavaScript handler that calls preventDefault() and never posts, or inputs with no name attribute, which the browser leaves out of the submission.
Can a form submission be lost?
It should not be. Storage and delivery are separate concerns, and a submission should be written durably the moment it is accepted, before any email is attempted. If your setup only emails and never stores, every delivery failure is permanent data loss.
Keep going
Last reviewed by the MakeTheForm team.