정의: 서로 다른 출처(Origin: 스킴+호스트+포트) 간의 리소스 접근을 브라우저가 제어하기 위한 표준 메커니즘입니다. 기본 정책인 SOP(Same‑Origin Policy) 를 확장하여, 서버가 명시적으로 허용한 범위에 한해 교차 출처 요청을 허용합니다.
GET, HEAD, POSTAccept, Accept-Language, Content-Language, Content-Type, Range)Content-Type: application/x-www-form-urlencoded, multipart/form-data, text/plainOrigin 과 응답의 Access-Control-Allow-Origin 을 비교하여 허용 여부를 판단합니다.OPTIONS 로 사전 요청을 보냅니다.Origin: 요청자의 출처Access-Control-Request-Method: 본 요청 메서드Access-Control-Request-Headers: 본 요청에 포함될 커스텀 헤더 목록Access-Control-Allow-Origin: 허용 출처(정확한 값 또는 *)Access-Control-Allow-Methods: 허용 메서드 목록Access-Control-Allow-Headers: 허용 커스텀 헤더 목록Access-Control-Max-Age: 프리플라이트 결과 캐시 시간(초)Authorization 헤더 등 자격 증명을 동반한 요청.credentials 설정(fetch/XHR): include(쿠키 포함) 또는 same-originAccess-Control-Allow-Credentials: trueAccess-Control-Allow-Origin 에 와일드카드 * 사용 불가, 반드시 정확한 출처를 지정해야 합니다.| 방향 | 헤더 | 설명 |
|---|---|---|
| 요청 | Origin | 요청을 보낸 문서의 출처 |
| 요청(Preflight) | Access-Control-Request-Method | 본 요청의 메서드 |
| 요청(Preflight) | Access-Control-Request-Headers | 본 요청의 커스텀 헤더 목록 |
| 응답 | Access-Control-Allow-Origin | 허용 출처(* 또는 특정 도메인) |
| 응답 | Access-Control-Allow-Methods | 허용 메서드 목록 |
| 응답 | Access-Control-Allow-Headers | 허용 커스텀 헤더 목록 |
| 응답 | Access-Control-Expose-Headers | 자바스크립트에서 접근 가능하도록 노출할 응답 헤더 |
| 응답 | Access-Control-Allow-Credentials | 자격 증명 동반 요청 허용 여부 |
| 응답 | Access-Control-Max-Age | 프리플라이트 캐시 지속 시간(초) |
import express from "express";
import cors from "cors";
const app = express();
app.use(
cors({
origin: ["https://app.example.com", "https://admin.example.com"],
credentials: true, // 쿠키/자격증명 허용
methods: ["GET", "POST", "PUT", "DELETE"],
allowedHeaders: ["Content-Type", "Authorization", "X-Request-Id"],
exposedHeaders: ["X-Request-Id"], // 클라이언트에서 읽을 헤더
maxAge: 600, // 프리플라이트 캐시(초)
})
);
app.get("/api/data", (req, res) => res.json({ ok: true }));
app.listen(3000);
@Configuration
public class CorsConfig {
@Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurer() {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/api/**")
.allowedOrigins("https://app.example.com", "https://admin.example.com")
.allowedMethods("GET","POST","PUT","DELETE")
.allowedHeaders("*")
.exposedHeaders("X-Request-Id")
.allowCredentials(true)
.maxAge(600);
}
};
}
}
location /api/ {
if ($request_method = OPTIONS) {
add_header Access-Control-Allow-Origin "https://app.example.com";
add_header Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS";
add_header Access-Control-Allow-Headers "Content-Type, Authorization, X-Request-Id";
add_header Access-Control-Allow-Credentials "true";
add_header Access-Control-Max-Age 600;
return 204;
}
add_header Access-Control-Allow-Origin "https://app.example.com" always;
add_header Access-Control-Allow-Credentials "true" always;
add_header Access-Control-Expose-Headers "X-Request-Id" always;
proxy_pass http://backend_upstream;
}
Access-Control-Allow-Origin: * 와 Credentials 동시 사용 불가.Access-Control-Allow-Origin 에 반영되는가Allow-Origin 이 * 가 아닌가Allow-Methods/Allow-Headers 에 포함됐는가Max-Age 로 캐시되는가Origin ↔ Access-Control-Allow-Origin 을 비교하고, 필요 시 프리플라이트로 안전성을 검증합니다.Allow-Credentials: true 와 정확한 Origin 지정이 필수이며, 운영 환경에서는 최소 권한 원칙과 프리플라이트 캐시로 성능과 보안을 함께 확보하십시오.