프로토콜 + 도메인 + 포트
https://example.com
http://example.com
https://api.example.com
https://example.com:3000
https://example.com:8080
| 항목 | 값 |
|---|---|
| 프로토콜 | http, https |
| 도메인 | example.com, api.example.com |
| 포트 | 3000, 8080, 443 |
React 개발 서버: http://localhost:5173
NestJS API 서버: http://localhost:3000
http://localhost:5173 !== http://localhost:3000
프론트엔드: https://www.example.com
백엔드 API: https://api.example.com
https://www.example.com의 요청을 허용하도록 CORS 설정을 해야 합니다.사용자가 bank.com에 로그인한 상태
↓
악성 사이트 evil.com 접속
↓
evil.com이 bank.com API에 몰래 요청
↓
브라우저가 보안 정책으로 차단
Access to fetch at 'https://api.example.com/users'
from origin 'https://www.example.com'
has been blocked by CORS policy.
Postman 요청 성공
브라우저 요청 실패
→ 서버 API 자체는 살아 있지만, 브라우저에서 허용되지 않은 요청일 수 있음
Access-Control-Allow-Origin: https://www.example.com
Access-Control-Allow-Origin: *
*를 사용할 수 없습니다.Access-Control-Allow-Methods: GET, POST, PATCH, DELETE, OPTIONS
Access-Control-Allow-Headers: Content-Type, Authorization
Authorization 헤더로 보낼 경우 반드시 허용해야 합니다.Access-Control-Allow-Credentials: true
credentials: "include" 또는 axios의 withCredentials: true를 사용할 때 필요합니다.조건이 단순한 요청은 브라우저가 바로 서버에 보냅니다.
예시:
GETPOSTContent-TypeGET /api/products
OPTIONS 메서드로 서버에 확인 요청을 보냅니다.OPTIONS /api/admin/users
Origin: https://www.example.com
Access-Control-Request-Method: DELETE
Access-Control-Request-Headers: Authorization
DELETE /api/admin/users/1
Authorization: Bearer access-token
Authorization 헤더를 포함한 요청Content-Type: application/json 요청PUT, PATCH, DELETE 요청main.ts에서 CORS 설정을 적용할 수 있습니다.const app = await NestFactory.create(AppModule);
app.enableCors();
await app.listen(3000);
const app = await NestFactory.create(AppModule);
app.enableCors({
origin: ['https://www.example.com', 'https://admin.example.com'],
methods: ['GET', 'POST', 'PATCH', 'DELETE', 'OPTIONS'],
allowedHeaders: ['Content-Type', 'Authorization'],
});
await app.listen(3000);
const app = await NestFactory.create(AppModule);
app.enableCors({
origin: ['https://www.example.com'],
credentials: true,
});
await app.listen(3000);
credentials: true가 필요합니다.origin: '*'와 함께 사용할 수 없습니다.await fetch('https://api.example.com/auth/me', {
method: 'GET',
credentials: 'include',
});
import axios from 'axios';
const api = axios.create({
baseURL: 'https://api.example.com',
withCredentials: true,
});
Access-Control-Allow-Credentials: true를 설정해도, 프론트엔드가 credentials 옵션을 넣지 않으면 쿠키가 함께 전송되지 않을 수 있습니다.Authorization 헤더에 담아 보내는 경우도 CORS 설정이 필요합니다.await fetch('https://api.example.com/admin/users', {
method: 'GET',
headers: {
Authorization: `Bearer ${accessToken}`,
},
});
app.enableCors({
origin: ['https://admin.example.com'],
allowedHeaders: ['Content-Type', 'Authorization'],
});
Authorization 헤더가 허용되지 않으면 브라우저가 요청을 차단할 수 있습니다.location /api/ {
proxy_pass http://localhost:3000;
add_header Access-Control-Allow-Origin "https://www.example.com";
add_header Access-Control-Allow-Methods "GET, POST, PATCH, DELETE, OPTIONS";
add_header Access-Control-Allow-Headers "Content-Type, Authorization";
if ($request_method = OPTIONS) {
return 204;
}
}
*를 사용함app.enableCors({
origin: '*',
credentials: true,
});
axios.get('/auth/me');
withCredentials: true가 없으면 쿠키가 요청에 포함되지 않을 수 있습니다.axios.get('/auth/me', {
withCredentials: true,
});
Set-Cookie: refreshToken=abc; HttpOnly; Secure; SameSite=Lax
SameSite=None; Secure가 필요합니다.Set-Cookie: refreshToken=abc; HttpOnly; Secure; SameSite=None
SameSite=None은 HTTPS 환경에서 Secure와 함께 사용해야 합니다.프론트엔드: http://localhost:5173
백엔드: http://127.0.0.1:3000
localhost와 127.0.0.1은 개발자가 보기에는 비슷하지만 브라우저 입장에서는 다른 Origin으로 처리될 수 있습니다.CORS: 브라우저 요청 제한
인증: 사용자가 누구인지 확인
인가: 해당 기능을 사용할 권한이 있는지 확인
origin: '*'로 열어버리면 안 됩니다.OPTIONS 요청이 실패했는지 확인Access-Control-Allow-Origin이 있는지 확인localhost와 127.0.0.1을 혼용하지 않는가?origin: '*'를 사용하지 않는가?credentials: true와 구체적인 Origin을 사용했는가?React + NestJS 프로젝트에서 CORS 에러가 발생해.
프론트엔드 주소: http://localhost:5173
백엔드 주소: http://localhost:3000
인증 방식: JWT를 Authorization 헤더로 전송
요청 코드:
fetch('/api/admin/users', {
headers: {
Authorization: `Bearer ${token}`
}
})
NestJS CORS 설정:
app.enableCors({
origin: ['http://localhost:5173'],
allowedHeaders: ['Content-Type']
})
브라우저 에러:
Request header field authorization is not allowed by Access-Control-Allow-Headers
어디를 수정해야 하는지 설명해줘.
origin: '*'로 무조건 열라고 하지 않는가?Access-Control-Allow-Origin, Access-Control-Allow-Methods, Access-Control-Allow-Headers, Access-Control-Allow-Credentials 같은 헤더로 허용 범위를 알려줍니다.Authorization 헤더, JSON 요청, PATCH, DELETE, 쿠키 인증 요청 등은 Preflight 요청이 발생할 수 있습니다.app.enableCors()로 CORS를 설정할 수 있고, 운영 환경에서는 허용 Origin을 명확히 제한해야 합니다.credentials: true, 프론트엔드의 withCredentials: true 또는 credentials: "include" 설정이 함께 필요합니다.