Same-origin policy ngăn JavaScript từ một origin (scheme+host+port) đọc response từ origin khác — bảo vệ khỏi CSRF và data theft.
- CORS cho phép server opt-in cho cross-origin requests qua response headers.
- Simple requests (GET/POST với basic Content-Type) không cần preflight — browser gửi request và check response headers.
- Preflight (OPTIONS request): cho non-simple requests (custom headers, PUT/DELETE, Content-Type: application/json) — browser gửi OPTIONS trước, server phải respond với allowed methods/headers trước khi browser gửi actual request.
- Response headers: Access-Control-Allow-Origin: https://trusted.com (hoặc cho public APIs nhưng không thể dùng với credentials); Access-Control-Allow-Methods: GET, POST, PUT, DELETE; Access-Control-Allow-Headers: Content-Type, Authorization; Access-Control-Max-Age: 86400 (cache preflight 24h, giảm OPTIONS requests); Access-Control-Allow-Credentials: true (để gửi cookies/auth headers — phải specify exact origin, không dùng ).
- Common mistakes: add CORS headers ở nginx nhưng app cũng trả CORS headers → duplicate headers → browser reject; thêm * nhưng cần credentials → không work; không handle OPTIONS method trong route → 405 error.
- CORS chỉ restrict browser requests — Postman, curl, server-to-server không bị ảnh hưởng.
- Credentials mode: fetch(url, { credentials: 'include' }) gửi cookies; cần server set Access-Control-Allow-Credentials: true và exact origin.
The same-origin policy prevents JavaScript from one origin (scheme + host + port) from reading responses from another origin — protecting against CSRF and data theft.
- CORS allows servers to opt in to cross-origin requests via response headers.
- Simple requests (GET/POST with basic Content-Type) do not require a preflight — the browser sends the request and checks the response headers.
- Preflight (OPTIONS request): for non-simple requests (custom headers, PUT/DELETE, Content-Type: application/json) — the browser sends an OPTIONS request first; the server must respond with allowed methods/headers before the browser sends the actual request.
- Response headers: Access-Control-Allow-Origin: https://trusted.com (or for public APIs but not usable with credentials); Access-Control-Allow-Methods: GET, POST, PUT, DELETE; Access-Control-Allow-Headers: Content-Type, Authorization; Access-Control-Max-Age: 86400 (cache preflight for 24h, reducing OPTIONS requests); Access-Control-Allow-Credentials: true (to send cookies/auth headers — must specify an exact origin, not ).
- Common mistakes: adding CORS headers in nginx while the app also returns CORS headers → duplicate headers → browser rejects; using * but needing credentials → does not work; not handling the OPTIONS method in the route → 405 error.
- CORS only restricts browser requests — Postman, curl, and server-to-server calls are not affected.
- Credentials mode: fetch(url, { credentials: 'include' }) sends cookies; requires the server to set Access-Control-Allow-Credentials: true and an exact origin.