Cross-Origin Resource Sharing
- CORS의 개념, 필요성
- Same-Origin Policy(동일 출처 정책)의 의미, 한계
- CORS의 동작 원리(단순 요청 vs Preflight 요청) 이해
- CORS 해결을 위한 HTTP 응답/요청 헤더의 역할
- Security에서 제공하는 CORS 설정법
CORS(Cross-Origin Resource Sharing) 이란

위 에러는 브라우저가 preflight 요청을 보냈는데 (Response to preflight request)
서버가 응답 헤더에 Access-Control-Allow-Origin 이걸 안넣었다 (서버가 CORS 허용 헤더를 안내려줌)
그래서 브라우저가 다른 출처의 요청을 허용하지 않아서 차단한거임
- CORS(Cross Origin Resource Sharing) 라는건 교차 출처 리소스 공유를 의미한다
- 서로 다른 Origin을 가진 애플리케이션이 서로의 리소스에 접근할 수 있도록 해준다

Origin은 프로토콜 + 호스트 + 포트의 조합임
이중에 하나라도 다르면 Cross-Origin으로 간주한다
보안상의 이유로 브라우저는 Cross-Origin HTTP 요청을 기본적으로 제한하고 있다
최근 웹 서버에서 정적파일과 데이터를 한번에 내려주는 구조가 아닌, 프론트엔드 서버와 API 서버를 분리해서 통신한다고 함
이러한 경우, Cross-Origin 환경이므로 CORS 오류가 발생할 수 있다
출처(Origin)와 동일 출처 정책(SOP)
1. 출처(Origin)
- URL의 프로토콜, 호스트, 포트를 합친 값
- 브라우저의 개발자 도구 콘솔에서 location.origin을 실행하면 확인 가능

2. 같은 출처 vs 다른 출처
위의 location.origin 결과로 나온 https://b1uffer.tistory.com 을 예로 든다면
| URL | 결과 | 이유 |
| https://b1uffer.tistory.com/about | 같은 출처 | Protocol, Host, Port가 동일함 |
| https://b1uffer.tistory.com/about?q=work | 같은 출처 | Protocol, Host, Port 동일 |
| http://b1uffer.tistory.com/about | 다른 출처 | Protocol이 다름, 기본 Port 다름 |
| https://b1uffer.tistory.com:81/about | 다른 출처 | Port가 다름 |
| https://github.com/B1uffer | 다른 출처 | Host가 다름 |
3. 동일 출처 정책(Same-Origin Policy, SOP)
- 브라우저는 SOP(Same-Origin Policy)를 적용하여 다른 출처의 리소스 접근을 차단한다
- Postman이나 다른 서버에서 API를 호출할 땐 잘 되지만, 브라우저에서만 CORS policy가 발생하는 이유가
브라우저는 차단하고 Postman은 차단하지 않기 때문임
(1) SOP의 장점
외부 리소스를 무분별하게 가져오는 것을 막아, XSS-XSRF 공격을 방어할 수 있다
(2) SOP의 한계
실제 웹페이지는 외부 리소스를 자주, 많이 사용한다
이를 위해 SOP 예외로 제공되는 메커니즘이 CORS 임
CORS의 동작 원리
CORS는 크게 단순 요청(Simple Request), 예비 요청(Preflight Request) 두가지가 있다
1. Simple Request (단순 요청)
- 서버에 바로 요청을 보내고, 서버는 Access-Control-Allow-Origin 헤더를 포함한 응답을 반환한다
- 브라우저는 해당 헤더를 확인하여 요청 허용 여부를 결정한다

Simple Request의 조건
- 요청 메서드는 GET, HEAD, POST 중 하나여야함
- Accept, Accept-Language, Content-Language, Content-Type 등 제한된 헤더만 사용 가능함
- Content-Type은 application/x-www-form-urlencoded, multipart/form-data, text/plain 만 허용된다
대부분의 REST API는 application/json을 사용하기 때문에 Preflight 요청(예비 요청)으로 처리된다고 함
2. Preflight Request(예비 요청)
- 본요청 전에 OPTIONS 메서드로 예비 요청을 보낸다
- 서버가 허용 여부(Access-Control-Allow-* 헤더) 를 응답하면, 브라우저가 본 요청을 진행한다

