Tại sao một yêu cầu TÙY CHỌN được gửi và tôi có thể vô hiệu hóa nó không?


415

Tôi đang xây dựng một API web. Tôi đã tìm thấy bất cứ khi nào tôi sử dụng Chrome để POST, GET vào API của mình, luôn có một yêu cầu TÙY CHỌN được gửi trước yêu cầu thực sự, điều này khá khó chịu. Hiện tại tôi nhận được máy chủ để bỏ qua bất kỳ yêu cầu TÙY CHỌN. Bây giờ câu hỏi của tôi là những gì tốt để gửi một yêu cầu TÙY CHỌN để tăng gấp đôi tải của máy chủ? Có cách nào để ngăn chặn hoàn toàn trình duyệt gửi yêu cầu TÙY CHỌN không?

Câu trả lời:


376

chỉnh sửa 2018-09-13 : đã thêm một số quy định về yêu cầu trước chuyến bay này và cách tránh yêu cầu này vào cuối phản hồi này.

OPTIONSyêu cầu là những gì chúng ta gọi pre-flightyêu cầu trong Cross-origin resource sharing (CORS).

Chúng cần thiết khi bạn thực hiện các yêu cầu qua các nguồn gốc khác nhau trong các tình huống cụ thể.

Yêu cầu trước chuyến bay này được một số trình duyệt thực hiện như một biện pháp an toàn để đảm bảo rằng yêu cầu được thực hiện được máy chủ tin cậy. Có nghĩa là máy chủ hiểu rằng phương thức, nguồn gốc và tiêu đề được gửi theo yêu cầu là an toàn để hành động.

Máy chủ của bạn không nên bỏ qua nhưng xử lý các yêu cầu này bất cứ khi nào bạn cố gắng thực hiện các yêu cầu xuất xứ chéo.

Một nguồn tài nguyên tốt có thể được tìm thấy ở đây http://enable-cors.org/

Một cách để xử lý những điều này để có được sự thoải mái là đảm bảo rằng đối với bất kỳ đường dẫn nào có OPTIONSphương thức, máy chủ sẽ gửi phản hồi với tiêu đề này

Access-Control-Allow-Origin: *

Điều này sẽ cho trình duyệt biết rằng máy chủ sẵn sàng trả lời các yêu cầu từ bất kỳ nguồn gốc nào.

Để biết thêm thông tin về cách thêm hỗ trợ CORS vào máy chủ của bạn, hãy xem sơ đồ sau

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

Lưu đồ CORS


chỉnh sửa 2018-09-13

OPTIONSYêu cầu CORS chỉ được kích hoạt trong các trường hợp somes, như được giải thích trong tài liệu MDN :

Một số yêu cầu không kích hoạt đèn pha CORS. Những cái đó được gọi là những yêu cầu đơn giản của người dùng trong bài viết này, mặc dù thông số Fetch (định nghĩa CORS) không sử dụng thuật ngữ đó. Một yêu cầu không kích hoạt tiền tố CORS, một cái gọi là yêu cầu đơn giản, đó là một yêu cầu đơn giản đáp ứng tất cả các điều kiện sau:

Các phương thức được phép duy nhất là:

  • ĐƯỢC
  • CÁI ĐẦU
  • BÀI ĐĂNG

Ngoài các tiêu đề được đặt tự động bởi tác nhân người dùng (ví dụ: Kết nối, Tác nhân người dùng hoặc bất kỳ tiêu đề nào khác có tên được xác định trong thông số Fetch là một tiêu đề bị cấm tên), các tiêu đề duy nhất được phép được đặt thủ công là những cái mà đặc tả Fetch định nghĩa là một tiêu đề yêu cầu được bảo vệ an toàn bởi CORS, đó là:

  • Chấp nhận
  • Ngôn ngữ chấp nhận
  • Nội dung ngôn ngữ
  • Loại nội dung (nhưng lưu ý các yêu cầu bổ sung bên dưới)
  • Sở DPR
  • Đường xuống
  • Lưu dữ liệu
  • Chiều rộng khung nhìn
  • Chiều rộng

Các giá trị được phép duy nhất cho tiêu đề Kiểu nội dung là:

  • application / x-www-form-urlencoding
  • nhiều dữ liệu / biểu mẫu
  • văn bản / đồng bằng

Không có trình lắng nghe sự kiện nào được đăng ký trên bất kỳ đối tượng XMLHttpRequestUpload nào được sử dụng trong yêu cầu; chúng được truy cập bằng thuộc tính XMLHttpRequest.upload.

