βοΈ What is a Preflight Request?
- A preflight is an automatic
OPTIONSrequest sent by the browser before the actual API call. - Purpose: to ask the server for permission to send a non-simple cross-origin request (e.g., one with custom headers like
AuthorizationorContent-Type).
π Preflight vs Actual API Response
| Aspect | Preflight (OPTIONS) |
Actual Request (GET, POST, etc.) |
|---|---|---|
| Who sends it | Browser (automatically) | Browser (after preflight passes) |
| Purpose | Check if origin, method, and headers are allowed | Perform the real API action |
| Typical Headers |
Access-Control-Allow-Origin, Access-Control-Allow-Methods, Access-Control-Allow-Headers
|
Access-Control-Allow-Origin, Access-Control-Expose-Headers
|
| Response Body | Usually empty (204 No Content) |
Contains actual API data or error |
| Visible to Frontend JS | No (browser internal) | Yes (if CORS rules allow) |
π§© Why Preflight is Useful
- π‘οΈ Security: Prevents unauthorized cross-origin requests.
- β Negotiation: Lets the browser confirm allowed origins, methods, and headers.
- π¬ Safety: Ensures your API only accepts intended headers and methods.
1οΈβ£ Middleware handles only OPTIONS
Preflight (success):
HTTP/1.1 204 No Content
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, POST
Access-Control-Allow-Headers: Content-Type, Authorization
Actual request (fails CORS):
HTTP/1.1 200 OK
Content-Type: application/json
(no Access-Control-Allow-Origin header)
π₯ Browser blocks the response because the actual API didnβt include the same CORS header.
2οΈβ£ Error responses skip CORS config
Preflight (success):
Access-Control-Allow-Origin: https://app.example.com
Actual (fails due to 500):
HTTP/1.1 500 Internal Server Error
Content-Type: application/json
(no Access-Control-Allow-Origin header)
π₯ Browser treats this as a CORS violation β even though preflight passed.
3οΈβ£ Credentials + Wildcard Origin
Preflight:
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true
π₯ Invalid combo β browsers block if Access-Control-Allow-Credentials: true is set with *.
You must return the exact origin instead:
Access-Control-Allow-Origin: https://app.example.com
Access-Control-Allow-Credentials: true
4οΈβ£ Missing Allowed Headers
Browser requests:
Access-Control-Request-Headers: Content-Type, Authorization
Server responds (missing Authorization):
Access-Control-Allow-Headers: Content-Type
π₯ Browser rejects the request:
βRequest header field Authorization is not allowed by Access-Control-Allow-Headers in preflight response.β
π‘ Developer Recommendations
- β Apply CORS consistently on all responses β success or error.
- βοΈ Use middleware per route if different APIs need different origins.
- π§± Include
AuthorizationandContent-TypeinallowedHeadersif you use tokens or JSON. - π Be explicit β donβt rely on defaults.
- π§ͺ Test in DevTools β verify that both
OPTIONSand the actual request return matchingAccess-Control-Allow-*headers.
π§ TL;DR
Preflight = browser asking for permission
Actual request = performing the actionPreflight can pass while the actual request fails if headers, origins, or credentials arenβt consistent.
π§ Fix: Apply the same
Access-Control-Allow-*headers to every response β including errors β and explicitly list headers likeAuthorizationandContent-Type.
Top comments (0)