Trigger a CORS preflight

Reproduce the CORS preflight (OPTIONS) that a browser sends before a cross-origin request, using curl to verify the server's CORS configuration.

Overview

Reproduce the CORS preflight (OPTIONS) that a browser sends before a cross-origin request, using curl to verify the server's CORS configuration.

In code

# simulate the browser's preflight OPTIONS request:
curl -i -X OPTIONS https://api.example.com/users \
  -H 'Origin: https://app.example.com' \
  -H 'Access-Control-Request-Method: POST' \
  -H 'Access-Control-Request-Headers: Content-Type, Authorization'
# a passing preflight echoes back the allowances:
#   HTTP/1.1 204 No Content
#   Access-Control-Allow-Origin: https://app.example.com
#   Access-Control-Allow-Methods: POST, GET, OPTIONS
#   Access-Control-Allow-Headers: Content-Type, Authorization
#   Access-Control-Max-Age: 86400
// This request is non-simple (custom header + JSON), so the browser
// automatically fires an OPTIONS preflight before the POST:
await fetch('https://api.example.com/users', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    Authorization: 'Bearer <token>'
  },
  body: JSON.stringify({ name: 'Ada' })
});

Details

For non-'simple' requests (e.g. `Content-Type: application/json`, custom headers, PUT/DELETE), the browser sends an OPTIONS preflight before the real request, asking 'may I send this?' via `Origin`, `Access-Control-Request-Method`, and `Access-Control-Request-Headers`. The curl above imitates exactly that preflight.

The server must respond allowing the method and headers via `Access-Control-Allow-Origin` (must match the request Origin), `Access-Control-Allow-Methods`, and `Access-Control-Allow-Headers` for the real request to proceed. Miss any one and the browser blocks the request with a CORS error in the console (even a 200 response is unreadable by JS).

To send cookies/credentials, the server must return `Access-Control-Allow-Credentials: true` and set `Access-Control-Allow-Origin` to a specific origin, not the wildcard `*`. The preflight result is cached for `Access-Control-Max-Age`, reducing repeat OPTIONS. Remember CORS is enforced by the browser, not the server, so it does not apply to direct curl-to-server calls.

Related headers

Related status codes