OPTIONS 요청이 전송되는 이유는 무엇이며 비활성화 할 수 있습니까?


415

웹 API를 작성 중입니다. Chrome을 사용하여 POST, API에 GET을 사용할 때마다 실제 요청 전에 항상 OPTIONS 요청이 전송되므로 상당히 성가신 일입니다. 현재 서버가 OPTIONS 요청을 무시하도록합니다. 이제 내 질문은 서버로드를 두 배로 늘리기 위해 OPTIONS 요청을 보내는 것이 좋은 이유는 무엇입니까? 브라우저가 OPTIONS 요청을 보내지 못하게하는 방법이 있습니까?

답변:


376

2018-09-13 편집 : 비행 전 요청에 대한 정밀도와 응답이 끝날 때이를 피하는 방법을 추가했습니다.

OPTIONS요청은 pre-flight에서 요청 이라고 합니다 Cross-origin resource sharing (CORS).

특정 상황에서 다른 출처로 요청을 할 때 필요합니다.

이 비행 전 요청은 일부 브라우저에서 수행되는 요청을 서버에서 신뢰하는지 확인하기위한 안전 조치로 이루어집니다. 서버는 요청시 전송되는 메소드, 오리진 및 헤더가 작동하기에 안전하다는 것을 이해합니다.

교차 요청을 할 때마다 서버는 무시하지 말고 이러한 요청을 처리해야합니다.

http://enable-cors.org/ 에서 좋은 리소스를 찾을 수 있습니다 .

이러한 방법을 편하게 처리 할 수있는 OPTIONS방법은 메소드가 있는 경로에 대해 서버가이 헤더로 응답을 보내도록하는 것입니다.

Access-Control-Allow-Origin: *

그러면 서버가 서버가 모든 출처의 요청에 응답 할 의사가 있음을 브라우저에 알립니다.

서버에 CORS 지원을 추가하는 방법에 대한 자세한 정보는 다음 플로우 차트를 참조하십시오.

http://www.html5rocks.com/static/images/cors_server_flowchart.png

CORS 순서도


2018-09-13 수정

CORS OPTIONS요청은 MDN 문서에 설명 된대로 일부 경우에만 트리거됩니다 .

일부 요청은 CORS 프리 플라이트를 트리거하지 않습니다. Fetch 스펙 (CORS를 정의 함)은이 용어를 사용하지 않지만,이 기사에서는이를 "간단한 요청"이라고합니다. CORS 프리 플라이트를 트리거하지 않는 요청 (소위 "단순 요청")은 다음 조건을 모두 충족하는 요청입니다.

허용되는 유일한 방법은 다음과 같습니다.

  • 가져 오기
  • 머리
  • 게시하다

사용자 에이전트가 자동으로 설정 한 헤더 (예 : Connection, User-Agent 또는 Fetch 사양에 "금지 헤더 이름"으로 정의 된 이름을 가진 다른 헤더) 외에는 다음과 같은 유일한 헤더가 허용됩니다. 수동 설정은 Fetch 사양이 "CORS 안전 요청 헤더"로 정의한 것입니다.

  • 동의하기
  • 언어를 받아들이십시오
  • 내용 언어
  • 콘텐츠 유형 (하지만 아래의 추가 요구 사항에 유의)
  • DPR
  • 다운 링크
  • 데이터를 저장
  • 뷰포트 폭

Content-Type 헤더에 허용되는 유일한 값은 다음과 같습니다.

  • application / x-www-form-urlencoded
  • 멀티 파트 / 폼 데이터
  • 텍스트 / 일반

요청에 사용 된 XMLHttpRequestUpload 객체에는 이벤트 리스너가 등록되어 있지 않습니다. 이들은 XMLHttpRequest.upload 속성을 사용하여 액세스합니다.

요청에 ReadableStream 객체가 사용되지 않습니다.


8
그러나이 Chrome 플래그를 모든 일반 사용자에게 설정하는 것은 현실적이지 않습니다.
Qian Chen

37
교차 출발 요청을 할 때 프리 플라이트 요청이 필요하다고 말하는 것은 잘못입니다. 프리 플라이트 요청은 사용자 정의 헤더를 설정하거나 get, head 및 post 이외의 요청을하는 경우와 같은 특정 상황에서만 필요합니다.
Robin Clowers 16:27에

