Handle OPTIONS Preflight Requests in Your Server
Ensure your server correctly responds to OPTIONS preflight requests. Covers common pitfalls with 405 errors, missing headers, and framework-specific handling.
Preflight
Detailed Explanation
Responding to OPTIONS Requests
If your server returns 405 Method Not Allowed or a 200 with an HTML body for OPTIONS requests, the browser will fail the preflight and block the actual request. Your server must respond with a 204 No Content (or 200) and the correct CORS headers.
What the Browser Sends
OPTIONS /api/users HTTP/1.1
Host: api.example.com
Origin: https://app.example.com
Access-Control-Request-Method: POST
Access-Control-Request-Headers: Content-Type, Authorization
What the Server Must Return
HTTP/1.1 204 No Content
Access-Control-Allow-Origin: https://app.example.com
Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Max-Age: 3600
Vary: Origin
Common Pitfalls
- Framework routing ignores OPTIONS — Many frameworks route only GET/POST by default. You need to explicitly handle OPTIONS or use CORS middleware.
- Authentication middleware runs first — If your auth layer rejects unauthenticated requests before CORS headers are set, the preflight fails with a 401. CORS middleware must run before authentication.
- Reverse proxy strips headers — A misconfigured Nginx or AWS ALB might strip the
Access-Control-*headers. Addalwaysin Nginx to ensure headers are sent even on non-2xx responses. - Missing Vary header — Without
Vary: Origin, cached preflight responses can leak across origins.
Express.js: CORS Before Auth
// CORS must come BEFORE auth middleware
app.use(cors(corsOptions));
app.use(authMiddleware);
Nginx: Explicit OPTIONS Block
if ($request_method = 'OPTIONS') {
add_header Access-Control-Allow-Origin $cors_origin always;
add_header Access-Control-Allow-Methods "GET, POST, OPTIONS" always;
add_header Access-Control-Allow-Headers "Content-Type, Authorization" always;
add_header Access-Control-Max-Age 3600 always;
return 204;
}
Use Case
A backend developer deploying a new API endpoint finds that PUT and DELETE requests from the frontend fail with CORS errors, even though GET requests work. The issue is that non-simple methods trigger preflights, and the server was not configured to handle OPTIONS.