Không có đối tượng ReadableStream được sử dụng trong yêu cầu.


8
Nhưng việc đặt cờ Chrome này cho tất cả người dùng phổ thông là không thực tế.
Qian Chen

37
Không đúng khi nói yêu cầu preflight là bắt buộc khi thực hiện yêu cầu nguồn gốc chéo. Yêu cầu chiếu trước chỉ được yêu cầu trong các tình huống cụ thể, như nếu bạn đang đặt tiêu đề tùy chỉnh hoặc thực hiện các yêu cầu khác ngoài nhận, đầu và đăng.
Robin Clowers

4
Thật thú vị, khi thực hiện một yêu cầu CORS bằng jQuery, thư viện JavaScript đặc biệt tránh đặt tiêu đề tùy chỉnh, cùng với một lời cảnh báo cho các nhà phát triển: Đối với các yêu cầu tên miền chéo, xem như là điều kiện cho một ánh sáng giống như trò chơi ghép hình, chúng tôi chỉ đơn giản là không bao giờ đặt nó để chắc chắn.
Bên dưới Radar

3
Làm thế nào nếu tôi làm một curlapi nó hoạt động, nhưng khi chạy từ chrome tôi gặp lỗi?
SuperUberDuper

5
@SuperUberDuper vì CORS và các yêu cầu preflight là một vấn đề liên quan đến trình duyệt. Bạn có thể mô phỏng CORS bằng cách thêm một Origintiêu đề vào yêu cầu của bạn để mô phỏng như thể yêu cầu đến từ một máy chủ cụ thể (ví dụ: yourwebsite.com). Bạn cũng có thể mô phỏng các yêu cầu chiếu trước bằng cách đặt phương thức HTTP của yêu cầu OPTIONSAccess-Control-*Tiêu đề
Leo Correa

234

Đã đi qua vấn đề này, dưới đây là kết luận của tôi về vấn đề này và giải pháp của tôi.

Theo chiến lược CORS (rất khuyến khích bạn đọc về nó) Bạn không thể buộc trình duyệt ngừng gửi yêu cầu TÙY CHỌN nếu nó nghĩ rằng nó cần.

Có hai cách bạn có thể làm việc xung quanh nó:

  1. Hãy chắc chắn rằng yêu cầu của bạn là một "yêu cầu đơn giản"
  2. Đặt Access-Control-Max-Agecho yêu cầu TÙY CHỌN

Yêu cầu đơn giản

Một yêu cầu chéo trang đơn giản là một yêu cầu đáp ứng tất cả các điều kiện sau:

Các phương thức được phép duy nhất là:

  • ĐƯỢC
  • CÁI ĐẦU
  • BÀI ĐĂNG

Ngoài các tiêu đề được đặt tự động bởi tác nhân người dùng (ví dụ: Kết nối, Tác nhân người dùng, v.v.), các tiêu đề duy nhất được phép đặt thủ công là:

  • Chấp nhận
  • Ngôn ngữ chấp nhận
  • Nội dung ngôn ngữ
  • Loại nội dung

Các giá trị được phép duy nhất cho tiêu đề Kiểu nội dung là:

  • application / x-www-form-urlencoding
  • nhiều dữ liệu / biểu mẫu
  • văn bản / đồng bằng

Một yêu cầu đơn giản sẽ không gây ra yêu cầu TÙY CHỌN trước chuyến bay.

Đặt bộ đệm cho kiểm tra TÙY CHỌN

Bạn có thể đặt Access-Control-Max-Ageyêu cầu TÙY CHỌN để nó sẽ không kiểm tra lại quyền cho đến khi hết hạn.

Access-Control-Max-Age cung cấp giá trị tính bằng giây trong thời gian phản hồi cho yêu cầu preflight có thể được lưu trong bộ nhớ cache mà không gửi yêu cầu preflight khác.

Giới hạn lưu ý

  • Đối với Chrome, giây tối đa cho Access-Control-Max-Age600đó là 10 phút, theo chrome mã nguồn
  • Access-Control-Max-Agechỉ hoạt động cho một tài nguyên mỗi lần, ví dụ: GETcác yêu cầu có cùng đường dẫn URL nhưng các truy vấn khác nhau sẽ được coi là các tài nguyên khác nhau. Vì vậy, yêu cầu đối với tài nguyên thứ hai vẫn sẽ kích hoạt yêu cầu preflight.

