CORS

브라우저의 Cross-Origin 차단 정책

CORS(Cross-Origin Resource Sharing)는 브라우저가 다른 출처의 요청을 기본 차단하는 정책이다. 서버가 Access-Control-Allow-Origin 헤더로 허용해줘야 통과한다. CSRF와 이름이 비슷하지만 완전히 다른 개념.

Security

개념

CORS(Cross-Origin Resource Sharing)는 브라우저가 다른 출처(origin)의 응답을 읽지 못하게 막는 정책이다. 서버가 아니라 브라우저가 강제하는 규칙이고, 서버가 명시적으로 "이 출처는 괜찮아"라고 허용해야 통과된다.

출처(origin) = 프로토콜 + 도메인 + 포트. 하나라도 다르면 다른 출처다.

https://my-app.com       → https://api.my-app.com    ❌ 도메인 다름
https://my-app.com       → https://my-app.com:8080   ❌ 포트 다름
https://my-app.com       → http://my-app.com         ❌ 프로토콜 다름
https://my-app.com/a     → https://my-app.com/b      ✅ 경로만 다름

왜 차단하나

브라우저가 아무 사이트에서 다른 서버로 요청 보내는 걸 그대로 허용하면, 로그인된 쿠키가 같이 날아가서 악성 사이트가 사용자 계정을 가로챌 수 있다. 그래서 기본은 차단이고, 서버가 "이 출처는 괜찮아"라고 명시해야 브라우저가 응답을 JS에게 넘긴다.

동작 방식

단순 요청 (Simple Request)

GET, POST(폼 전송 수준)처럼 단순한 요청은 바로 보내고, 응답의 Access-Control-Allow-Origin 헤더를 확인한다.

요청 → 서버 응답: Access-Control-Allow-Origin: https://my-app.com → 브라우저 통과

프리플라이트 (Preflight)

Content-Type: application/json이나 커스텀 헤더가 붙으면 브라우저가 먼저 OPTIONS 요청을 보내서 허용 여부를 확인한다.

1. OPTIONS /api/data (프리플라이트)
   → 서버: 허용할 출처·메서드·헤더 응답
2. 허용됐으면 실제 요청 전송

서버 응답 헤더

Access-Control-Allow-Origin: https://my-app.com
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Allow-Credentials: true
  • Allow-Origin: *은 전체 허용이지만 Allow-Credentials: true와 동시에 쓸 수 없음
  • 쿠키를 보내려면 출처를 명시해야 함

CORS vs CSRF

이름이 비슷해서 헷갈리는데 관점이 다르다.

CORS CSRF
뭐냐 브라우저의 차단 정책 공격 기법
누가 막냐 브라우저가 응답을 차단 서버가 토큰/SameSite로 검증
뭘 막냐 다른 출처에서 응답 읽는 것 다른 사이트에서 요청 위조하는 것

핵심: CORS는 응답을 JS에게 넘기지 않는 거지, 요청 자체를 막는 게 아니다. 그래서 CORS만으로는 CSRF를 막을 수 없다. 브라우저가 요청은 보내고 쿠키도 붙여주되, 응답 본문만 JS에서 못 읽게 할 뿐이다.

개발할 때 자주 만나는 상황

  • 로컬에서 localhost:3000localhost:8000 요청 → 포트가 달라서 CORS 에러
  • 프록시로 우회하거나 서버에서 Access-Control-Allow-Origin 설정
  • Nginx에서 리버스 프록시로 같은 출처로 만들어버리는 것도 방법

더 보기

  • XSS-CSRF — 이름은 비슷하지만 전혀 다른 공격/방어
  • Proxy — CORS를 우회하는 전형적인 방법
  • Nginx — 리버스 프록시로 같은 출처로 통합
sunshinemoon · 2026