GET, POST, PUT, DELETE 등의 메서드로 API를 요청했는데
크롬 개발자 도구의 네트워크 탭에 OPTIONS 메서드로 요청이 보내지는걸 경험했다면 CORS를 경험한 것임
Preflight 요청은 실제 리소스를 요청하기 전에 OPTIONS 라는 메서드를 통해 실제 요청을 전송할지 판단한다
OPTIONS 메서드로 서버에 예비 요청을 보내고, 서버는 이 예비 요청에 대한 응답으로 Access-Control-Allow-Origin 헤더를 포함한 응답을 브라우저에 보낸다. 브라우저는 단순 요청과 동일하게 Access-Control-Allow-Origin 헤더를 확인해서 CORS 동작을 수행할지 판단하게 된다
CORS 오류 해결법
CORS 오류는 단순히 서버가 응답에 CORS 관련 헤더를 포함하지 않아서 발생하는 오류이다
따라서 서버에서 적절한 Access-Control-Allow-헤더 를 설정해줘야한다
1. Access-Control-Allow-Origin
- 브라우저가 리소스를 접근할 수 있는 출처를 지정한다
- (와일드카드) 를 쓰면 모든 출처에 대해 허용한다
Access-Control-Allow-Origin: <https://b1uffer.tistory.com>
Access-Control-Allow-Origin: * # 와일드카드
2. Access-Control-Allow-Methods
- 허용할 HTTP 메서드를 지정한다
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
3. Access-Control-Expose-Headers
- 브라우저 JavaScript 코드에서 접근 가능한 응답 헤더를 지정한다
Access-Control-Expose-Headers: X-Custom-Header, X-Another-Header
4. Access-Control-Allow-Headers
- 브라우저가 보낼 수 있는 요청 헤더를 지정한다
Access-Control-Allow-Headers: X-Custom-Request
5. Access-Control-Max-Age
- Preflight 요청 결과를 캐싱(?)할 시간을 지정한다
Access-Control-Max-Age: 3600
6. Access-Control-Allow-Credentials
- 쿠키, 인증 정보를 포함한 요청을 허용할지에 대한 여부를 설정한다
Access-Control-Allow-Credentials: true
이 경우 Access-Control-Allow-Origin 에 * 대신 특정 출처를 지정해야한다
Spring Security에서 CORS 지원
Security에서는 http.cors() 와 CorsConfigurationSource 를 통해 CORS를 지원하고 있다
@Bean
@Order(3)
public SecurityFilterChain corsFilterChain(HttpSecurity http) throws Exception {
http
.cors(Customizer.withDefaults()) // CORS 활성화하기
.authorizeHttpRequests(auth -> auth.anyRequest().permitAll());
return http.build();
}
@Bean
public CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(List.of("http://localhost:3000"));
configuration.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE"));
configuration.setAllowedHeaders(List.of("*"));
configuration.setAllowCredentials(true);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
팁
- 개발 단계에서는 Access-Control-Allow-Origin : * 를 해도 되지만, 운영에서는 반드시 특정 출처만 허용해야한다
- Access-Control-Allow-Credentials: true 를 사용할 경우, 와일드카드 (*) 는 사용할 수 없다
- CDN, API Gateway 등 중간 계층에서 CORS를 미리 처리하는 경우도 많다
- CORS 정책은 보안 기능이므로, 무분별하게 허용하지 말고 최소 권한 원칙을 지켜야함
정리
| 항목 | 설명 |
| SOP | 브라우저가 다른 출처 리소스 접근을 차단하는 보안 정책 |
| CORS | SOP의 예외로, 교차 출처 리소스 공유 허용 |
| Simple Request | 조건 충족시 바로 요청 + Access-Control-Allow-Origin 확인 |
| Preflight Request | OPTIONS 요청으로 사전 확인 후 본 요청 진행 |
| 주요 응답 헤더 | Access-Control-Allow-Origin Access-Control-Allow-Methods Access-Control-Allow-Headers Access-Control-Allow-Credentials, Access-Control-Max-Age 등 |
| Spring Security | http.cors() + CorsConfigurationSource 로 손쉽게 지원 |
'Spring Boot > Security' 카테고리의 다른 글
| SecurityFilterChain에 H2 콘솔이 막힐때 (0) | 2026.05.06 |
|---|---|
| CSRF : enable(), disable() (0) | 2026.05.03 |
| 주요 웹 보안 이슈 & Security 방어 전략 : XSS (0) | 2026.05.01 |
| 주요 웹 보안 이슈 & Security 방어 전략 : CSRF (0) | 2026.05.01 |
| 커스텀 필터 구현 : 구현해보기 (0) | 2026.05.01 |