업데이트됨 1개월 전
OPTIONS 메서드는 서버에 간단한 질문을 던집니다: "이 리소스로 뭘 할 수 있나요?" 하지만 그건 지루한 답변입니다.
흥미로운 진실은 이렇습니다: OPTIONS는 브라우저가 자신이 실행 중인 웹 페이지를 신뢰하지 않기 때문에 존재합니다. 한 웹사이트의 JavaScript가 다른 서버와 통신하려 할 때, 브라우저가 나서서 그 서버에 물어봅니다: "어떤 코드가 요청을 보내려 합니다. 괜찮으신가요?"
이것이 바로 사전 요청(preflight request)이며, 이 질문을 던지는 것이 OPTIONS입니다.
OPTIONS 응답에 담긴 것
OPTIONS는 허용된 작업에 대한 정보를 요청합니다:
Allow 헤더는 서버가 이 리소스에 대해 지원하는 모든 메서드를 나열합니다. Access-Control-* 헤더들은 CORS를 위한 것으로, 어떤 교차 출처 요청이 허용되는지 브라우저에 알려줍니다.
사전 요청 과정
여기서 OPTIONS가 진가를 발휘합니다.
https://app.example.com의 JavaScript가 다음 요청을 보내고 싶을 때:
브라우저는 그냥 전송하지 않습니다. 그럴 수가 없어요. 이건 데이터를 수정할 수 있는 교차 출처 요청입니다. 브라우저는 신뢰할 수 없는 JavaScript로부터 서버를 보호하고 있습니다—사용자가 직접 실행하기로 선택한 JavaScript라도 마찬가지입니다.
그래서 브라우저는 자동으로 사전 요청을 보냅니다:
풀어 쓰면: "app.example.com의 JavaScript가 이 헤더들로 PUT을 보내려 합니다. 허용하시겠어요?"
서버가 응답합니다:
"네, 해당 출처는 그 헤더들로 PUT을 보낼 수 있습니다. 이 응답은 24시간 동안 캐시해도 됩니다."
그 후에야 브라우저가 실제 PUT 요청을 전송합니다.
일부 요청이 사전 요청을 건너뛰는 이유
모든 교차 출처 요청이 OPTIONS를 유발하지는 않습니다. "단순 요청"은 바로 통과됩니다:
- 메서드: GET, HEAD, 또는 POST
- 헤더:
Accept,Accept-Language,Content-Type같은 기본 헤더만 - Content-Type:
application/x-www-form-urlencoded,multipart/form-data, 또는text/plain만
이것들은 HTML 폼이 언제나 할 수 있었던 것들을 반영합니다—JavaScript 이전에, fetch 이전에, API 이전에. 브라우저는 이런 요청들을 안전하다고 간주합니다. 늘 가능했던 것들이니까요.
그 외의 것들은 사전 요청을 유발합니다:
사전 요청 응답 캐싱
Access-Control-Max-Age: 86400은 브라우저에게 사전 요청 응답을 캐시하라고 지시합니다. 이게 없으면 PUT이나 DELETE를 보낼 때마다 두 번씩 왕복해야 합니다—OPTIONS 먼저, 그 다음 실제 요청.
하지만 브라우저마다 자체 제한을 적용합니다1:
| 브라우저 | 최대 캐시 시간 |
|---|---|
| Firefox | 24시간 |
| Chrome | 2시간 |
| Safari | 5분 |
max-age를 86400(24시간)으로 설정해두면 브라우저가 알아서 자체 제한을 적용합니다. 기본값은 고작 5초입니다.
OPTIONS 구현
대부분의 프레임워크는 OPTIONS를 자동으로 처리합니다:
직접 구현하는 경우:
서버 전체 탐색을 위한 OPTIONS
*에 OPTIONS를 보내면 서버 전체를 조회할 수 있습니다:
"이 특정 리소스로 뭘 할 수 있나요?"가 아니라 "이 서버는 전반적으로 어떤 메서드를 지원하나요?"를 묻는 것입니다.
보안: 진짜 위험
OPTIONS도 인증을 준수해야 합니다. 비밀 리소스가 존재한다는 것을 드러내지 마세요:
사용자가 알아서는 안 되는 리소스에 Allow: GET, PUT, DELETE를 절대 반환하지 마세요.
가장 위험한 CORS 실수는 와일드카드 사용이 아닙니다—브라우저는 설계상 Access-Control-Allow-Origin: *와 Access-Control-Allow-Credentials: true를 함께 사용하는 것을 차단합니다2. 진짜 위험은 출처 반영(origin reflection): 수신한 Origin 헤더를 그대로 돌려주는 것입니다.
이렇게 하면 어떤 웹사이트든 여러분의 API에 인증된 요청을 보낼 수 있게 됩니다. 항상 허용 목록으로 출처를 검증하세요:
OPTIONS로 CORS 디버깅하기
CORS가 이유 모를 오류를 일으킬 때, OPTIONS가 진단 도구가 됩니다:
이 명령으로 서버가 허용하는 것이 무엇인지—또는 왜 사전 요청을 거부하는지—정확히 확인할 수 있습니다.
핵심 정리
- OPTIONS는 "이 리소스로 뭘 할 수 있나요?"라고 묻고, 허용된 메서드를
Allow헤더에 담아 반환합니다. - 진짜 목적은 CORS 사전 요청입니다: JavaScript의 교차 출처 요청을 허용하기 전에 브라우저가 서버에 허가를 구하는 것입니다.
- 단순 요청(기본적인 GET, HEAD, 단순 헤더를 사용한 POST)은 사전 요청을 건너뜁니다. 그 외 모든 것은 사전 요청을 유발합니다.
Access-Control-Max-Age로 사전 요청 응답을 캐시해 왕복 횟수를 줄이세요. 브라우저는 이를 5분(Safari)에서 24시간(Firefox) 사이로 제한합니다.- 정보를 유출하지 마세요—다른 메서드와 마찬가지로 OPTIONS 응답에서도 인증을 준수해야 합니다.
- 브라우저가 문지기입니다. OPTIONS는 서버가 브라우저에게 무엇을 통과시켜도 되는지 알려주는 수단입니다.
HTTP OPTIONS에 관해 자주 묻는 질문
PUT이나 DELETE 전에 왜 API가 OPTIONS 요청을 받나요?
브라우저의 사전 확인입니다. JavaScript가 데이터를 수정할 수 있는 교차 출처 요청을 보내거나 커스텀 헤더를 사용할 때, 브라우저는 서버가 허용하는지 확인하기 위해 자동으로 OPTIONS를 먼저 보냅니다. 여러분의 코드가 하는 것이 아닙니다—잠재적으로 악의적인 JavaScript로부터 서버를 보호하는 브라우저가 하는 것입니다.
사전 요청을 없앨 수 있나요?
사전 요청이 필요한 요청에 대해서는 완전히 없앨 수 없습니다—그것은 브라우저 보안이 설계대로 작동하는 것입니다. 하지만 응답에 Access-Control-Max-Age를 설정하면 빈도를 줄일 수 있습니다. 또한 가능한 곳에서 단순 요청(GET, 폼 형식 콘텐츠 타입의 POST)을 사용하도록 API를 설계할 수도 있습니다.
왜 CORS가 존재하나요? 개발만 더 어렵게 만드는 것 같은데요.
CORS는 악의적인 웹사이트가 여러분의 브라우저와 쿠키, 인증 정보를 이용해 다른 사이트를 공격하는 것을 막습니다. CORS가 없다면 어떤 웹사이트든 여러분의 브라우저를 통해 은행 API에 인증된 요청을 보낼 수 있게 됩니다. 개발 중에 겪는 불편함은 그 보호를 위한 대가입니다. 프로덕션에서는 올바른 CORS 설정을 사용하고, 개발 환경에서만 더 관대한 설정을 적용하세요.
출처
이 페이지가 도움이 되었나요?