Разрешить кроссдоменные запросы в браузере

Обновлено: 05.07.2024

Если мы сделаем запрос fetch на другой веб-сайт, он, вероятно, завершится неудачей.

Вызов fetch не удался, как и ожидалось.

Ключевым понятием здесь является источник (origin) – комбинация домен/порт/протокол.

Запросы на другой источник – отправленные на другой домен (или даже поддомен), или протокол, или порт – требуют специальных заголовков от удалённой стороны.

Эта политика называется «CORS»: Cross-Origin Resource Sharing («совместное использование ресурсов между разными источниками»).

Зачем нужен CORS? Экскурс в историю

CORS существует для защиты интернета от злых хакеров.

Серьёзно. Давайте сделаем краткое историческое отступление.

Многие годы скрипт с одного сайта не мог получить доступ к содержимому другого сайта.

В то время в JavaScript не было методов для сетевых запросов. Это был «игрушечный» язык для украшения веб-страниц.

Но веб-разработчики жаждали большей власти. Чтобы обойти этот запрет и всё же получать данные с других сайтов, были придуманы разные хитрости.

Использование форм

Одним из способов общения с другим сервером была отправка туда формы <form> . Люди отправляли её в <iframe> , чтобы оставаться на текущей странице, вот так:

Таким способом было возможно сделать GET/POST запрос к другому сайту даже без сетевых методов, так как формы можно отправлять куда угодно. Но так как запрещено получать доступ к содержимому <iframe> с другого сайта, прочитать ответ было невозможно.

Если быть точным, были трюки и для этого, требующие специального кода на странице и в ифрейме, так что общение с ифреймом было технически возможно. Сейчас мы не будем вдаваться в подробности, пусть эти динозавры покоятся в мире.

Использование скриптов

Вот как он работал.

Сначала, заранее, объявляем глобальную функцию для обработки данных, например gotWeather .

Когда этот скрипт загрузится и выполнится, наша функция gotWeather получает данные.

Это работает и не нарушает безопасность, потому что обе стороны согласились передавать данные таким образом. А когда обе стороны согласны, то это определённо не хак. Всё ещё существуют сервисы, которые предоставляют такой доступ, так как это работает даже для очень старых браузеров.

Спустя некоторое время в браузерном JavaScript появились методы для сетевых запросов.

Вначале запросы на другой источник были запрещены. Но в результате долгих дискуссий было решено разрешить их делать, но для использования новых возможностей требовать разрешение сервера, выраженное в специальных заголовках.

Простые запросы

Есть два вида запросов на другой источник:

Простые запросы будут попроще, поэтому давайте начнём с них.

Простой запрос – это запрос, удовлетворяющий следующим условиям:

    : GET, POST или HEAD – разрешены только:
    • Accept ,
    • Accept-Language ,
    • Content-Language ,
    • Content-Type со значением application/x-www-form-urlencoded , multipart/form-data или text/plain .

Принципиальное отличие между ними состоит в том, что «простой запрос» может быть сделан через <form> или <script> , без каких-то специальных методов.

Таким образом, даже очень старый сервер должен быть способен принять простой запрос.

В противоположность этому, запросы с нестандартными заголовками или, например, методом DELETE нельзя создать таким способом. Долгое время JavaScript не мог делать такие запросы. Поэтому старый сервер может предположить, что такие запросы поступают от привилегированного источника, «просто потому, что веб-страница неспособна их посылать».

Когда мы пытаемся сделать непростой запрос, браузер посылает специальный предварительный запрос («предзапрос», по англ. «preflight»), который спрашивает у сервера – согласен ли он принять такой непростой запрос или нет?

И, если сервер явно не даёт согласие в заголовках, непростой запрос не посылается.

Далее мы разберём конкретные детали.

CORS для простых запросов

При запросе на другой источник браузер всегда ставит «от себя» заголовок Origin .

Как вы можете видеть, заголовок Origin содержит именно источник (домен/протокол/порт), без пути.