3
Có ... đây phải là câu trả lời được chấp nhận và phù hợp nhất với câu hỏi ..!
Rajesh Mbm

7
Cảm ơn bạn đã đề cập Access-Control-Max-Age. Đó là chìa khóa ở đây. Nó giúp bạn tránh các yêu cầu preflight quá mức.
Idris Mokhtarzada

Tôi đang sử dụng axios để gọi yêu cầu nhận. Tôi có thể đặt Access-Control-Max-Age trong yêu cầu axios ở đâu?
Mohit Shah

+1 Tiêu đề Access-Control-Max-Age là chìa khóa ở đây. Đây phải là câu trả lời được chấp nhận! Tôi thiết lập 86400 giây (24 giờ) trên tiêu đề và yêu cầu prefligth đã biến mất!
revazedz

1
@VitalyZdanevich không! Đừng tránh application/jsonchỉ vì nó làm cho yêu cầu của bạn không "đơn giản" (và do đó kích hoạt CORS). Trình duyệt đang làm công việc của nó. Đặt máy chủ của bạn để trả lại một cái gì đó như một tiêu đề Access-Control-Max-Age: 86400và trình duyệt sẽ không gửi lại yêu cầu TÙY CHỌN trong 24 giờ.
colm.anseo

139

Vui lòng tham khảo câu trả lời này về nhu cầu thực tế đối với yêu cầu TÙY CHỌN trước chuyến bay: CORS - Động lực đằng sau việc giới thiệu các yêu cầu preflight là gì?

Để tắt yêu cầu TÙY CHỌN, các điều kiện dưới đây phải được thỏa mãn cho yêu cầu ajax:

  1. Yêu cầu không đặt tiêu đề HTTP tùy chỉnh như 'application / xml' hoặc 'application / json', v.v.
  2. Phương thức yêu cầu phải là một trong các GET, HEAD hoặc POST. Nếu POST, kiểu nội dung nên là một trong application/x-www-form-urlencoded, multipart/form-datahoặctext/plain

Tham khảo: https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS


14
+1 cho "tiêu đề HTTP tùy chỉnh"! Trong trường hợp của tôi, họ đã khiến yêu cầu trước chuyến bay được kích hoạt. Tôi đã cấu trúc lại yêu cầu gửi bất cứ thứ gì tôi đang gửi trong các tiêu đề vì phần yêu cầu và các yêu cầu TÙY CHỌN đã ngừng được gửi.
Andre

21
application/xmlhoặc application/jsonkhông phải là "tiêu đề HTTP tùy chỉnh". Bản thân tiêu đề sẽ là Content-Typevà gọi tiêu đề đó là "tùy chỉnh" sẽ gây hiểu nhầm.
Leo Correa

1
Đã xóa Tiêu đề HTTP tùy chỉnh và điều này hoạt động như một cơ duyên!
Tim D

47

Khi bạn mở bảng điều khiển gỡ lỗi và Disable Cachetùy chọn được bật, các yêu cầu preflight sẽ luôn được gửi (tức là trước mỗi yêu cầu). nếu bạn không tắt bộ đệm, yêu cầu trước chuyến bay sẽ chỉ được gửi một lần (trên mỗi máy chủ)


3
oh tôi đang nghĩ gì gỡ lỗi trong nhiều giờ đây là giải pháp của tôi. bộ nhớ cache bị vô hiệu hóa do bàn điều khiển gỡ lỗi.
mauris

1
Ngay cả khi bảng điều khiển gỡ lỗi bị đóng, các yêu cầu preflight vẫn được gửi
Luca Perico

2
Luca: điều đó đúng nhưng vấn đề là "vô hiệu hóa bộ đệm" không có tác dụng khi các công cụ dev được đóng lại. yêu cầu preflight chỉ được gửi một lần (tất nhiên trên mỗi máy chủ) nếu bộ đệm không bị tắt và được gửi trước mỗi yêu cầu nếu bộ đệm bị tắt.
Nir

Điều đó thực sự hữu ích.
Anuraag Patil

38

Có, có thể tránh yêu cầu tùy chọn. Yêu cầu tùy chọn là yêu cầu chiếu trước khi bạn gửi (đăng) bất kỳ dữ liệu nào sang tên miền khác. Đây là một vấn đề bảo mật trình duyệt. Nhưng chúng ta có thể sử dụng một công nghệ khác: lớp vận chuyển iframe. Tôi thực sự khuyên bạn nên quên mọi cấu hình CORS và sử dụng giải pháp readymade và nó sẽ hoạt động ở bất cứ đâu.

