CORS Decision Pipeline

CORS는 서버 간 요청 차단이 아니라 브라우저의 응답 공유 판정이다

브라우저는 요청의 Origin과 서버의 Access-Control-* 응답 헤더를 비교해 JavaScript가 응답 본문과 특정 헤더를 읽어도 되는지 결정한다.

same-origin https://app.example.com 기본 포트 443까지 같으면 같은 origin이다.
host differs https://api.example.com host가 달라 cross-origin이다.
scheme differs http://app.example.com scheme이 달라 cross-origin이다.
네트워크 성공과 JavaScript 노출 허가는 서로 다른 판정이다 request Origin 포함 preflight? OPTIONS 확인 actual response 헤더 비교 expose JS 읽기 block 서버 처리는 끝났어도 ACAO/credentials/expose 조건이 맞지 않으면 브라우저가 응답 접근을 막는다.
1. request Origin 헤더 포함 브라우저가 cross-origin 요청에 출처를 표시한다.
2. preflight? 필요하면 OPTIONS 먼저 preflight가 실패하면 실제 요청은 보내지 않는다.
3. actual simple 또는 허가된 실제 요청 서버 동작은 일어날 수 있으므로 CSRF 방어는 별도다.
4. expose 응답을 JS에 노출할지 결정 ACAO가 맞지 않으면 브라우저가 응답 접근을 막는다.
ACAO Access-Control-Allow-Origin이 요청 Origin과 일치하거나 credentials 없는 요청에서 *여야 한다.
credentials 쿠키 포함 응답 공유에는 명시 origin과 Access-Control-Allow-Credentials: true가 필요하다. *는 실패한다.
Expose 기본 노출 헤더 밖의 X-Request-Id 같은 헤더는 Access-Control-Expose-Headers에 넣어야 JS가 읽는다.
Vary Origin에 따라 ACAO를 동적으로 반사하면 공유 캐시 오염을 막기 위해 Vary: Origin을 둔다.
public read credentials 없는 공개 API Access-Control-Allow-Origin: *
cookie read 쿠키 포함 API Access-Control-Allow-Origin: https://app.example.com Access-Control-Allow-Credentials: true Vary: Origin
custom header 응답 헤더 읽기 Access-Control-Expose-Headers: X-Request-Id

핵심: simple request도 응답을 읽으려면 실제 응답에 CORS 허가가 필요하다. CORS는 CSRF 방어가 아니며, Set-Cookie는 JS가 읽는 응답 헤더가 아니라 브라우저 쿠키 처리 규칙의 영향을 받는다.