CORS (Cross-Origin Resource Sharing) 설정

ILOV-IT·2026년 1월 22일

내가 shop.com에 들어갔을 때, 그 페이지의 자바스크립트가 https://bank.com/acc/balance 를 호출해 잔액 응답(JSON)을 그대로 읽을 수 있다면? 그러면 당연히 사용자가 정보 보호가 되지 않는다.

CORS는 “내가 로그인해 둔 사이트의 데이터(응답, DOM 등)가 다른 사이트의 자바스크립트에 의해 몰래 읽히지 않게” 막기 위한 정책이다. 예를 들어 shop.com에서 실행 중인 JS가 https://bank.com/acc/balance 를 fetch로 호출해도, 응답 내용을 읽는 건 기본적으로 차단된다.

이렇게 차단하는 정책은 서버가 응답에 넣어주는 CORS 헤더에 따라 브라우저가 행동하기 때문이다. 여기서 서버는 FastAPI, Nginx 설정 등을 말하며, 그 흐름은 다음과 같다.

  1. 사용자가 https://app.com 페이지에서 JS 실행
  2. 그 JS가 https://api.com 으로 요청 보냄. 이때 브라우저가 자동으로 요청에 Origin: https://app.com 헤더를 붙임
  3. 서버(또는 Nginx)가 응답에 Access-Control-Allow-Origin: https://app.com 를 붙임
  4. 브라우저가 확인하고, 허용이면 JS가 응답(JSON)을 읽게 해주고, 불허이면 요청은 갔어도, JS가 응답을 못 읽게 막는다.(콘솔에 CORS 에러)

즉, 사용자가 app.com 페이지에 있고, 거기서 JS가 fetch("https://api.com")을 하면 요청은 할 수 있다. 그런데 브라우저는 기본 규칙(SOP) 때문에 “다른 출처(api.com) 응답을 app.com의 JS가 읽는 건 원칙적으로 허용하지 않는다. 그래서 api.com 서버가 응답에 CORS 헤더(예: Access-Control-Allow-Origin: https://app.com)를 붙여서 "app.com에서 온 JS가 이 응답 읽어도 됨”이라고 예외 허가를 주면, res.json()이 동작하게 된다는 의미이다.

따라서 API 앱(FastAPI)에서 CORSMiddleware로 붙여도 되고, 앞단 프록시(Nginx)에서 add_header로 붙여도 된다. 둘 다 하면 충돌/중복 나기 쉬우니 한 군데에서만 관리하는 게 보통 깔끔하다. (특히 OPTIONS 프리플라이트 처리까지 포함해서)

$ sudo cat conf.d/cors_map.conf
# /etc/nginx/conf.d/cors_map.conf

# 허용할 Origin만 그대로 반환 (그 외/없음은 빈 문자열)
map $http_origin $cors_allow_origin {
    default "";
    "" "";

    "https://aaaaa.kr"      $http_origin;
    "https://www.aaaaa.kr"  $http_origin;

    "http://localhost:1212" $http_origin;
    "http://localhost:1313" $http_origin;
}

# Origin이 있고(=CORS 요청) 허용목록 아니면 1(차단), 나머지는 0(통과)
map $http_origin $cors_block {
    default 1;
    "" 0;

    "https://aaaaa.kr"      0;
    "https://www.aaaaa.kr"  0;

    "http://localhost:1212" 0;
    "http://localhost:1313" 0;
}
$ sudo cat conf.d/api.aaaaa.kr.conf

upstream aaaaa_api {
    server 127.0.0.1:18000;
    keepalive 64;
}

# -----------------------------------------------------------
# 80: 
# -----------------------------------------------------------
server {
    listen 80;
    listen [::]:80;
    server_name api.aaaaa.kr;

    return 301 https://$host$request_uri;
}

# -----------------------------------------------------------
# 443: 
# -----------------------------------------------------------
server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    server_name api.aaaaa.kr;

    include /etc/nginx/snippets/ssl_cloudflare_origin.conf;
    client_max_body_size 100M;

    # 업스트림(FastAPI)이 CORS 헤더를 주면 중복 방지로 제거
    proxy_hide_header Access-Control-Allow-Origin;
    proxy_hide_header Access-Control-Allow-Credentials;
    proxy_hide_header Access-Control-Allow-Methods;
    proxy_hide_header Access-Control-Allow-Headers;
    proxy_hide_header Access-Control-Max-Age;
    proxy_hide_header Vary;

    location / {

        # 허용 Origin 아니면 차단
        if ($cors_block) { return 403; }

        # 프리플라이트(OPTIONS) 즉시 처리
        if ($request_method = OPTIONS) {
            add_header Access-Control-Allow-Origin      $cors_allow_origin always;
            add_header Access-Control-Allow-Credentials "true" always;
            add_header Access-Control-Allow-Methods     "GET, POST, PUT, PATCH, DELETE, OPTIONS" always;
            add_header Access-Control-Allow-Headers     "Authorization, Content-Type, X-CSRF-Token, X-Requested-With, X-Platform, X-Device-Id, X-Attestation-Token, X-API-KEY, Accept, Origin" always;
            add_header Access-Control-Max-Age           600 always;
            add_header Vary                             "Origin" always;
            return 204;
        }

        # 일반 요청에도 CORS 헤더 부착
        add_header Access-Control-Allow-Origin      $cors_allow_origin always;
        add_header Access-Control-Allow-Credentials "true" always;
        add_header Vary                             "Origin" always;

        proxy_pass http://aaaaa_api;
        proxy_http_version 1.1;

        proxy_set_header Host $host;

    }

    location ~ /\.(?!well-known) {
        deny all;
    }
}

.
도메인 여러 개가 하나의 API를 쓴다면, 하나의 도메인으로 정규화(Canonical)하는 것이 유리히다. 예컨대, https://www.aaaaa.kr, https://aaaaa.kr, https://www.aaaaa.co.kr https://aaaaa.co.kr 4개가 있다면, CORS allowlist도 4개가 된다. 기술적으로는 4개를 허용하면 되지만, 하나로 리다이렉트하는 실무적으로 편리하다. 대안으로는 프론트를 aaaaa.kr로 고정하고, Nginx가 /api를 내부적으로 api.aaaaa.kr(또는 업스트림)으로 프록시하면 CORS 자체가 필요 없어진다.

profile
because we know you'll love it

0개의 댓글