CORS

What is a Preflight Request? Why Browsers Send OPTIONS First

Before sending a cross-origin POST, PUT, DELETE, or any request with custom headers, the browser first asks for permission with an OPTIONS request. If your server does not respond correctly, the actual request never runs.

Why preflights exist

Simple GET requests (like loading an image from another domain) were already possible before CORS existed — via HTML img tags. For more complex requests that can have side effects (POST, PUT, DELETE), browsers add a safety check: ask the server first before sending data.

What triggers a preflight

You get a preflight when a cross-origin request uses:

What the browser sends

OPTIONS /api/data HTTP/1.1
Host: api.example.com
Origin: https://app.example.com
Access-Control-Request-Method: POST
Access-Control-Request-Headers: Authorization, Content-Type

The browser is asking: "Is a POST from https://app.example.com with Authorization and Content-Type headers allowed?"

What your server must respond

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: Authorization, Content-Type
Access-Control-Allow-Credentials: true
Access-Control-Max-Age: 86400

The status must be 200 or 204. If your server returns 404 or 405 for OPTIONS, the browser blocks the actual request and shows a CORS error.

Postman does not send preflights

Postman is a developer tool — it does not enforce browser security policies. If your API works in Postman but fails in the browser, a missing preflight handler is almost always the reason. This is why "CORS errors only in browser" is such a common complaint.

Caching preflights

Access-Control-Max-Age: 86400 tells the browser to cache the preflight result for 24 hours. Without it, the browser sends OPTIONS before every single cross-origin request with custom headers.

Test your preflight response → CORSFixer