Why isn't my form submitting?
Paste a form that won't submit, posts to the wrong place or arrives empty. The debugger reads the markup the way a browser does and tells you which field, attribute or button is the problem, and how to fix it. It runs entirely in your browser: your markup is not sent anywhere, including to us.
Nothing happens when you press Submit
If the button does nothing at all, the browser never started a submission. A <button type="button"> never submits, and a button outside the <form> submits nothing unless its form attribute names the form's id. A required field that is hidden blocks the submit without a word: the browser wants to point at the empty field, finds nothing visible to point at, and stops, leaving only a message in Chrome's console. An onsubmit that returns false has the same effect, and so does a script that calls preventDefault() and then fails. A disabled submit button can't be clicked, and pressing Enter won't submit the form either.
The page reloads, but nothing arrives
When the page reloads or shows an error, the browser did send the form, just not to anything that keeps it. A form with no action posts to the page it is on. On a static host such as Netlify, Vercel, GitHub Pages or Cloudflare Pages there is no code behind that page, so the POST fails, often with 405 Method Not Allowed. A form with no method uses GET, which puts the answers in the address bar instead of a request body. An action pointing at localhost worked on your machine and nowhere else, and an http:// action on an https page makes Chrome show a full-page warning before anything is sent.
If reloading the thank-you page asks you to confirm form resubmission, the POST did reach a server. Why that dialog appears, and how to stop it.
The submission arrives with fields empty or missing
Look at the name attributes first. The browser sends a field only if it has a name, and the name is the key its value arrives under. An id is not a name: <input id="email"> sends nothing. Disabled fields are left out whatever they contain, so use readonly for a value that should be shown and still sent. Fields outside the form are not sent. A form nested inside another is dropped by the HTML parser, and its closing tag ends the outer form early, cutting off the fields after it. Two fields with the same name both send, and some backends keep only one of them.
WebForms adds one rule of its own: field names that start with an underscore are settings, such as _next or _subject, so a field called _name is never stored. The options reference lists them all.
A file upload arrives as just its name
A form can only send a file in a POST body encoded as multipart/form-data. Without enctype="multipart/form-data" on the form tag the browser sends each file's name as text and leaves the file behind, and a GET request has no body to put a file in. On WebForms, uploads also need turning on for the form, and a Pro plan or above; see file uploads.
It works as a plain form but not with fetch
A plain form post to another domain goes straight through. A fetch that sets Content-Type: application/json or a custom header does not: the browser first sends a CORS preflight, and if the server refuses it, the real request is never sent. When posting FormData with fetch, don't set the Content-Type header yourself, because the browser has to add the multipart boundary to it.
When the markup is fine
Some failures don't show in markup. Script attached with addEventListener or bundled by a framework can stop or change what is sent. A server can also refuse a correct request: a WebForms endpoint answers form_not_found (404) for an unknown or deactivated key, validation_failed (422) when a value breaks the form's field schema, quota_exceeded (402) once the monthly limit is reached, and account_unverified (403) while nobody who owns or runs the workspace has verified their account. Every code is in the error reference. To see which one you are getting, open your browser's developer tools, submit the form, and read the response in the Network panel.
This debugger finds why data doesn't arrive. For a form that already works, the form spam test audits its defences against bots: honeypot, captcha, HTTPS and exposed addresses. To start from markup that avoids every problem above, build the form with the HTML form generator.