Why an API works in curl but fails at the browser preflight
Description and use
corswhy reveals that the server returns 401 to OPTIONS, blocking the browser POST. Configuring the server to allow unauthenticated OPTIONS and return the required CORS headers lets the request succeed.
Problem
A browser preflight OPTIONS request fails with 401 or missing headers, preventing the actual POST from being sent, while the same request succeeds via curl.
Outcome
The corswhy tool identifies that the server’s 401 response to OPTIONS blocks the browser POST. By configuring the server to allow unauthenticated OPTIONS and to return the correct CORS headers, the preflight succeeds and the browser request proceeds.
Workflow
- 1. Run corswhy with the API URL, origin, method, and header names:
node bin/corswhy.js http://127.0.0.1:18765/auth --origin http://localhost:5173 --method POST --header authorization,content-type - 2. corswhy sends an OPTIONS request with the specified details and prints a row for each CORS check.
- 3. Review the report: a 401 status indicates failure; a 204 with allowed headers indicates success.
- 4. Adjust the server to allow unauthenticated OPTIONS and to include the required
Access-Control-Allow-OriginandAccess-Control-Allow-Headersin the response. - 5. Re‑run corswhy to confirm the preflight passes.
- 6. Test the actual POST from the browser to ensure it is now sent and processed.
Evidence
I built corswhy to inspect that exchange from a terminal. It takes the URL of the API, the page origin, the intended method, and names from the browser's Access-Control-Request-Headers field. It sends OPTIONS with those details and prints one row for each check.
The failing endpoint returns 401 to OPTIONS. The report names the status failure first and suggests letting OPTIONS reach the CORS handler without authentication.
The second endpoint answers with a 204 response and the required allowed origin and header names, so its report passes.
- Record date: 2026-10-02T13:56:02.254Z