Чому надсилається запит OPTIONS і чи можу я його відключити?


415

Я будую веб-API. Я виявляв, коли я використовую Chrome для POST, ЗАРАЗ на свій API, завжди є запит 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

OPTIONSЗапит CORS запускається лише в деяких випадках, як пояснено в документах MDN :

Деякі запити не запускають передполіт CORS. У цій статті вони називаються "простими запитами", хоча специфікація "Вибір" (яка визначає CORS) не використовує цей термін. Запит, який не викликає передпольотний запуск CORS (так званий "простий запит"), який відповідає всім наступним умовам:

Єдині дозволені методи:

  • ЗАРАЗ
  • ГОЛОВА
  • POST

Крім заголовків, встановлених автоматично агентом користувача (наприклад, Connection, User-Agent або будь-який з інших заголовків з іменами, визначеними в специфіці Fetch, як "заборонене ім'я заголовка"), єдиними заголовками можуть бути встановлені вручну - це ті, які специфікація "Вилучення" визначає як "заголовки запиту, що містяться в CORS", які:

  • Прийміть
  • Прийняти мову
  • Мова вмісту
  • Тип вмісту (але врахуйте додаткові вимоги нижче)
  • ДНР
  • Низхідній лінії зв'язку
  • Збереження даних
  • Ширина огляду
  • Ширина

Єдині дозволені значення для заголовка типу вмісту:

  • додаток / x-www-form-urlencoded
  • багаточастинні / форми-дані
  • текст / простий

На жодному об'єкті XMLHttpRequestUpload, який використовується у запиті, не зареєстровано жодних слухачів подій; Доступ до них здійснюється за допомогою властивості XMLHttpRequest.upload.

У запиті не використовується жоден об'єкт ReadableStream.


8
Але встановити цей прапор Chrome нереально для всіх загальних користувачів.
Qian Chen

37
Неправильно сказати, що запити перед польотом потрібні під час запитів перехресного походження Запити попереднього перегляду потрібні лише в конкретних ситуаціях, наприклад, якщо ви встановлюєте власні заголовки або робите запити, окрім отримання, заголовка та публікації.
Робін Клоуерс

4
Як не дивно, коли роблячи запит CORS за допомогою jQuery, бібліотека JavaScript спеціально уникає встановлення користувацького заголовка, а також слово попередження для розробників: Для запитів між доменами, оскільки умови для передпольоту схожі на головоломку, просто ніколи не встановлюйте це, щоб бути впевненим.
Нижче

3
Як прийти, якщо я curlпрацюю на api, він працює, але при запуску з хрому я отримую помилку?
SuperUberDuper

5
@SuperUberDuper, оскільки CORS та запити перед польотом - це питання, пов’язане з браузером. Ви можете імітувати CORS, додавши Originзаголовок до свого запиту, щоб імітувати так, ніби запит надходить від конкретного хоста (наприклад, yourwebsite.com). Ви також можете імітувати запити перед польотом, встановивши метод HTTP запиту на OPTIONSта Access-Control-*заголовки
Leo Correa

234

Я вирішив це питання, нижче - мій висновок до цього питання та моє рішення.

Відповідно до стратегії CORS (настійно рекомендую прочитати про неї) Ви не можете просто змусити браузер перестати надсилати запит OPTIONS, якщо він вважає, що це потрібно.

Є два способи обійти це:

  1. Переконайтеся, що ваш запит є "простим запитом"
  2. Встановіть Access-Control-Max-Ageдля запиту OPTIONS

Простий запит

Простий запит між сайтом - це той, який відповідає всім наступним умовам:

Єдині дозволені методи:

  • ЗАРАЗ
  • ГОЛОВА
  • POST