4
CORS을 할 때 충분히 재미있게 요청, jQuery를 사용하여, 특히 개발자들에게 경고의 말과 함께, 사용자 정의 헤더를 설정 피한다 자바 스크립트 라이브러리 : 크로스 도메인 요청의 경우, 프리 플라이트 조건으로보고 우리는 퍼즐과 유사하다 절대로 확실하게 설정하지 마십시오.
레이더 아래

3
curlAPI에 a 를 사용 하면 어떻게 작동합니까?하지만 크롬에서 실행할 때 오류가 발생합니까?
SuperUberDuper

5
CORS 및 프리 플라이트 요청은 브라우저 관련 문제이므로 @SuperUberDuper. Origin요청이 특정 호스트 (예 : yourwebsite.com)에서 온 것처럼 시뮬레이션하기 위해 요청에 헤더를 추가하여 CORS를 시뮬레이션 할 수 있습니다 . 요청의 HTTP 메소드 OPTIONSAccess-Control-*헤더 를 설정하여 프리 플라이트 요청을 시뮬레이션 할 수도 있습니다.
Leo Correa

234

이 문제를 겪은 다음은이 문제에 대한 결론과 해결책입니다.

CORS 전략 에 따르면 (강제로 읽어 보는 것이 좋습니다) 필요한 경우 브라우저가 OPTIONS 요청 전송을 중지하도록 강요 할 수는 없습니다.

이 문제를 해결할 수있는 두 가지 방법이 있습니다.

  1. 귀하의 요청이 "간단한 요청"인지 확인하십시오
  2. Access-Control-Max-AgeOPTIONS 요청으로 설정

간단한 요청

간단한 사이트 간 요청은 다음 조건을 모두 충족하는 요청입니다.

허용되는 유일한 방법은 다음과 같습니다.

  • 가져 오기
  • 머리
  • 게시하다

사용자 에이전트가 자동으로 설정 한 헤더 (예 : Connection, User-Agent 등) 외에 수동으로 설정할 수있는 유일한 헤더는 다음과 같습니다.

  • 동의하기
  • 언어를 받아들이십시오
  • 내용 언어
  • 컨텐츠 타입

Content-Type 헤더에 허용되는 유일한 값은 다음과 같습니다.

  • application / x-www-form-urlencoded
  • 멀티 파트 / 폼 데이터
  • 텍스트 / 일반

간단한 요청으로 비행 전 OPTIONS 요청이 발생하지 않습니다.

OPTIONS 검사를위한 캐시 설정

Access-Control-Max-AgeOPTIONS 요청에 대해 를 설정하여 만료 될 때까지 권한을 다시 확인하지 않도록 할 수 있습니다.

Access-Control-Max-Age는 다른 프리 플라이트 요청을 보내지 않고 프리 플라이트 요청에 대한 응답을 캐시 할 수있는 시간을 초 단위로 제공합니다.

제한 사항

  • 크롬, 최대 초 동안 Access-Control-Max-AgeIS 600에 따라 10 분, 크롬 소스 코드
  • Access-Control-Max-Age예를 들어 GETURL 경로가 동일한 요청이지만 쿼리마다 다른 리소스는 다른 리소스로 취급됩니다. 따라서 두 번째 자원에 대한 요청은 여전히 ​​프리 플라이트 요청을 트리거합니다.

3
그렇습니다 ...이 질문에 대한 답이되어야합니다.
Rajesh Mbm

7
언급 해 주셔서 감사합니다 Access-Control-Max-Age. 이것이 핵심입니다. 과도한 비행 전 요청을 피하는 데 도움이됩니다.
Idris Mokhtarzada

요청을 불러 오기 위해 axios를 사용하고 있습니다. axios 요청에서 Access-Control-Max-Age를 어디에서 설정할 수 있습니까?
Mohit Shah

+1 Access-Control-Max-Age 헤더가 핵심입니다. 이것이 정답입니다! 헤더에 86400 초 (24 시간)를 설정했으며 prefligth 요청이 사라졌습니다!
revobtz

1
@VitalyZdanevich 아니! application/json요청을 "단순"하게하므로 CORS를 트리거하기 때문에 피하지 마십시오 . 브라우저가 작업을 수행하고 있습니다. 서버가 헤더와 같은 것을 반환하도록 설정 Access-Control-Max-Age: 86400하면 브라우저가 24 시간 동안 OPTIONS 요청을 다시 보내지 않습니다.
colm.anseo

