Security & Authentication

CORS Configuration: A Complete Guide

How the Same-Origin Policy works, when preflight requests are triggered, and how to configure CORS headers correctly and securely.

What Is CORS?

Cross-Origin Resource Sharing (CORS) is a browser security mechanism that controls which origins (domain + protocol + port) can make requests to your API. Without CORS headers, the browser blocks responses from a different origin — not the request itself, but the response JavaScript can read.

An *origin* is the combination of scheme, host, and port:

https://app.example.com:443. Changing any component creates a different origin, even a port change from 443 to 3000.

Same-Origin Policy

The Same-Origin Policy (SOP) is the browser rule that CORS relaxes. By default:

  • https://app.example.com can freely call https://app.example.com/api
  • https://app.example.com cannot read the response from https://api.example.com (different subdomain)
  • http://localhost:3000 cannot read the response from http://localhost:8000 (different port)

Note: SOP restricts *reading* responses. Simple POST requests (forms) were always allowed; CORS adds protection for those too.

Simple vs Preflight Requests

Simple Requests

A request is *simple* if it uses GET, HEAD, or POST with specific content types (application/x-www-form-urlencoded, multipart/form-data, text/plain) and no custom headers. Simple requests are sent directly; the browser checks CORS headers on the response.

Preflight Requests

All other requests trigger an automatic preflight — an OPTIONS request sent before the actual request:

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

The server must respond with appropriate CORS headers before the browser sends the real request:

HTTP/1.1 204 No Content
Access-Control-Allow-Origin: https://app.example.com
Access-Control-Allow-Methods: GET, POST, DELETE
Access-Control-Allow-Headers: Authorization, Content-Type
Access-Control-Max-Age: 3600

Configuring CORS Headers

`Access-Control-Allow-Origin`

The most important header. Options:

# Allow a specific origin
Access-Control-Allow-Origin: https://app.example.com

# Allow any origin (public APIs only)
Access-Control-Allow-Origin: *

Never use * with credentials — it is not permitted. When credentials are involved, you must echo the specific Origin header.

`Access-Control-Allow-Methods`

Access-Control-Allow-Methods: GET, POST, PUT, PATCH, DELETE, OPTIONS

`Access-Control-Allow-Headers`

List all custom headers the client may send:

Access-Control-Allow-Headers: Authorization, Content-Type, X-Request-ID

`Access-Control-Max-Age`

How long (seconds) browsers can cache the preflight result. Reduces preflight overhead for repeat requests:

Access-Control-Max-Age: 86400

Credentials and Cookies

By default, cross-origin requests do not include credentials (cookies, TLS certificates, HTTP auth). To enable:

// Client must opt in
fetch('https://api.example.com/data', { credentials: 'include' })
# Server must respond with:
Access-Control-Allow-Credentials: true
Access-Control-Allow-Origin: https://app.example.com  # must be specific

Common CORS Errors

ErrorCauseFix
Missing CORS headerServer didn't send ACAOAdd CORS middleware
Wildcard with credentials`*` + credentialsEcho Origin explicitly
Preflight failingOPTIONS not handledAdd OPTIONS handler
Wrong port in originDev vs prod portInclude all allowed origins

Security Best Practices

  • Whitelist, don't wildcard for authenticated APIs — maintain an explicit list of allowed origins
  • Validate the Origin header server-side — do not blindly echo it; check against your whitelist
  • Avoid Access-Control-Allow-Headers: * in production — list headers explicitly
  • Log rejected CORS requests — suspicious origins are a signal of attempted cross-site attacks
  • CORS is a browser enforcement mechanism — it does not protect server-to-server calls or curl requests

Protocoles associés

Termes du glossaire associés

Plus dans Security & Authentication