Крім заголовків, встановлених автоматично користувачем (наприклад, з'єднання, користувальницький агент тощо), єдиними заголовками, які можна встановити вручну, є:

  • Прийміть
  • Прийняти мову
  • Мова вмісту
  • Тип вмісту

Єдині дозволені значення для заголовка типу вмісту:

  • додаток / x-www-form-urlencoded
  • багаточастинні / форми-дані
  • текст / простий

Простий запит не спричинить запит OPTIONS перед польотом.

Встановіть кеш для перевірки ВАРІАНТІВ

Ви можете встановити Access-Control-Max-Ageдля запиту OPTIONS, щоб він не перевіряв дозвіл ще раз, поки він не закінчиться.

Access-Control-Max-Age дає значення в секундах, скільки часу може бути кешована відповідь на запит перед політ, не надсилаючи інший запит перед політ.

Обмеження помічено

  • Для Chrome максимальна секунда Access-Control-Max-Age- 600це 10 хвилин відповідно до хромованого вихідного коду
  • Access-Control-Max-Ageпрацює лише для одного ресурсу кожного разу, наприклад, GETзапити з однаковим URL-адресом, але різні запити будуть розглядатися як різні ресурси. Таким чином, запит на другий ресурс все ще викликатиме попередній запит.

3
Так ... це повинна бути прийнята відповідь і найбільш відповідна на питання "!
Rajesh Mbm

7
Дякую за згадування Access-Control-Max-Age. Ось ключ тут. Це допомагає вам уникнути надмірних запитів перед польотом.
Ідріс Мохтарзада

Я використовую axios для дзвінка отримати запит. Де я можу встановити Access-Control-Max-Age у запиті axios?
Мохіт Шах

+1 Ключовим тут є заголовок Access-Control-Max-Age. Це має бути прийнята відповідь! Я встановлюю 86400 секунд (24 години) на заголовок, і запит на попереднє відступлення відсутній!
revobtz

1
@VitalyZdanevich ні! Не уникайте application/jsonлише тому, що це робить ваш запит непростим (і тим самим спрацьовує CORS). Браузер робить свою роботу. Налаштуйте ваш сервер повертати щось на зразок заголовка, Access-Control-Max-Age: 86400і браузер не надсилатиме запит OPTIONS протягом 24 годин.
colm.anseo

139

Будь ласка, зверніться до цієї відповіді про фактичну потребу в попередньо розібраному запиті ВАРІАНТІВ : CORS - Яка мотивація для введення запитів на передпольотний рейс?

Щоб вимкнути запит OPTIONS, для запиту ajax повинні бути виконані нижче умови:

  1. Запит не встановлює власні заголовки HTTP, такі як "application / xml" або "application / json" тощо
  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
+1 для "користувацьких заголовків HTTP"! У моєму випадку вони викликали спрацьовування запиту перед польотом. Я відновив запит надіслати те, що я надсилав у заголовки, оскільки орган запиту та OPTIONS запити перестали надсилатися.
Андре

21
application/xmlабо application/jsonне є "Спеціальними заголовками HTTP". Сам заголовок був би, Content-Typeа названня цього заголовка "користувацьким" було б оманом.
Лев Кореа

1
Видалено спеціальні заголовки HTTP, і це спрацювало як шарм!
Тім Д

47

Якщо у вас відкрита консоль налагодження та Disable Cacheувімкнена опція, запити перед польотом завжди будуть надсилатися (тобто перед кожним запитом). якщо ви не відключите кеш, запит перед польотом буде надісланий лише один раз (на кожен сервер)


3
о, що я думаю. маючи налагодження годинами, це було моїм рішенням. кеш вимкнено через конфігурацію налагодження.
mauris

1
Навіть якщо консоль налагодження закрита, надсилаються запити до передпольоту
Luca Perico

2
Лука: Це правда, але справа в тому, що "вимкнути кеш" не має ефекту, коли інструменти розробки закриті. Запити перед перельотом надсилаються лише один раз (звичайно на сервер), якщо кеш не вимкнено і надсилається перед кожним запитом, якщо кеш вимкнено.
Нір

Це було дуже корисно.
Анурааг Патіл

38

Так, можна уникнути запиту опцій. Запит опцій - це запит перед польотом, коли ви надсилаєте (публікуєте) будь-які дані на інший домен. Це проблема безпеки браузера. Але ми можемо використовувати іншу технологію: транспортний шар iframe. Настійно рекомендую забути про будь-яку конфігурацію CORS і використовувати готове рішення, і воно працюватиме де завгодно.

Погляньте тут: https://github.com/jpillora/xdomain

І робочий приклад: http://jpillora.com/xdomain/


це насправді своєрідний проксі-сервер, що випадає?
matanster

15
"Запит на опції - це запит перед польотом, коли ви надсилаєте (публікуєте) будь-які дані на інший домен." - Це не правда. Ви можете використовувати XHR для надсилання будь-якого запиту POST, який ви могли б надіслати за допомогою звичайної форми HTML, не викликаючи попередній запит. Лише коли ви починаєте робити такі речі, які форма не може робити (наприклад, спеціальні типи вмісту або додаткові заголовки запиту), надсилається передпольотний посилання.
Квентін

Здається, рішення тут покладається на рамку iframe, яка працює в деяких випадках, але має деякі основні обмеження. Що станеться, якщо ви хочете дізнатися код статусу HTTP відповіді або значення іншого заголовка відповіді HTTP?
Стівен Кросбі

5
iFrames для цього не робилися.
Ромко

1
це реалізація програмного забезпечення та дає відповідь на останнє запитання, яке було: "Чи є спосіб повністю зупинити браузер від надсилання запитів OPTIONS?". Коротше кажучи, немає можливості відключити його в Mozilla або Chromium, він реалізований в коді, і єдині "робочі" варіанти просто обходять поведінку.
прибиральник

15

Для розробника, який розуміє причину його існування, але йому потрібно отримати доступ до API, який не обробляє виклики OPTIONS без auth, мені потрібна тимчасова відповідь, щоб я міг розвиватися локально, поки власник API не додасть належну підтримку SPA CORS або не отримаю проксі API вгору і працює.

Я виявив, що ви можете відключити CORS в Safari та Chrome на Mac.

Вимкніть політику щодо оригіналу в Chrome

Chrome: Закрийте Chrome, відкрийте термінал і вставте цю команду: open /Applications/Google\ Chrome.app --args --disable-web-security --user-data-dir

Safari: відключення політики однакового походження в Safari

Якщо ви хочете вимкнути політику того самого походження на Safari (у мене 9.1.1), тоді вам потрібно лише включити меню розробника та вибрати "Вимкнути обмеження перехресного походження" у меню розробки.


4
Ви повинні поставити більше уваги на частині, яка говорить "це НІКОЛИ не повинно бути постійним рішенням !!!!" . Одна і та ж політика щодо походження є дуже важливим заходом безпеки веб-переглядача і ніколи не повинна бути відключена під час звичайного перегляду Інтернету.
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();
}