Hãy xem tại đây: https://github.com/jpillora/xdomain

Và ví dụ hoạt động: http://jpillora.com/xdomain/


Đây thực sự là một loại proxy thả?
matanster

15
"Yêu cầu tùy chọn là yêu cầu chiếu trước khi bạn gửi (đăng) bất kỳ dữ liệu nào sang tên miền khác." - Đo không phải sự thật. Bạn có thể sử dụng XHR để gửi bất kỳ yêu cầu POST nào bạn có thể gửi với biểu mẫu HTML thông thường mà không kích hoạt yêu cầu preflight. Chỉ khi bạn bắt đầu thực hiện những việc mà một biểu mẫu không thể thực hiện (như loại nội dung tùy chỉnh hoặc tiêu đề yêu cầu bổ sung) thì đèn chiếu được gửi.
Quentin

Giải pháp ở đây dường như dựa vào một shim iframe hoạt động trong một số trường hợp, nhưng có một số hạn chế lớn. Điều gì xảy ra nếu bạn muốn biết mã trạng thái HTTP của phản hồi hoặc giá trị của tiêu đề phản hồi HTTP khác?
Stephen Crosby

5
iFrames đã không được thực hiện cho điều đó.
Romko

1
đây là một triển khai phần mềm và trả lời câu hỏi cuối cùng là: "Có cách nào ngăn chặn hoàn toàn trình duyệt gửi yêu cầu TÙY CHỌN không?". Tóm lại, không có cách nào để vô hiệu hóa nó trong Mozilla hoặc Chromium, nó được triển khai trong mã và các tùy chọn "làm việc" duy nhất chỉ tránh được hành vi.
người nhặt rác

15

Đối với nhà phát triển hiểu lý do tồn tại nhưng cần truy cập API không xử lý các cuộc gọi TÙY CHỌN mà không cần xác thực, tôi cần câu trả lời tạm thời để tôi có thể phát triển cục bộ cho đến khi chủ sở hữu API thêm hỗ trợ SPA CORS phù hợp hoặc tôi nhận được API proxy lên và chạy

Tôi thấy bạn có thể tắt CORS trong Safari và Chrome trên máy Mac.

Vô hiệu hóa chính sách nguồn gốc tương tự trong Chrome

Chrome: Thoát Chrome, mở một thiết bị đầu cuối và dán lệnh này: open /Applications/Google\ Chrome.app --args --disable-web-security --user-data-dir

Safari: Vô hiệu hóa chính sách cùng nguồn gốc trong Safari

Nếu bạn muốn tắt chính sách cùng nguồn gốc trên Safari (Tôi có 9.1.1), thì bạn chỉ cần bật menu nhà phát triển và chọn "Tắt hạn chế nguồn gốc chéo" từ menu phát triển.


4
Bạn nên nhấn mạnh hơn vào phần có nội dung "đây KHÔNG BAO GIỜ nên là một giải pháp cho phép !!!!" . Chính sách nguồn gốc tương tự là một biện pháp bảo mật trình duyệt rất quan trọng và không bao giờ bị vô hiệu hóa khi bình thường duyệt internet.
jannis

Tôi ước web sẽ hoạt động theo cách này, vì vậy chúng tôi chỉ có thể yêu cầu dữ liệu chúng tôi cần từ các máy chủ mà không gặp rắc rối thêm.
jemiloii

14

Như đã đề cập trong các bài viết trước, OPTIONSyêu cầu là có lý do. Nếu bạn gặp sự cố với thời gian phản hồi lớn từ máy chủ của mình (ví dụ: kết nối ở nước ngoài), bạn cũng có thể yêu cầu trình duyệt của mình lưu trữ các yêu cầu preflight.

Yêu cầu máy chủ của bạn trả lời với Access-Control-Max-Agetiêu đề và đối với các yêu cầu đi đến cùng điểm cuối, yêu cầu preflight sẽ được lưu trữ và không xảy ra nữa.


1
Cảm ơn vì điều này! Thực tế là OPTIONScác yêu cầu sẽ được lưu trong bộ đệm với tiêu đề này khá mờ trong tất cả các tài liệu CORS mà tôi đã đọc.
joshperry

Và bộ đệm chỉ có hiệu lực với cùng một url. Tôi muốn một bộ đệm preflight cấp tên miền thực sự có thể làm giảm các chuyến đi khứ hồi. (CORS thật ngớ ngẩn!)
tự hỏi

8

