Skip to content

How to Configure CORS Correctly

Key idea:

CORS (Cross-Origin Resource Sharing) is the mechanism letting JavaScript from one origin (domain:port:scheme) request resources from another. Without CORS, browsers block fetch/XHR. Setup: HTTP headers Access-Control-Allow-Origin, Access-Control-Allow-Methods, Access-Control-Allow-Headers. For credentials and non-simple requests — preflight (OPTIONS).

Below: step-by-step, working examples, common pitfalls, FAQ.

Step-by-Step Setup

  1. Decide allowed origins. Wildcard * only for public APIs without credentials
  2. For specific origins: echo Origin header if whitelisted
  3. For preflight (OPTIONS): respond with Access-Control-Allow-Methods: GET, POST, PUT
  4. If you need cookies: Access-Control-Allow-Credentials: true + NOT wildcard
  5. Cache preflight: Access-Control-Max-Age: 86400 (24h)
  6. Verify: Enterno CORS checker with a test Origin
  7. In DevTools → Network → look for preflight OPTIONS + headers

Working Examples

ScenarioConfig
nginx simple CORSadd_header 'Access-Control-Allow-Origin' 'https://app.example.com' always; add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS' always; add_header 'Access-Control-Allow-Headers' 'Authorization, Content-Type' always;
nginx preflightif ($request_method = OPTIONS) { add_header 'Access-Control-Allow-Origin' '$http_origin'; add_header 'Access-Control-Max-Age' 86400; return 204; }
Apache .htaccessHeader set Access-Control-Allow-Origin "https://app.example.com" Header set Access-Control-Allow-Credentials "true"
Express.jsapp.use(cors({ origin: 'https://app.example.com', credentials: true }));
Whitelist multiple originsmap $http_origin $cors_origin { default ""; "~^https?://(app|api)\.example\.com$" $http_origin; } add_header 'Access-Control-Allow-Origin' $cors_origin always;

Common Pitfalls

  • Wildcard * + Credentials=true — browsers block silently
  • Not responding to preflight OPTIONS — Chrome defers headers to actual request that never runs
  • Access-Control-Allow-Headers missing a custom header like X-Request-Id — fail
  • Echo Origin without whitelist — CSRF vulnerability for credentialed endpoints
  • Difference between simple request (GET without headers) and non-simple — first skips preflight

Learn more

Frequently Asked Questions

What is a simple request?

Simple request: GET/HEAD/POST + only standard headers (Accept, Content-Type in form/text). No preflight OPTIONS; goes through directly. Non-simple = preflight required.

Why doesn't wildcard * work with credentials?

Security spec: credentials (cookies) + wildcard = theoretically any site could steal sessions. Browsers block the combo.

How to debug CORS?

DevTools → Network → red request → Headers. Console shows "CORS policy: ..." with the cause. <a href="/en/cors">Enterno CORS checker</a> simulates different Origin headers.

Check CORS online?

<a href="/en/cors">Enterno CORS checker</a> — enter URL + Origin → see returned headers.