139

사전 비행 옵션 요청의 실제 필요성에 대해서는이 답변을 참조하십시오. CORS- 사전 비행 요청을 도입 한 동기는 무엇입니까?

OPTIONS 요청을 비활성화하려면 ajax 요청에 대해 다음 조건이 충족되어야합니다.

  1. 요청이 'application / xml'또는 'application / json'등과 같은 사용자 정의 HTTP 헤더를 설정하지 않습니다.
  2. 요청 방법은 GET, HEAD 또는 POST 중 하나 여야합니다. POST 경우 콘텐츠 유형 중 하나 여야합니다 application/x-www-form-urlencoded, multipart/form-data또는text/plain

참조 : https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS


14
"커스텀 HTTP 헤더"+1! 제 경우에는 비행 전 요청이 트리거되었습니다. 요청 본문과 OPTIONS 요청의 전송이 중지됨에 따라 헤더에서 전송하는 내용을 보내도록 요청을 리팩터링했습니다.
Andre

21
application/xml또는 application/json"사용자 정의 HTTP 헤더"가 아닙니다. 헤더 자체는 Content-Type해당 헤더를 "custom"이라고 부르는 것이 오해의 소지가 있습니다.
레오 코레아

1
사용자 정의 HTTP 헤더를 제거했으며 이것은 매력처럼 작동했습니다!
Tim D

47

디버그 콘솔을 열고 Disable Cache옵션을 켜면 프리 플라이트 요청이 항상 전송됩니다 (즉, 각 요청 전에). 캐시를 비활성화하지 않으면 프리 플라이트 요청이 서버 당 한 번만 전송됩니다.


3
내가 무슨 생각을하는지 몇 시간 동안 디버깅하는 것이 내 솔루션이었습니다. 콘솔 디버깅으로 인해 캐시가 비활성화되었습니다.
mauris

1
디버그 콘솔이 닫혀도 프리 플라이트 요청이 전송됩니다
Luca Perico

2
루카 : 사실이지만 요점은 개발자 도구를 닫을 때 "캐시 비활성화"가 효과가 없다는 것입니다. 캐시가 비활성화되지 않은 경우 프리 플라이트 요청은 서버 당 한 번만 전송되며 캐시가 비활성화 된 경우 각 요청 전에 전송됩니다.
Nir

정말 도움이되었습니다.
Anuraag Patil

38

예, 옵션 요청을 피할 수 있습니다. 옵션 요청은 데이터를 다른 도메인으로 보내거나 게시 할 때 프리 플라이트 요청입니다. 브라우저 보안 문제입니다. 그러나 다른 기술인 iframe 전송 계층을 사용할 수 있습니다. CORS 구성을 잊어 버리고 기성품 솔루션을 사용하는 것이 좋습니다. 어디에서나 작동합니다.

https://github.com/jpillora/xdomain을 살펴보십시오.

작업 예제 : http://jpillora.com/xdomain/


이것은 실제로 일종의 드롭 인 프록시입니까?
matanster

15
"옵션 요청은 다른 도메인으로 데이터를 보내거나 게시 할 때 프리 플라이트 요청입니다." - 그건 사실이 아니야. XHR을 사용하여 프리 플라이트 요청을 트리거하지 않고 일반 HTML 양식으로 보낼 수있는 POST 요청을 보낼 수 있습니다. 프리 플라이트가 전송되는 양식이 수행 할 수없는 작업 (예 : 사용자 정의 컨텐츠 유형 또는 추가 요청 헤더)을 시작하는 경우에만 해당됩니다.
Quentin

이 솔루션은 경우에 따라 작동하지만 몇 가지 주요 제한이있는 iframe 심에 의존하는 것으로 보입니다. 응답의 HTTP 상태 코드 또는 다른 HTTP 응답 헤더의 값을 알고 싶다면 어떻게됩니까?
Stephen Crosby

5
이를 위해 iFrame이 만들어지지 않았습니다.
Romko

1
이것은 소프트웨어 구현이며 "브라우저가 OPTIONS 요청을 보내지 못하도록 완전히 막을 방법이 있습니까?"라는 최종 질문에 대답합니다. 간단히 말해서, Mozilla 또는 Chromium에서 비활성화 할 수있는 방법이 없으며 코드로 구현되며 유일한 "작동"옵션은 동작을 우회합니다.
청소부