Tôi đã giải quyết vấn đề này như thế nào.

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();
}

Nó chỉ dành cho sự phát triển. Với điều này, tôi đang chờ 9ms và 500ms chứ không phải 8 giây và 500ms. Tôi có thể làm điều đó bởi vì ứng dụng JS sản xuất sẽ nằm trên cùng một máy với sản xuất nên sẽ không có OPTIONSnhưng sự phát triển là cục bộ của tôi.


4

Bạn không thể nhưng bạn có thể tránh CORS bằng JSONP.


2
Bạn chỉ nhận được một yêu cầu TÙY CHỌN nếu bạn đang làm một việc không đơn giản . Bạn chỉ có thể thực hiện các yêu cầu đơn giản (GET, không có tiêu đề tùy chỉnh, không có dữ liệu xác thực) với JSONP, vì vậy JSONP không thể thay thế ở đây.
Quentin

Vâng, tôi biết điều đó nhưng tôi không biết chính xác các yêu cầu của dự án. Tôi biết nó không đơn giản tránh cors nhưng nó phụ thuộc vào dự án. Trong trường hợp xấu nhất để tránh CORS, bạn cần truyền dữ liệu bằng cách sử dụng tham số get. Vì vậy, JSONP có thể thay thế cors tùy theo yêu cầu của dự án (như bạn đã nói, sử dụng các yêu cầu đơn giản)
Jose Mato

Tôi không hiểu thiết kế của cái gọi là trước chuyến bay. Điều gì có thể khiến nó không an toàn, nếu khách hàng quyết định gửi dữ liệu đến máy chủ? Tôi không thấy nó có ý nghĩa gì để tăng gấp đôi tải trên dây.
Qian Chen

@ElgsQianChen điều này có thể có thể trả lời câu hỏi stackoverflow.com/questions/15381105/
Leo Correa

0

Sau khi dành cả một ngày rưỡi để cố gắng giải quyết vấn đề tương tự, tôi thấy nó phải làm với IIS .

Dự án API Web của tôi đã được thiết lập như sau:

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

Tôi không có tùy chọn cấu hình cụ thể CORS trong nút web.config> system.webServer như tôi đã thấy trong rất nhiều bài đăng

Không có mã cụ thể CORS trong global.asax hoặc trong bộ điều khiển làm trang trí

Vấn đề là cài đặt nhóm ứng dụng .

Các chế độ quản lý đường ống được thiết lập để cổ điển ( thay đổi nó để tích hợp ) và nhận dạng được thiết lập để dịch vụ mạng ( thay đổi nó để ApplicationPoolIdentity )

Thay đổi các cài đặt đó (và làm mới nhóm ứng dụng) đã sửa nó cho tôi.


-2

Điều làm việc cho tôi là nhập "github.com/gorilla/handlers" và sau đó sử dụng theo cách này:

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)))

Ngay sau khi tôi thực hiện một yêu cầu POST của Ajax và đính kèm dữ liệu JSON vào nó, Chrome sẽ luôn thêm tiêu đề Kiểu nội dung không có trong cấu hình cho phép trước đây của tôi.


-2

Một giải pháp tôi đã sử dụng trong quá khứ - giả sử trang web của bạn nằm trên mydomain.com và bạn cần thực hiện một yêu cầu ajax để foreigndomain.com

Định cấu hình viết lại IIS từ miền của bạn sang miền nước ngoài - ví dụ:

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

trên trang web mydomain.com của bạn - sau đó bạn có thể thực hiện cùng một yêu cầu nguồn gốc và không cần bất kỳ yêu cầu tùy chọn nào :)


-2

Nó có thể được giải quyết trong trường hợp sử dụng proxy chặn yêu cầu và viết các tiêu đề thích hợp. Trong trường hợp cụ thể của Varnish, đây sẽ là các quy tắc:

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

Có thể có một giải pháp (nhưng tôi đã không kiểm tra nó): bạn có thể sử dụng CSP (Chính sách bảo mật nội dung) để cho phép miền từ xa của bạn và các trình duyệt có thể bỏ qua xác minh yêu cầu CORS OPTION.

Tôi nếu tìm thấy một thời gian, tôi sẽ kiểm tra điều đó và cập nhật bài viết này!

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

Đặc điểm kỹ thuật CSP: https://www.w3.org/TR/CSP/


Tôi mới thử nghiệm và nó không hoạt động, CORS vẫn được yêu cầu sau khi CSP cho nhập học yêu cầu xhr ...
Arnaud Tournier
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.