Здесь браузер играет роль доверенного посредника:

  1. Он гарантирует, что к запросу на другой источник добавляется правильный заголовок Origin .
  2. Он проверяет наличие разрешающего заголовка Access-Control-Allow-Origin в ответе и, если всё хорошо, то JavaScript получает доступ к ответу сервера, в противном случае – доступ запрещается с ошибкой.

Вот пример ответа сервера, который разрешает доступ:

Заголовки ответа

По умолчанию при запросе к другому источнику JavaScript может получить доступ только к так называемым «простым» заголовкам ответа:

  • Cache-Control
  • Content-Language
  • Content-Type
  • Expires
  • Last-Modified
  • Pragma

При доступе к любому другому заголовку ответа будет ошибка.

Пожалуйста, обратите внимание: в списке нет заголовка Content-Length !

Этот заголовок содержит полную длину ответа. Поэтому если мы загружаем что-то и хотели бы отслеживать прогресс в процентах, то требуется дополнительное разрешение для доступа к этому заголовку (читайте ниже).

Чтобы разрешить JavaScript доступ к любому другому заголовку ответа, сервер должен указать заголовок Access-Control-Expose-Headers . Он содержит список, через запятую, заголовков, которые не являются простыми, но доступ к которым разрешён.

При таком заголовке Access-Control-Expose-Headers , скрипту разрешено получить заголовки Content-Length и API-Key ответа.

«Непростые» запросы

Некоторое время назад никто не мог даже предположить, что веб-страница способна делать такие запросы. Так что могут существовать веб-сервисы, которые рассматривают нестандартный метод как сигнал: «Это не браузер». Они могут учитывать это при проверке прав доступа.

Поэтому, чтобы избежать недопониманий, браузер не делает «непростые» запросы (которые нельзя было сделать в прошлом) сразу. Перед этим он посылает предварительный запрос, спрашивая разрешения.

Предварительный запрос использует метод OPTIONS , у него нет тела, но есть два заголовка:

Если сервер согласен принимать такие запросы, то он должен ответить без тела, со статусом 200 и с заголовками:

  • Access-Control-Allow-Methods должен содержать разрешённые методы.
  • Access-Control-Allow-Headers должен содержать список разрешённых заголовков.
  • Кроме того, заголовок Access-Control-Max-Age может указывать количество секунд, на которое нужно кешировать разрешения. Так что браузеру не придётся посылать предзапрос для последующих запросов, удовлетворяющих данным разрешениям.

Давайте пошагово посмотрим, как это работает, на примере PATCH запроса (этот метод часто используется для обновления данных) на другой источник:

Этот запрос не является простым по трём причинам (достаточно одной):

  • Метод PATCH
  • Content-Type не один из: application/x-www-form-urlencoded , multipart/form-data , text/plain .
  • Содержит «непростой» заголовок API-Key .

Шаг 1 (предзапрос)