Це лише для розвитку. З цим я чекаю 9мс і 500мс, а не 8с і 500мс. Я можу це зробити, тому що додаток JS для виробництва буде працювати на тій же машині, що і виробництво, тому не буде нічого, OPTIONSале розвиток мій локальний.


4

Ви не можете, але ви могли уникнути CORS, використовуючи JSONP.


2
Ви отримуєте запит ВАРІАНТІВ лише тоді, коли робите щось не просте . З JSONP ви можете робити лише прості запити (GET, немає спеціальних заголовків, відсутні дані автентифікації), тому JSONP не може замінити тут.
Квентін

Так, я це знаю, але не знаю точно вимог проекту. Я знаю, що це не просто уникнення корсів, але це залежить від проекту. У гіршому випадку, щоб уникнути CORS, вам потрібно передавати дані, використовуючи параметри get. Отже, JSONP може замінити корзи залежно від вимог проекту (як ви сказали, використовуючи прості запити)
Хосе Мато

Я не розумію конструкцію так званого передпольоту. Що може спричинити його небезпеку, якщо клієнт вирішив надіслати дані на сервер? Я не бачу сенсу подвоювати навантаження на дріт.
Цянь Чен

@ElgsQianChen це, ймовірно, може відповісти на ваше запитання stackoverflow.com/questions/15381105/…
Лео Коррея

0

Провівши цілий півтора дня, намагаючись вирішити подібну проблему, я виявив, що це стосується IIS .

Мій проект Web API був налаштований так:

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

У мене не було специфічних параметрів налаштування CORS у вузлі web.config> system.webServer, як я бачив у такій кількості публікацій

Немає специфічного коду CORS у global.asax або в контролері як декоратор

Проблемою були налаштування пулу додатків .

Режим керованого конвеєра був встановлений на класичний ( змінив його на інтегрований ), а Identity - на мережевий сервіс ( змінив його на 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.


-2

Я використовував одне рішення раніше: скажімо, ваш сайт на mydomain.com, і вам потрібно зробити запит на ajax на 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 (Політика безпеки вмісту), щоб увімкнути віддалений домен, а браузери, можливо, пропустять перевірку запиту CORS OPTIONS.

Якщо я знайду якийсь час, тестую це і оновлю цей пост!

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

Специфікація CSP: https://www.w3.org/TR/CSP/


Я щойно тестував, і це не працює, CORS все ще потрібен після CSP для прийому запиту xhr ...
Арно Турньє,
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.