15

존재 이유를 이해하지만 인증없이 OPTIONS 호출을 처리하지 않는 API에 액세스해야하는 개발자의 경우 API 소유자가 적절한 SPA CORS 지원을 추가하거나 프록시 API를받을 때까지 로컬로 개발할 수 있도록 임시 답변이 필요합니다. 가동.

Mac의 Safari 및 Chrome에서 CORS를 비활성화 할 수 있다는 것을 알았습니다.

Chrome에서 동일한 출처 정책을 사용 중지합니다.

Chrome : Chrome을 종료하고 터미널을 열고 다음 명령을 붙여 넣습니다. open /Applications/Google\ Chrome.app --args --disable-web-security --user-data-dir

Safari : Safari에서 동일한 출처 정책 비활성화

Safari에서 동일한 출처 정책을 비활성화하려면 (9.1.1이 있음) 개발자 메뉴 만 활성화하고 개발 메뉴에서 "교차 원본 제한 비활성화"를 선택하면됩니다.


4
당신은 말한다 부분에 더 강조를 넣어해야 "이것은 permament 솔루션 않을 것입니다 !!" . 동일한 출처 정책은 매우 중요한 브라우저 보안 수단이며 일반적으로 인터넷을 탐색 할 때 비활성화해서는 안됩니다.
jannis

웹이 이런 식으로 작동하기를 원하므로 별도의 번거 로움없이 서버에서 필요한 데이터를 요청할 수 있습니다.
jemiloii

14

이전 게시물에서 이미 언급했듯이 OPTIONS 요청은 이유가 있습니다. 서버의 응답 시간이 큰 문제 (예 : 해외 연결)가있는 경우 브라우저가 프리 플라이트 요청을 캐시하도록 할 수도 있습니다.

서버가 Access-Control-Max-Age헤더로 응답 하고 동일한 엔드 포인트로가는 요청에 대해 프리 플라이트 요청이 캐시되어 더 이상 발생하지 않습니다.


1
감사합니다! OPTIONS내가 읽은 모든 CORS 문서에서 요청 이이 헤더와 함께 캐시 된다는 사실 은 매우 불투명합니다.
joshperry

캐시는 정확히 동일한 URL로만 적용됩니다. 왕복을 실제로 줄일 수있는 도메인 수준 프리 플라이트 캐시를 원합니다. (CORS은 바보입니다!)
경이

8

나는이 문제를 좋아했다.

if($_SERVER['REQUEST_METHOD'] == 'OPTIONS' && ENV == 'devel') {
    header('Access-Control-Allow-Origin: *');
    header('Access-Control-Allow-Headers: X-Requested-With');
    header("HTTP/1.1 200 OK");
    die();
}

개발만을위한 것입니다. 이것으로 나는 9ms와 500ms를 기다리고 있지만 8s와 500ms는 아닙니다. 프로덕션 JS 앱은 프로덕션과 동일한 시스템에 있으므로 OPTIONS개발은 로컬이 아니기 때문에 그렇게 할 수 있습니다 .


4

JSONP를 사용하여 CORS를 피할 수는 없습니다.


2
단순하지 않은 작업을 수행하는 경우에만 OPTIONS 요청을받습니다 . JSONP를 사용하여 간단한 요청 (GET, 사용자 정의 헤더, 인증 데이터 없음) 만 수행 할 수 있으므로 여기서 JSONP를 대체 할 수 없습니다.
Quentin

예, 알고 있지만 정확한 프로젝트 요구 사항을 모릅니다. 나는 그것이 단순한 cors를 피하는 것이 아니라 프로젝트에 달려 있다는 것을 알고 있습니다. CORS를 피하기위한 최악의 시나리오에서는 get 매개 변수를 사용하여 데이터를 전달해야합니다. 따라서 JSONP는 프로젝트 요구 사항에 따라 간단한 요청을 사용하여 cors를 대체 할 수 있습니다.
Jose Mato

소위 사전 비행의 설계를 이해하지 못합니다. 클라이언트가 서버로 데이터를 전송하기로 결정한 경우 안전하지 않은 원인은 무엇입니까? 나는 와이어의 부하를 두 배로하는 것이 의미가 없다고 생각합니다.
Qian Chen

@ElgsQianChen 이것은 아마도 귀하의 질문에 대답 할 수 있습니다 stackoverflow.com/questions/15381105/…
Leo Correa

0

하루 종일 동안 비슷한 문제를 해결하려고 노력한 후 IIS 와 관련이 있음을 알았습니다. .

내 웹 API 프로젝트는 다음과 같이 설정되었습니다.

// WebApiConfig.cs
public static void Register(HttpConfiguration config)
{
    var cors = new EnableCorsAttribute("*", "*", "*");
    config.EnableCors(cors);
    //...
}

많은 게시물에서 보았던 것처럼 web.config> system.webServer 노드에 CORS 특정 구성 옵션이 없었습니다.

global.asax 또는 컨트롤러에 데코레이터로 CORS 특정 코드가 없습니다.

문제는 앱 풀 설정 이었습니다.

관리되는 파이프 라인 모드가 클래식 (로 설정되어 통합으로 변경 )과 ID가 네트워크 서비스로 설정 ( ApplicationPoolIdentity로 변경 )

해당 설정을 변경하고 (앱 풀을 새로 고침) 설정이 해결되었습니다.


-2

나를 위해 일한 것은 "github.com/gorilla/handlers"를 가져온 다음 다음과 같이 사용하는 것입니다.

router := mux.NewRouter()
router.HandleFunc("/config", getConfig).Methods("GET")
router.HandleFunc("/config/emcServer", createEmcServers).Methods("POST")

headersOk := handlers.AllowedHeaders([]string{"X-Requested-With", "Content-Type"})
originsOk := handlers.AllowedOrigins([]string{"*"})
methodsOk := handlers.AllowedMethods([]string{"GET", "HEAD", "POST", "PUT", "OPTIONS"})

log.Fatal(http.ListenAndServe(":" + webServicePort, handlers.CORS(originsOk, headersOk, methodsOk)(router)))

Ajax POST 요청을 실행하고 JSON 데이터를 첨부하자마자 Chrome은 항상 이전 AllowedHeaders 구성에없는 Content-Type 헤더를 추가합니다.


-2

과거에 사용한 한 가지 솔루션-사이트가 mydomain.com에 있다고 가정하고 foreigndomain.com에 아약스 요청을해야합니다.

도메인에서 외부 도메인으로 IIS 다시 쓰기 구성

<rewrite>
  <rules>
    <rule name="ForeignRewrite" stopProcessing="true">
        <match url="^api/v1/(.*)$" />
        <action type="Rewrite" url="https://foreigndomain.com/{R:1}" />
    </rule>
  </rules>
</rewrite>

mydomain.com 사이트에서 동일한 출발지 요청을 할 수 있으며 옵션 요청이 필요 없습니다. :)


-2

요청을 가로 채고 적절한 헤더를 작성하는 프록시를 사용하는 경우 해결할 수 있습니다. 니스의 특별한 경우에는 다음과 같은 규칙이 있습니다.

if (req.http.host == "CUSTOM_URL" ) {
set resp.http.Access-Control-Allow-Origin = "*";
if (req.method == "OPTIONS") {
   set resp.http.Access-Control-Max-Age = "1728000";
   set resp.http.Access-Control-Allow-Methods = "GET, POST, PUT, DELETE, PATCH, OPTIONS";
   set resp.http.Access-Control-Allow-Headers = "Authorization,Content-Type,Accept,Origin,User-Agent,DNT,Cache-Control,X-Mx-ReqToken,Keep-Alive,X-Requested-With,If-Modified-Since";
   set resp.http.Content-Length = "0";
   set resp.http.Content-Type = "text/plain charset=UTF-8";
   set resp.status = 204;
}

}


-5

해결책이있을 수 있습니다 (그러나 테스트하지는 않았습니다) .CSP (Content Security Policy)를 사용하여 원격 도메인을 활성화 할 수 있으며 브라우저는 CORS OPTIONS 요청 확인을 건너 뛸 수 있습니다.

시간이 있으면 테스트 하고이 게시물을 업데이트합니다!

CSP : https://developer.mozilla.org/fr/docs/Web/HTTP/Headers/Content-Security-Policy

CSP 사양 : https://www.w3.org/TR/CSP/


방금 테스트했지만 작동하지 않습니다. xhr 요청 승인을 위해 CSP 후에도 CORS가 계속 필요합니다 ...
Arnaud Tournier
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.