Перед тем, как послать такой запрос, браузер самостоятельно генерирует и посылает предзапрос, который выглядит следующим образом:

  • Метод: OPTIONS .
  • Путь – точно такой же, как в основном запросе: /service.json .
  • Особые заголовки:
    • Origin – источник.
    • Access-Control-Request-Method – запрашиваемый метод.
    • Access-Control-Request-Headers – разделённый запятыми список «непростых» заголовков запроса.

    Шаг 2 (ответ сервера на предзапрос)

    Сервер должен ответить со статусом 200 и заголовками:

    • Access-Control-Allow-Methods: PATCH
    • Access-Control-Allow-Headers: Content-Type,API-Key .

    Это разрешит будущую коммуникацию, в противном случае возникает ошибка.

    Если сервер ожидает в будущем другие методы и заголовки, то он может в ответе перечислить их все сразу, разрешить заранее, например:

    Теперь, когда браузер видит, что PATCH есть в Access-Control-Allow-Methods , а Content-Type,API-Key – в списке Access-Control-Allow-Headers , он посылает наш основной запрос.

    Кроме того, ответ на предзапрос кешируется на время, указанное в заголовке Access-Control-Max-Age (86400 секунд, один день), так что последующие запросы не вызовут предзапрос. Они будут отосланы сразу при условии, что соответствуют закешированным разрешениям.

    Шаг 3 (основной запрос)

    Если предзапрос успешен, браузер делает основной запрос. Алгоритм здесь такой же, что и для простых запросов.

    Основной запрос имеет заголовок Origin (потому что он идёт на другой источник):

    Шаг 4 (основной ответ)

    Сервер не должен забывать о добавлении Access-Control-Allow-Origin к ответу на основной запрос. Успешный предзапрос не освобождает от этого:

    После этого JavaScript может прочитать ответ сервера.

    Предзапрос осуществляется «за кулисами», невидимо для JavaScript.

    JavaScript получает только ответ на основной запрос или ошибку, если со стороны сервера нет разрешения.

    Авторизационные данные

    Потому что запрос с авторизационными данными даёт намного больше возможностей, чем без них. Если он разрешён, то это позволяет JavaScript действовать от имени пользователя и получать информацию, используя его авторизационные данные.

    Действительно ли сервер настолько доверяет скрипту? Тогда он должен явно разрешить такие запросы при помощи дополнительного заголовка.

    Чтобы включить отправку авторизационных данных в fetch , нам нужно добавить опцию credentials: "include" , вот так:

    Если сервер согласен принять запрос с авторизационными данными, он должен добавить заголовок Access-Control-Allow-Credentials: true к ответу, в дополнение к Access-Control-Allow-Origin .

    Пожалуйста, обратите внимание: в Access-Control-Allow-Origin запрещено использовать звёздочку * для запросов с авторизационными данными. Там должен быть именно источник, как показано выше. Это дополнительная мера безопасности, чтобы гарантировать, что сервер действительно знает, кому он доверяет делать такие запросы.

    Итого

    С точки зрения браузера запросы к другому источнику бывают двух видов: «простые» и все остальные.

    Простые запросы должны удовлетворять следующим условиям:

    • Метод: GET, POST или HEAD.
    • Заголовки – мы можем установить только:
      • Accept
      • Accept-Language
      • Content-Language
      • Content-Type со значением application/x-www-form-urlencoded , multipart/form-data или text/plain .

      Основное их отличие заключается в том, что простые запросы с давних времён выполнялись с использованием тегов <form> или <script> , в то время как непростые долгое время были невозможны для браузеров.

      Практическая разница состоит в том, что простые запросы отправляются сразу с заголовком Origin , а для других браузер делает предварительный запрос, спрашивая разрешения.

      Для простых запросов:

      • → Браузер посылает заголовок Origin с источником.
      • ← Для запросов без авторизационных данных (не отправляются по умолчанию) сервер должен установить:
        • Access-Control-Allow-Origin в * или то же значение, что и Origin
        • Access-Control-Allow-Origin в то же значение, что и Origin
        • Access-Control-Allow-Credentials в true

        Дополнительно, чтобы разрешить JavaScript доступ к любым заголовкам ответа, кроме Cache-Control , Content-Language , Content-Type , Expires , Last-Modified или Pragma , сервер должен перечислить разрешённые в заголовке Access-Control-Expose-Headers .

        Для непростых запросов перед основным запросом отправляется предзапрос:

        • → Браузер посылает запрос OPTIONS на тот же адрес с заголовками:
          • Access-Control-Request-Method – содержит запрашиваемый метод,
          • Access-Control-Request-Headers – перечисляет непростые запрашиваемые заголовки.
          • Access-Control-Allow-Methods со списком разрешённых методов,
          • Access-Control-Allow-Headers со списком разрешённых заголовков,
          • Access-Control-Max-Age с количеством секунд для кеширования разрешений

          Задачи

          Почему нам нужен Origin?

          Как вы можете видеть, присутствуют и Referer , и Origin .

          1. Почему нужен Origin , если Referer содержит даже больше информации?
          2. Возможно ли отсутствие Referer или Origin , или это неправильно?

          Content Security Policy (политика безопасности содержимого) может запретить отправление Referer .

          Как мы увидим позже, у fetch есть опции, которые предотвращают отправку Referer и даже позволяют изменять его (в пределах того же сайта).

          Именно потому что Referer ненадёжен, был изобретён Origin . Браузер гарантирует наличие правильного Origin при запросах на другой источник.


          Доброго времени суток.
          Сегодня мы рассмотрим варианты организации кроссдоменных запросов в юзерскриптах.
          В подробности реализации того или иного механизма я вдаваться не буду, но приведу пример кроссбраузерной оболочки для кроссдоменных запросов.

          Вступление

          Перерыв:Если вы дочитали до этого места, и не поняли ни слова, то сделайте перерыв, погуглите неизвестные термины, выпейте чаю. Не унывайте, дальше будут объяснения «на пальцах» и с кодом. Начинающие скриптописатели могут вздохнуть спокойно: вдаваться в подробности мы не будем, но рабочий код и капля теории со всеми ссылками будет в вашем «загашнике».

          Основы

          В чем проблематичность кроссдоменных запросов? Все браузеры ограничивают нашу свободу в угоду безопасности. Посему, запросы из «недоверенных» источников пресекаются на корню. А что делать, если мы источникам доверяем? Правильно, использовать «костыли» (многие из которых являются общепринятой практикой).

          В рамках юзерскриптов следует учитывать ограничения, накладываемые окружением, в котором запускаются скрипты (подробности в этой статье) плюс дополнительное важное условие: наличие доступа к серверной части (наличие (или возможность создать) серверного API для реализации конкретного метода запроса)!

          Для подавляющего большинства скриптов критичным параметром является наличие доступа к серверу. К тому же, чаще всего используются GET-запросы (если скрипт не зловред, не наделён мегаинтеллектом или не является частью какого-либо сервиса).

          BeforeEvent

          Это событие актуально только для Opera. Вызывается, когда в документе появляется элемент скрипта перед его выполнением. Событие можно перехватить и остановить выполнение скрипта. Чем мы и воспользуемся.

          Важно: Для Opera нужно поставлять отдельный файл, в котором будет осуществляться навешивание события. Это связано с особеннностями выполнения пользовательских скриптов. Обратите внимание: в дополнительном скрипте не должно быть метаданных! Название скрипта начинается с нижнего подчеркивания, чтобы скрипт загружался первым.

          Запрос осуществляется вызовом следующей функции:

          Замечание: Для Opera существует ещё одно решение: тыц. Я его не ковырял, но вероятно оно лучше представленного здесь.

          JSONP

          Реализация JSONP остаётся в качестве домашнего задания :)
          Благо, работающих примеров в сети предостаточно.
          Но прежде всего нужно задаться вопросом: нужна ли вам вообще поддержка вашего юзерскрипта в IE?

          Разбираемся с Chrome

          Для осуществления запросов в Хроме вам необходимо упаковать юзерскрипт в расширение.
          Подробно данный метод описан в предыдущей статье. Там же дан пример проксирования кроссдоменного запроса (вызов функции запроса можно найти в оболочке, ниже по тексту).
          Если лень переходить по ссылке вы читали статью и знаете, как упаковать скрипт, то код для background.html доступен на pastebin.

          Оболочка

          Все перечисленные выше способы собраны в оболочку.
          Код не претендует на звание «лучший код 2011», но является рабочим и используется в различных модификациях в коммерческих юзерскриптах (да-да, бывают и такие).


          Кто должен читать данную статью?

          На самом деле, все.

          Какие запросы используют CORS?

          Обзор функциональности

          Примеры сценариев управления доступом

          Простые запросы

          Некоторые запросы не заставляют срабатывать CORS preflight. Они называются “простыми запросами” в данной статье, хотя Fetch спецификация, определяющая CORS, не использует этот термин. Запрос, для которого не срабатывает CORS preflight— так называемый “простой запросы”—это запрос, удовлетворяющий следующим условиям:

          • Допустимые методы для запроса:
          • application/x-www-form-urlencoded
          • multipart/form-data
          • text/plain
          Замечание: WebKit Nightly и Safari Technology Preview устанавливают дополнительные ограничения на значения, допустимые в заголовках Accept , Accept-Language , и Content-Language . Если любой из этих заголовков имеет "нестандартное" значение, WebKit/Safari используют предварительный запрос. Значения, которые WebKit/Safari считают "нестандартными" для этих заголовков, перечислены только в следующих проблемах WebKit: Require preflight for non-standard CORS-safelisted request headers Accept, Accept-Language, and Content-Language, Allow commas in Accept, Accept-Language, and Content-Language request headers for simple CORS, и Switch to a blacklist model for restricted Accept headers in simple CORS requests. Во всех других браузерах подобных дополнительных ограничений нет, потому что они не являются частью спецификации.

          Это приведёт к простому обмену запросами между клиентом и сервером, используя CORS заголовки для обработки привилегий:


          Посмотрим, что браузер отправит в таком случае на сервер, а также проверим ответ сервера:

          Предварительные запросы

          В частности, запрос предварительно просматривается, если выполняется любое из следующих условий:

          • Если в запросе используется любой из следующих методов:
          • application/x-www-form-urlencoded
          • multipart/form-data
          • text/plain

          Ниже приведён пример запроса, который будет предварительно просмотрен.


          Замечание: как описано ниже, фактический POST запрос не включает Access-Control-Request-* заголовки; они нужны только для OPTIONS запроса.

          Давайте посмотрим на полный обмен между клиентом и сервером. Первый обмен - это предварительный запрос/ответ:

          Как только предварительный запрос завершён, отправляется настоящий запрос:

          Заголовок Access-Control-Request-Method (en-US) уведомляет сервер как часть предварительного запроса о том, что при отправке фактического запроса он будет отправлен методом запроса POST . Заголовок Access-Control-Request-Headers (en-US) уведомляет сервер о том, что при отправке фактического запроса он будет отправлен с пользовательскими заголовками X-PINGOTHER и Content-Type. Теперь у сервера есть возможность определить, хочет ли он принять запрос в этих обстоятельствах.

          Строки 14 - 26 выше - это ответ, который сервер отправляет обратно, указывая, что метод запроса ( POST ) и заголовки запроса ( X-PINGOTHER ) являются приемлемыми. В частности, давайте посмотрим на строки 17-20:

          Сервер отвечает с Access-Control-Allow-Methods и сообщает, что POST , GET , и OPTIONS являются жизнеспособными методами для запроса соответствующего ресурса. Обратите внимание, что этот заголовок похож на заголовок ответа Allow (en-US), но используется строго в контексте контроля доступа.

          Сервер также отправляет Access-Control-Allow-Headers со значением " X-PINGOTHER, Content-Type ", подтверждая, что это разрешённые заголовки, которые будут использоваться с фактическим запросом. Как и Access-Control-Allow-Methods , Access-Control-Allow-Headers представляет собой список допустимых заголовков через запятую.

          Наконец, Access-Control-Max-Age даёт значение в секундах, в течение которого можно кешировать ответ на предварительный запрос без отправки другого предварительного запроса. В этом случае, 86400 секунды - это 24 часа. Обратите внимание, что каждый браузер имеет максимальное внутреннее значение, которое имеет приоритет, когда Access-Control-Max-Age больше.

          Предварительные запросы и переадресации

          Большинство браузеров в настоящее время не поддерживают следующие переадресации для предварительных запросов. Если переадресация происходит для предварительного запроса, большинство современных браузеров сообщат об ошибке, такой как следующее.

          Запрос требует предварительной проверки, которая запрещена для перенаправления между источниками

          Протокол CORS изначально требовал такого поведения, но впоследствии был изменён, чтобы больше не требовать его. Однако большинство браузеров ещё не реализовали это изменение и все ещё демонстрируют поведение, которое требовалось изначально.

          Поэтому, пока браузеры не догонят спецификацию, вы можете обойти это ограничение, выполнив одно или оба из следующих действий:

          • изменить поведение на стороне сервера, чтобы избежать предварительной проверки и/или избежать переадресации — если у вас есть контроль над сервером, к которому делается запрос
          • изменить запрос так, чтобы это был простой запрос, который не вызывает предварительную проверку

          Но если невозможно внести эти изменения, то возможен другой способ:

          Однако, если запрос инициирует предварительную проверку из-за наличия в запросе заголовка `Authorization`, вы не сможете обойти ограничение, используя описанные выше шаги. И вы вообще не сможете обойти это, если у вас нет контроля над сервером, на который делается запрос.

          Запросы с учётными данными


          Вот пример обмена между клиентом и сервером:

          Запросы с учётными данными и wildcards

          В процессе ответа на запрос с учётными данными сервер обязан указать точный источник в поле заголовка Access-Control-Allow-Origin вместо спецсимвола " * ".

          Из-за того что заголовки запроса в примере выше включают заголовок Cookie , запрос провалился бы, если бы значение заголовка Control-Allow-Origin было "*". Но он не провалился: потому что значение заголовка Access-Control-Allow-Origin - " http://foo.example " (действительный источник), а не спецсимвол " * ", контент, удостоверяющий полномочия, возвращается в вызывающий веб-контент.

          Отметьте, что заголовок ответа Set-Cookie в примере выше также устанавливает дополнительные куки. В случае неудачи, возникает исключение, в зависимости от используемого API.

          Access-Control-Allow-Origin

          Возвращаемый ресурс может иметь один заголовок Access-Control-Allow-Origin , синтаксис которого:

          Access-Control-Allow-Origin определяет либо один источник, что указывает браузеру разрешить этому источнику доступ к ресурсу; либо — для запросов без учётных данных — значение " * ", которое говорит браузеру разрешить запросы из любых источников.

          Если сервер возвращает название хоста, вместо "*", также может быть указан заголовок Vary со значением Origin, чтобы показать клиентам, что ответы с сервера будут отличаться в зависимости от значения заголовка запроса Origin.

          Access-Control-Expose-Headers

          The Access-Control-Expose-Headers (en-US) header lets a server whitelist headers that browsers are allowed to access. For example:

          This allows the X-My-Custom-Header and X-Another-Custom-Header headers to be exposed to the browser.

          Access-Control-Max-Age

          The Access-Control-Max-Age header indicates how long the results of a preflight request can be cached. For an example of a preflight request, see the above examples.

          The delta-seconds parameter indicates the number of seconds the results can be cached.

          Access-Control-Allow-Credentials

          The Access-Control-Allow-Credentials (en-US) header Indicates whether or not the response to the request can be exposed when the credentials flag is true. When used as part of a response to a preflight request, this indicates whether or not the actual request can be made using credentials. Note that simple GET requests are not preflighted, and so if a request is made for a resource with credentials, if this header is not returned with the resource, the response is ignored by the browser and not returned to web content.

          Access-Control-Allow-Methods

          The Access-Control-Allow-Methods header specifies the method or methods allowed when accessing the resource. This is used in response to a preflight request. The conditions under which a request is preflighted are discussed above.

          An example of a preflight request is given above, including an example which sends this header to the browser.

          Access-Control-Allow-Headers

          Origin

          The Origin header indicates the origin of the cross-site access request or preflight request.

          The origin is a URI indicating the server from which the request initiated. It does not include any path information, but only the server name.

          Note: The origin can be the empty string; this is useful, for example, if the source is a data URL.

          Note that in any access control request, the Origin header is always sent.

          Access-Control-Request-Method

          Examples of this usage can be found above.

          Access-Control-Request-Headers


          Who should read this article?

          More specifically, this article is for web administrators, server developers and front-end developers. Modern browsers handle the client side of cross-origin sharing, including headers and policy enforcement. But the CORS standard means servers have to handle new request and response headers.

          What requests use CORS?

          Functional overview

          CORS failures result in errors but for security reasons, specifics about the error are not available to JavaScript. All the code knows is that an error occurred. The only way to determine what specifically went wrong is to look at the browser's console for details.

          Examples of access control scenarios

          Simple requests

          Some requests don't trigger a CORS preflight. Those are called simple requests, though the Fetch spec (which defines CORS) doesn't use that term. A simple request is one that meets all the following conditions:

          • One of the allowed methods:
          • application/x-www-form-urlencoded
          • multipart/form-data
          • text/plain

          Note: WebKit Nightly and Safari Technology Preview place additional restrictions on the values allowed in the Accept , Accept-Language , and Content-Language headers. If any of those headers have "nonstandard" values, WebKit/Safari does not consider the request to be a "simple request". What values WebKit/Safari consider "nonstandard" is not documented, except in the following WebKit bugs:

          No other browsers implement these extra restrictions because they're not part of the spec.

          This operation performs a simple exchange between the client and the server, using CORS headers to handle the privileges:


          Let's look at what the browser will send to the server in this case, and let's see how the server responds:

          In response, the server returns a Access-Control-Allow-Origin header with Access-Control-Allow-Origin: * , which means that the resource can be accessed by any origin.

          Note: When responding to a credentialed requests request, the server must specify an origin in the value of the Access-Control-Allow-Origin header, instead of specifying the " * " wildcard.

          Preflighted requests

          The following is an example of a request that will be preflighted:


          Note: As described below, the actual POST request does not include the Access-Control-Request-* headers; they are needed only for the OPTIONS request.

          Let's look at the full exchange between client and server. The first exchange is the preflight request/response:

          The Access-Control-Request-Method header notifies the server as part of a preflight request that when the actual request is sent, it will do so with a POST request method. The Access-Control-Request-Headers header notifies the server that when the actual request is sent, it will do so with X-PINGOTHER and Content-Type custom headers. Now the server has an opportunity to determine whether it can accept a request under these conditions.

          Lines 13 - 22 above are the response that the server returns, which indicate that the request method ( POST ) and request headers ( X-PINGOTHER ) are acceptable. Let's have a closer look at lines 16-19:

          The server also sends Access-Control-Allow-Headers with a value of " X-PINGOTHER, Content-Type ", confirming that these are permitted headers to be used with the actual request. Like Access-Control-Allow-Methods , Access-Control-Allow-Headers is a comma-separated list of acceptable headers.

          Finally, Access-Control-Max-Age gives the value in seconds for how long the response to the preflight request can be cached without sending another preflight request. The default value is 5 seconds. In the present case, the max age is 86400 seconds (= 24 hours). Note that each browser has a maximum internal value that takes precedence when the Access-Control-Max-Age exceeds it.

          Once the preflight request is complete, the real request is sent:

          Preflighted requests and redirects

          Not all browsers currently support following redirects after a preflighted request. If a redirect occurs after such a request, some browsers currently will report an error message such as the following:

          The CORS protocol originally required that behavior but was subsequently changed to no longer require it. However, not all browsers have implemented the change, and thus still exhibit the originally required behavior.

          Until browsers catch up with the spec, you may be able to work around this limitation by doing one or both of the following:

          • Change the server-side behavior to avoid the preflight and/or to avoid the redirect
          • Change the request such that it is a simple request that doesn’t cause a preflight

          If that's not possible, then another way is to:

          However, if the request is one that triggers a preflight due to the presence of the Authorization header in the request, you won't be able to work around the limitation using the steps above. And you won't be able to work around it at all unless you have control over the server the request is being made to.

          Requests with credentials

          Note: When making credentialed requests to a different domain, third-party cookie policies will still apply. The policy is always enforced regardless of any setup on the server and the client as described in this chapter.


          Here is a sample exchange between client and server:

          Preflight requests and credentials

          CORS-preflight requests must never include credentials. The response to a preflight request must specify Access-Control-Allow-Credentials: true to indicate that the actual request can be made with credentials.

          Note: Some enterprise authentication services require that TLS client certificates be sent in preflight requests, in contravention of the Fetch specification.

          Firefox 87 allows this non-compliant behavior to be enabled by setting the preference: network.cors_preflight.allow_client_cert to true (bug 1511151). Chromium-based browsers currently always send TLS client certificates in CORS preflight requests (Chrome bug 775438).

          Credentialed requests and wildcards

          When responding to a credentialed request:

          • The server must not specify the " * " wildcard for the Access-Control-Allow-Origin response-header value, but must instead specify an explicit origin; for example: Access-Control-Allow-Origin: https://example.com
          • The server must not specify the " * " wildcard for the Access-Control-Allow-Headers response-header value, but must instead specify an explicit list of header names; for example, Access-Control-Allow-Headers: X-PINGOTHER, Content-Type
          • The server must not specify the " * " wildcard for the Access-Control-Allow-Methods response-header value, but must instead specify an explicit list of method names; for example, Access-Control-Allow-Methods: POST, GET

          If a request includes a credential (most commonly a Cookie header) and the response includes an Access-Control-Allow-Origin: * header (that is, with the wildcard), the browser will block access to the response, and report a CORS error in the devtools console.

          Also note that any Set-Cookie response header in a response would not set a cookie if the Access-Control-Allow-Origin value in that response is the " * " wildcard rather an actual origin.

          Cookie in the request (line 10) may also be suppressed in normal third-party cookie policies. The enforced cookie policy may therefore nullify the capability described in this chapter, effectively preventing you from making credentialed requests whatsoever.

          Cookie policy around the SameSite attribute would apply.

          Access-Control-Allow-Origin

          A returned resource may have one Access-Control-Allow-Origin header with the following syntax:

          Access-Control-Allow-Origin specifies either a single origin which tells browsers to allow that origin to access the resource; or else — for requests without credentials — the " * " wildcard tells browsers to allow any origin to access the resource.

          If the server specifies a single origin (that may dynamically change based on the requesting origin as part of an allowlist) rather than the " * " wildcard, then the server should also include Origin in the Vary response header to indicate to clients that server responses will differ based on the value of the Origin request header.

          Access-Control-Expose-Headers

          The Access-Control-Expose-Headers header adds the specified headers to the allowlist that JavaScript (such as getResponseHeader() ) in browsers is allowed to access.

          For example, the following:

          …would allow the X-My-Custom-Header and X-Another-Custom-Header headers to be exposed to the browser.

          Access-Control-Max-Age

          The Access-Control-Max-Age header indicates how long the results of a preflight request can be cached. For an example of a preflight request, see the above examples.

          The delta-seconds parameter indicates the number of seconds the results can be cached.

          Access-Control-Allow-Credentials

          The Access-Control-Allow-Credentials header indicates whether or not the response to the request can be exposed when the credentials flag is true. When used as part of a response to a preflight request, this indicates whether or not the actual request can be made using credentials. Note that simple GET requests are not preflighted, and so if a request is made for a resource with credentials, if this header is not returned with the resource, the response is ignored by the browser and not returned to web content.

          Access-Control-Allow-Methods

          The Access-Control-Allow-Methods header specifies the method or methods allowed when accessing the resource. This is used in response to a preflight request. The conditions under which a request is preflighted are discussed above.

          An example of a preflight request is given above, including an example which sends this header to the browser.

          Access-Control-Allow-Headers

          Origin

          The Origin header indicates the origin of the cross-site access request or preflight request.

          The origin is a URL indicating the server from which the request is initiated. It does not include any path information, only the server name.

          Note: The origin value can be null .

          Note that in any access control request, the Origin header is always sent.

          Access-Control-Request-Method

          Examples of this usage can be found above.

          Access-Control-Request-Headers

          Читайте также: