CORS errors on form submit mean the browser blocked a cross-origin POST because the form API did not return Access-Control-Allow-Origin for your site. Fix by adding your exact site URL to the form allowlist, using the vendor embed (which handles tokens), or submitting server-side with an ingest secret.
Guide
Why browser form POSTs fail with Access-Control-Allow-Origin errors, and how allowlists, embed scripts, Server Actions, and ingest secrets fix CORS on contact forms.
| Setup | Fix |
|---|---|
| embed.js | Allow production + localhost origins on the form |
| Hand-rolled fetch | Same allowlist + valid submit token from schema GET |
| Server Action | No browser CORS—secret on server only |
| React Native | No CORS—use ingest secret headers |
You wired a contact form, opened DevTools, and see red text: blocked by CORS policy or No Access-Control-Allow-Origin. The submission never reached your inbox. This is one of the most common integration failures on modern Jamstack sites—and it is fixable once you know browsers enforce origin, not your feelings about www.
This guide explains why CORS happens on form POSTs, how preflight works, and the four fixes teams actually use with hosted backends like MuxForms.
Browsers restrict JavaScript running on https://www.example.com from reading responses from https://api.other.com unless api.other.com explicitly allows https://www.example.com via Access-Control-Allow-Origin. Pure HTML <form action="https://api.other.com"> navigations are different; fetch and XHR from your React app trigger CORS. Embeds that POST JSON also trigger OPTIONS preflight.
| Console message | Likely cause |
|---|---|
| CORS policy blocked | Origin not allowlisted on form |
| Network shows 403 on POST | Origin, missing token, or ingest auth |
| OPTIONS fails | Server did not return CORS headers |
| Works locally, fails in prod | localhost allowed but prod URL missing |
| Works on apex, fails on www | Two different origins |
In MuxForms (and similar products), each form has allowed origins:
https://www.example.comhttp://localhost:3000 for devRules:
https://).www and non-www are different—allow both or redirect consistently.https://staging.example.com).After saving, hard-refresh and retry. Read the JSON body on 403—MuxForms returns Origin is not authorized for this form when CORS blocks you.
embed.js performs schema GET, obtains a submit token, and POSTs with headers the API expects. Custom fetch without that flow often dies on 403 even when CORS headers exist.
<div data-form-id="FORM_UUID"></div>
<script src="https://www.muxforms.com/embed.js" defer></script>
Next.js wrapper: blog tutorial.
Server Actions, Route Handlers, and PHP wp_remote_post are not browsers—CORS does not apply. Send the ingest secret:
POST /api/v1/forms/{id}/submit
Content-Type: application/json
X-MuxForms-Ingest-Secret: ingsec_xxxxxxxx
Documented in submit API. This is the right pattern when you refuse to maintain origin lists for every preview deployment—proxy through your server.
Native fetch does not send browser Origin the same way; CORS is rarely the issue. 403 usually means missing ingest secret or wrong field names (422). See collecting forms from React Native.
Expo Web is a browser—allowlist its dev origin.
When JavaScript sets Content-Type: application/json, the browser sends:
OPTIONS /api/v1/forms/{id}/submit
Origin: https://www.example.com
Access-Control-Request-Method: POST
The server must respond 204 with:
Access-Control-Allow-Origin: https://www.example.com
Access-Control-Allow-Methods: POST, OPTIONS
MuxForms handles OPTIONS when ingest is configured. If preflight fails, fix allowlist before debugging POST body.
| Code | Meaning | Fix |
|---|---|---|
| 403 | Auth / origin / token / Turnstile | Allowlist, secret, CAPTCHA |
| 422 | Validation | Field names and lengths |
| 429 | Rate limit | Slow down tests |
| 413 | Body > 64 KB | Shorten message |
CORS errors sometimes mask 403—check Network tab response headers and body.
example.com but site redirects to www.example.com.*.vercel.app or specific preview origin._hp empty string in JSON payloads.MuxForms is the inbox, embed, and JSON API. Start free, or upgrade to Pro or Team.
Upgrade to Pro