Preflight request explained.
A preflight request is the automatic OPTIONS call a browser makes before certain cross-origin requests, asking the server whether the real request, with its method and headers, is allowed.
How a preflight request works
Browsers enforce CORS to stop one site from quietly making sensitive requests to another on a visitor's behalf. For requests that could have side effects, the browser doesn't send the real request straight away. It first sends a small OPTIONS request to the same URL, carrying two headers: Access-Control-Request-Method names the method the real request will use, and Access-Control-Request-Headers lists any custom headers it will send. The server answers with its own Access-Control-Allow headers describing what it permits.
If the real request fits inside that permission, the browser proceeds and sends it; if not, the browser blocks it and your fetch fails before the real call ever leaves. This check happens once and can be cached for a window the server sets.
Not every request triggers it: a simple request (a plain GET, or a POST with a form-style content type and no custom headers) skips the preflight, which is exactly why a normal HTML form POST to a form backend goes straight through. Add a custom header or a JSON content type to that same submission from JavaScript and the browser will preflight first, so FormWire's endpoint answers OPTIONS with the right allow headers.
What a preflight asks and answers.
The OPTIONS probe
The browser sends an OPTIONS request naming the method and headers the real request intends to use, before sending anything with an effect.
The allow response
The server replies with Access-Control-Allow headers, and the browser only proceeds if the real request fits inside what's permitted.
Simple requests skip it
A plain form-style POST with no custom headers counts as a simple request, so a standard HTML form submission is never preflighted.
Related questions
Cross-origin requests that aren't simple. Anything using a method beyond GET, HEAD or POST, or sending a custom header, or a content type other than the form-style ones, gets preflighted. A plain HTML form POST stays simple and skips it, which is why static-site forms just work.
How FormWire's endpoint handles CORS →So the browser can ask permission without causing an effect. OPTIONS is meant to describe what's allowed rather than change anything, so if the answer is no, nothing happened. Only after the server approves does the browser send the real POST or other method.
Only by keeping the request simple: use a form-style content type and avoid custom headers. You can't disable CORS from the page, since it's the browser enforcing it, but the server can cache a preflight response so repeat calls skip the extra round trip for a while.
The server didn't return the right Access-Control-Allow headers for the method or headers your request wanted, so the browser blocked the real request. FormWire's endpoint answers preflights with the correct allow headers, so a submission that fits its rules is approved and sent.
Want to see it working?
Read the docsPost to an endpoint that answers correctly.
Free tier forever. No credit card required.