Ошибка access control allow origin header is present

This answer covers a lot of ground, so it’s divided into three parts:

  • How to use a CORS proxy to avoid “No Access-Control-Allow-Origin header” problems
  • How to avoid the CORS preflight
  • How to fix “Access-Control-Allow-Origin header must not be the wildcard” problems

How to use a CORS proxy to avoid “No Access-Control-Allow-Origin header” problems

If you don’t control the server your frontend code is sending a request to, and the problem with the response from that server is just the lack of the necessary Access-Control-Allow-Origin header, you can still get things to work—by making the request through a CORS proxy.

You can easily run your own proxy with code from https://github.com/Rob—W/cors-anywhere/.
You can also easily deploy your own proxy to Heroku in just 2-3 minutes, with 5 commands:

git clone https://github.com/Rob--W/cors-anywhere.git
cd cors-anywhere/
npm install
heroku create
git push heroku master

After running those commands, you’ll end up with your own CORS Anywhere server running at, e.g., https://cryptic-headland-94862.herokuapp.com/.

Now, prefix your request URL with the URL for your proxy:

https://cryptic-headland-94862.herokuapp.com/https://example.com

Adding the proxy URL as a prefix causes the request to get made through your proxy, which:

  1. Forwards the request to https://example.com.
  2. Receives the response from https://example.com.
  3. Adds the Access-Control-Allow-Origin header to the response.
  4. Passes that response, with that added header, back to the requesting frontend code.

The browser then allows the frontend code to access the response, because that response with the Access-Control-Allow-Origin response header is what the browser sees.

This works even if the request is one that triggers browsers to do a CORS preflight OPTIONS request, because in that case, the proxy also sends the Access-Control-Allow-Headers and Access-Control-Allow-Methods headers needed to make the preflight succeed.


How to avoid the CORS preflight

The code in the question triggers a CORS preflight—since it sends an Authorization header.

https://developer.mozilla.org/docs/Web/HTTP/Access_control_CORS#Preflighted_requests

Even without that, the Content-Type: application/json header will also trigger a preflight.

What “preflight” means: before the browser tries the POST in the code in the question, it first sends an OPTIONS request to the server, to determine if the server is opting-in to receiving a cross-origin POST that has Authorization and Content-Type: application/json headers.

It works pretty well with a small curl script — I get my data.

To properly test with curl, you must emulate the preflight OPTIONS the browser sends:

curl -i -X OPTIONS -H "Origin: http://127.0.0.1:3000" 
    -H 'Access-Control-Request-Method: POST' 
    -H 'Access-Control-Request-Headers: Content-Type, Authorization' 
    "https://the.sign_in.url"

…with https://the.sign_in.url replaced by whatever your actual sign_in URL is.

The response the browser needs from that OPTIONS request must have headers like this:

Access-Control-Allow-Origin:  http://127.0.0.1:3000
Access-Control-Allow-Methods: POST
Access-Control-Allow-Headers: Content-Type, Authorization

If the OPTIONS response doesn’t include those headers, the browser will stop right there and never attempt to send the POST request. Also, the HTTP status code for the response must be a 2xx—typically 200 or 204. If it’s any other status code, the browser will stop right there.

The server in the question responds to the OPTIONS request with a 501 status code, which apparently means it’s trying to indicate it doesn’t implement support for OPTIONS requests. Other servers typically respond with a 405 “Method not allowed” status code in this case.

So you’ll never be able to make POST requests directly to that server from your frontend JavaScript code if the server responds to that OPTIONS request with a 405 or 501 or anything other than a 200 or 204 or if doesn’t respond with those necessary response headers.

The way to avoid triggering a preflight for the case in the question would be:

  • if the server didn’t require an Authorization request header but instead, e.g., relied on authentication data embedded in the body of the POST request or as a query param
  • if the server didn’t require the POST body to have a Content-Type: application/json media type but instead accepted the POST body as application/x-www-form-urlencoded with a parameter named json (or whatever) whose value is the JSON data

How to fix “Access-Control-Allow-Origin header must not be the wildcard” problems

I am getting another error message:

The value of the ‘Access-Control-Allow-Origin’ header in the response
must not be the wildcard ‘*’ when the request’s credentials mode is
‘include’. Origin ‘http://127.0.0.1:3000‘ is therefore not allowed
access. The credentials mode of requests initiated by the
XMLHttpRequest is controlled by the withCredentials attribute.

For requests that have credentials, browsers won’t let your frontend JavaScript code access the response if the value of the Access-Control-Allow-Origin header is *. Instead the value in that case must exactly match your frontend code’s origin, http://127.0.0.1:3000.

See Credentialed requests and wildcards in the MDN HTTP access control (CORS) article.

If you control the server you’re sending the request to, a common way to deal with this case is to configure the server to take the value of the Origin request header, and echo/reflect that back into the value of the Access-Control-Allow-Origin response header; e.g., with nginx:

add_header Access-Control-Allow-Origin $http_origin

But that’s just an example; other (web) server systems have similar ways to echo origin values.


I am using Chrome. I also tried using that Chrome CORS Plugin

That Chrome CORS plugin apparently just simplemindedly injects an Access-Control-Allow-Origin: * header into the response the browser sees. If the plugin were smarter, what it would be doing is setting the value of that fake Access-Control-Allow-Origin response header to the actual origin of your frontend JavaScript code, http://127.0.0.1:3000.

So avoid using that plugin, even for testing. It’s just a distraction. To test what responses you get from the server with no browser filtering them, you’re better off using curl -H as above.


As far as the frontend JavaScript code for the fetch(…) request in the question:

headers.append('Access-Control-Allow-Origin', 'http://localhost:3000');
headers.append('Access-Control-Allow-Credentials', 'true');

Remove those lines. The Access-Control-Allow-* headers are response headers. You never want to send them in requests. The only effect of that is to trigger a browser to do a preflight.

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

Если вы давно хотели разобраться в CORS и вас достали постоянные ошибки, добро пожаловать под кат.

Ошибка в консоли вашего браузера

No ‘Access-Control-Allow-Origin’ header is present on the requested resource.

Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at https://example.com/

Access to fetch at ‘https://example.com’ from origin ‘http://localhost:3000’ has been blocked by CORS policy.

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

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

Но давайте-ка пойдем к истокам…

В начале был первый субресурс 

Субресурс  — это HTML элемент, который требуется вставить в документ или выполнить  в контексте этого документа. В 1993 году был введен первый тег <img>. С появлением веб стал более красивым, но заодно и стал сложнее.


Верни мне мой 1993 г.

Как вы поняли, если ваш браузер отображает страницу с <img>, он должен запросить этот тег из источника. Если браузер запрашивает тег из источника, который отличается от получателя по схеме, в полностью определенному имени хоста или порту, то это и есть запрос между различными источниками (cross-origin request).

Источники & cross-origin

Источник идентифицируется следующей тройкой параметров: схема, полностью определенное имя хоста и порт. Например, <http://example.com> и <https://example.com> имеют разные источники: первый использует схему http, а второй https. Вдобавок, портом для http по умолчанию является 80, тогда как для https — 443. Следовательно, в данном примере 2 источника отличаются схемой и портом, тогда как хост один и тот же (example.com).

Таким образом, если хотя бы один из трех элементов у двух ресурсов отличается, то источник ресурсов также считается разным.

Если, к примеру, мы будем сравнивать источник <https://blog.example.com/posts/foo.html> с другими источниками, то мы получим следующие результаты:

Пример запроса между различными источниками: когда ресурс (то есть, страница) типа <http://example.com/posts/bar.html> попробует отобразить тег из источника <https://example.com> (заметьте, что схема поменялась!).

Слишком много опасностей запроса между различными источниками

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

Когда тег <img> появился во Всемирной Паутине, мы тем самым открыли ящик Пандоры. Некоторое время спустя в Сети появились теги <script>, <frame>, <video>, <audio>, <iframe>, <link>, <form> и так далее. Эти теги могут быть загружены браузером уже после загрузки страницы, поэтому они все могут быть запросами в пределах одного источника и между о разными источниками. 

Давайте погрузимся в воображаемый мир, где не существует CORS и веб-браузеры допускают все типы запросов между источниками. 

Предположим, у меня есть страница на сайте evil.com с <script>. На первый взгляд это обычная страница, где можно прочесть полезную информацию. Но я специально создал код в теге <script>, который будет отправлять специально созданный запрос по удалению аккаунта (DELETE/account) на сайт банка. Как только вы загрузили страницу, JavaScript запускается и AJAX-запрос попадает в  API банка. 


Вжух, нет вашего аккаунта

Немыслимо, да? Представьте, что вы читаете что-то на веб странице и вам приходит электронное письмо от банка, что ваш аккаунт успешно удален. Знаю, знаю… если бы было так просто провести любую банковскую операцию… Отвлекся.

Для того чтобы мой вредоносный <script> сработал, ваш браузер должен был также отправить ваши учетные данные (куки), взятые с банковского сайта, как часть запроса. Именно таким образом банковские серверы идентифицировали бы вас и знали, какой аккаунт нужно удалить.

Давайте рассмотрим другой, не такой зловещий сценарий.

Мне нужно опознать людей, которые работают на Awesome Corp, внутренний сайт этой компании  находится по адресу intra.awesome-corp.com. На моем сайте, dangerous.com, у меня есть <img src="https://intra.awesome-corp.com/avatars/john-doe.png">.

У пользователей, у которых нет активного сеанса с intra.awesome-corp.com, аватарка не отобразится, так как это приведет к ошибке. Однако если вы совершили вход во внутреннюю сеть Awesome Corp., как только вы откроете мой dangerous.com сайт, я буду знать, что у вас там есть аккаунт. 

Это означает, что я смогу прощупать  определенную информацию о вас. Конечно, для меня будет сложнее устроить атаку, но знание о том, что у вас есть доступ к Awesome Corp., является потенциальным направлением  для атаки.


Утечка информации к 3-им лицам

Эти два примера крайне упрощены, но именно такие угрозы обусловили необходимость политики одинакового источника и CORS… Существуют разнообразные опасности, связанные с запросами между разными источниками. Некоторые из них можно сгладить, другие нет: они укоренены в природе интернета. Однако огромное количество заблокированных атак — это заслуга CORS.

Но до зарождения CORS существовала политика одинакового источника.

Политика одинакового источника

Политика одинакового источника предотвращает cross-origin атаки, блокируя доступ для прочтения загружаемых ресурсов из другого источника. Такая политика все еще разрешает нескольким тегам вроде <img> загружать ресурсы из других источников. 

Политика одинакового источника введена Netscape Navigator 2.02 в 1995 году, изначально для защищенного cross-origin доступа к Объектной модели документа (DOM).

Даже несмотря на то, что внедрение политики одинакового источника не требует придерживаться определенного порядка действий, все современные браузеры следуют этой политике в той или иной форме. Принципы политики описаны в запросе на спецификацию RFC6454 Инженерного совета интернета (Internet Engineering Task Force).

Выполнение политики одинакового источника определено этим сводом правил:

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

CORS же появился с целью смягчения политики одинакового источника и для тонкой настройки доступа между различными источниками.

Врываемся в CORS

Я уже разъяснил, что такое источник, как он определяется, какие ошибки бывают у запросов с различными источниками и политику общего происхождения, выполняемые браузером. 

Давайте разберемся с совместным использованием ресурсов различными источниками (CORS). CORS — это механизм, который дает контролировать доступ к тегам на веб странице по сети. Механизм классифицируется на три разные категории доступа тегов:

  1. Запись из разных источников
  2. Вставка из разных источников 
  3. Считывание из разных источников 

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

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

Вставки из разных источников — это теги, загружаемые через <script>, <link>, <img>, <video>, <audio>, <object>, <embed>, <iframe> и т.п. Все они разрешены по умолчанию. <iframe> выделяется на их фоне, так как он используется для загрузки другой страницы внутри фрейма. Его обрамление в зависимости от источника может регулироваться посредством использования заголовка  X-Frame-options.

Что касается <img> и других вставных тегов, то они устроены так, что сами инициируют запросы из разных источников cross-origin запроса. Именно поэтому в CORS существует различие между вставкой из разных источников и считыванием из разных источников. 

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

Если ваш браузер обновлён, то он уже дополнен всей этой эвристикой. 

Запись из разных источников

Операции записи из разных источников порой очень проблематичны. Давайте рассмотрим пример и посмотрим на CORS в деле.

Во-первых, у нас будет простой Crystal (с использованием Kemal) HTTP сервер:

require "kemal"

port = ENV["PORT"].to_i || 4000

get "/" do
  "Hello world!"
end

get "/greet" do
  "Hey!"
end

post "/greet" do |env|
  name = env.params.json["name"].as(String)
  "Hello, #{name}!"
end

Kemal.config.port = port
Kemal.run

Он просто берет запрос по ссылке /greet с name в теле запроса и возвращает Hello #{name}!. Чтобы запустить это маленький Crystal сервер мы можем написать

$ crystal run server.cr

 Так запускается сервер, который будет слушать localhost:4000. Если мы откроем localhost:4000 в нашем браузере, то появится страница с тривиальным «Hello World». 


Hello world!

Теперь, когда мы знаем, что наш сервер работает, давайте из консоли нашего браузера сделаем запрос  POST /greet  на сервер, слушающий localhost:4000,. Мы можем это сделать, используя fetch:

fetch(
  'http://localhost:4000/greet',
  {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ name: 'Ilija'})
  }
).then(resp => resp.text()).then(console.log)

Как только мы его запустили, мы увидим, что приветствие вернется из сервера:


Привет!

Это был POST запрос, но не из разных источников. Мы отправили запрос из браузера, где была отображена страница с адреса http://localhost:4000 (источник), к тому же источнику.

Теперь, давайте попробуем повторить такой же  запрос но с различными источниками. Мы откроем https://google.com и попробуем тот же запрос с той же вкладки нашего браузера:


Привет, CORS!

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

В первом примере, где мы отправили запрос в http://localhost:4000/greet из вкладки, которая отображала http://localhost:4000, наш браузер смотрит на запрос и разрешает, так как ему кажется, что наш сайт запрашивает наш сервер (что есть отлично). Но во втором примере где наш сайт (https://google.com) хочет написать на http://localhost:4000, тогда наш браузер отмечает этот запрос и не разрешает ему пройти. 

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

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


Как видно в панеле Network, отправленных запроса две штуки

Интересно заметить, то у первого запроса в HTTP фигурирует метод OPTIONS, в то время как у второго – метод POST.

Если внимательно посмотреть на запрос OPTIONS, то мы увидим, что этот запрос отправлен нашим браузером до отправления запроса POST.


Смотрим запрос OPTIONS

Интересно, что несмотря на то, что статус запроса OPTIONS был HTTP 200, он был все же отмечен красным в списке запросов. Почему?

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

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

Следовательно, в примере выше, несмотря на то, что мы отправили запрос POST, браузер считает наш запрос сложным из-за заголовка Content-Type: application/json.

Если бы мы изменили наш сервер так, чтобы он обрабатывал контент text/plain (вместо JSON), мы бы могли обойтись без предварительных запросов:

require "kemal"

get "/" do
  "Hello world!"
end

get "/greet" do
  "Hey!"
end

post "/greet" do |env|
  body = env.request.body

  name = "there"
  name = body.gets.as(String) if !body.nil?

  "Hello, #{name}!"
end

Kemal.config.port = 4000
Kemal.run

Теперь, когда мы можем отправить наш запрос с заголовком Content-type: text/plain:

fetch(
  'http://localhost:4000/greet',
  {
    method: 'POST',
    headers: {
      'Content-Type': 'text/plain'
    },
    body: 'Ilija'
  }
)
.then(resp => resp.text())
.then(console.log)

Теперь, пока предварительный запрос не будет отправлен, CORS политика браузера будет постоянно блокировать запрос:


CORS стоит насмерть

Но так как мы создали запрос, который не классифицируется как сложный, наш браузер не заблокирует запрос.


Запрос прошел

Проще говоря, наш сервер неправильно настроен на принятие text/plain запросов из разных источников без какой-либо защиты и наш браузер не сможет ничего с этим поделать. Но все же он делает следующую вещь: он не показывает нашу открытую страницу/вкладку в ответ на это запрос. Следовательно, в этом случае CORS не блокирует запрос, он блокирует ответ.

CORS политика вашего браузера считает, что это фактически считывание из разных источников, так как, несмотря на то, что запрос был отправлен как POST, Content-type значение заголовка по сути приравнивает его к GET. Считывания из разных источников заблокированы по умолчанию, следовательно мы видим заблокированный запрос в нашей панели Network.

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

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

  • Access-Control-Allow-Methods, который указывает на то, какие методы поддерживаются URL-ом ответа в контексте CORS протокола. 
  • Access-Control-Allow-Headers, который указывает, на то, какие заголовки поддерживаются URL-ом ответа в контексте CORS протокола.
  • Access-Control-Max-Age, который указывает число секунд (5 по умолчанию) и это значение соответствует периоду, на который предоставляемая заголовками Access-Control-Allow-Methods и Access-Control-Allow-Headers информация может быть кэширована.

Давайте вернемся к предыдущему примеру, где мы отправили сложный запрос:

etch(
  'http://localhost:4000/greet',
  {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ name: 'Ilija'})
  }
).then(resp => resp.text()).then(console.log

Мы уже выяснили, что когда мы отправляем запрос, наш браузер будет сверяться с сервером, можно ли выполнить  запрос с данными из разных источников. Чтобы обеспечить работоспособность в среде с разными источниками, мы должны сначала добавить конечную точку OPTIONS/greet к нашему серверу. В заголовке ответа новая конечная точка  должна сообщить браузеру, что запрос на POST /greet с заголовком Content-type: application/json из источника https://www.google.com может быть принят. 

Мы это сделаем, используя заголовки Access-Control-Allow-*:

options "/greet" do |env|
  # Allow `POST /greet`...
  env.response.headers["Access-Control-Allow-Methods"] = "POST"
  # ...with `Content-type` header in the request...
  env.response.headers["Access-Control-Allow-Headers"] = "Content-type"
  # ...from https://www.google.com origin.
  env.response.headers["Access-Control-Allow-Origin"] = "https://www.google.com"
end

Если мы запустим сервер и отправим запрос, то 


Все еще заблокирован?

Наш запрос остается заблокированным. Даже несмотря на то что наша конечная точка OPTIONS/greet в самом деле разрешила запрос, мы пока еще видим сообщение об ошибке. В нашей панели Network происходит кое-что интересное:


OPTIONS стал зеленым!

Запрос в конечную точку OPTIONS/greet прошел успешно! Однако запрос POST /greet все еще терпит неудачу. Если взглянуть на внутрь запроса POST /greet мы увидим знакомую картинку:


POST тоже стал зеленым?

На самом деле запрос удался: сервер вернул HTTP 200. Предварительный запрос заработал. Браузер совершил POST-запрос вместо того, чтобы его заблокировать. Однако ответ на запрос POST не содержит никаких CORS заголовков, так что даже несмотря на то, что браузер сделал запрос, он заблокировал любой ответ. 

Чтобы разрешить браузеру обработать ответ из запроса POST /greet, нам также нужно добавить заголовок CORS к конечной точке POST:

post "/greet" do |env|
  name = env.params.json["name"].as(String)

  env.response.headers["Access-Control-Allow-Origin"] = "https://www.google.com"

  "Hello, #{name}!"
end

Добавляя к заголовку Access-Control-Allow-Origin заголовок ответа, мы сообщаем браузеру, что вкладка с открытой https://www.google.com имеет доступ к содержимому ответа.

Если попытаться еще разок, то 


POST работает!

Мы увидим, что POST /greet получил для нас ответ без каких-либо ошибок. Если посмотреть на панели Network, то мы увидим, что оба запроса зеленые.


OPTIONS & POST в деле!

Используя надлежащие заголовки ответа в нашем конечной точке OPTIONS /greet, выполнявшей предварительный запрос, мы разблокировали конечную точку  POST /greet нашего сервера, так, чтобы он имел доступ к информации из разных источников. Вдобавок, предоставляя правильный CORS заголовок ответа в ответе конечной POST /greet, мы позволили браузеру обрабатывать  ответы без возникновения блокировок.

Считывание из разных источников 

Как мы отмечали ранее, считывание из разных источников  блокируются по умолчанию. Это делается намеренно: мы не хотели бы загружать другие ресурсы из других источников в пределах нашего источника.

Скажем, в нашем Crystal сервере есть действие GET /greet.

get "/greet" do
  "Hey!"
end

Из нашей вкладки, что передала www.google.com если мы попробуем запросить эндпоинт GET /greet, то CORS нас заблокирует:


CORS блокирует

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

На самом деле, как и прежде, наш браузер разрешил запрос: мы получили код состояния HTTP 200. Однако он не показал нашу открытую страницу/вкладку в ответ на этот запрос. Еще раз, в данном случае CORS не заблокировал запрос, он заблокировал ответ.

Так же, как и в случае с записью из разных источников, мы можем освободить CORS и обеспечить  считывание  из разных источников, добавляя заголовок Access-Control-Allow-Origin:

get "/greet" do |env|
  env.response.headers["Access-Control-Allow-Origin"] = "https://www.google.com"
  "Hey!"
end

Когда браузер получит ответ от сервера, он проверит заголовок Access-Control-Allow-Origin и исходя из его значения решит, сможет ли он позволить странице прочитать ответ. Учитывая, что в данном случае значением является https://www.google.com, итог будет успешным:


Успешный запрос GET между разными источниками

Вот как наш браузер защищает нас от считывания из разных источников  и соблюдает директивы server, сообщенные через заголовки.

Тонкая настройка CORS

Как мы уже видели в предыдущих примерах, чтобы смягчить политику CORS нашего сайта мы можем присвоить опцию Access-Control-Allow-Origin для нашего действия /greet значению https://www.google.com:

post "/greet" do |env|
  body = env.request.body

  name = "there"
  name = body.gets.as(String) if !body.nil?

  env.response.headers["Access-Control-Allow-Origin"] = "https://www.google.com"
  "Hello, #{name}!"
end

Это разрешит нашему источнику https://www.google.com запросить наш сервер, и наш браузер свободно сделает это. Имея Access-Control-Allow-Origin мы можем попробовать снова выполнить вызов fetch:


Сработало!

И это работает! С новой политикой CORS мы можем вызвать действие /greet из нашей вкладки, в которой загружена страница https://www.google.com. Или, мы могли бы присвоить заголовку значение *, которое сообщило бы браузеру, что сервер может быть вызван из любого источника. 

Устанавливая такую конфигурацию, нужно тщательно взвесить все риски. Тем не менее, вставка заголовков с нестрогими требованиями к CORS почти всегда безопасна. Есть эмпирическое правило:  если вы открываете URL в приватной вкладке и вас устраивает информация, которая там отображается, то вы можете установить разрешающую CORS политику (*) для  данного URL.

Другой способ настройки CORS на нашем сайте — это использование заголовка запроса Access-Control-Allow-Credentials. Access-Control-Allow-Credentials запрашивает браузер, показывать ли ответ JavaScript  коду клиентской части, когда в качестве режима учетных данных запроса используется  include.

Учетный режим запросов исходит из внедрения Fetch API, который в свою очередь корнями идет к объектам XMLHttpRequest:

var client = new XMLHttpRequest()
client.open("GET", "./")
client.withCredentials = true

С вводом fetch, метод withCredentials превратился в опциональный аргумент fetch запроса:

fetch("./", { credentials: "include" }).then(/* ... */)

Доступными опциями для обработки учетных данных являются omit, same-origin и include. Доступны разные режимы, так что программист может настроить отправляемый запрос, пока ответ от сервера сообщает браузеру как вести себя, когда учетные  данные отправлены с запросом (через заголовок Access-Control-Allow-Credential).

Спецификация  Fetch API содержит подробно расписанный и детально разобранный функционал  взаимодействия CORS и Web API fetch, а также характеризует механизмы безопасности, используемые браузерами.

Несколько правильных решений

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

Свободный доступ для всех 

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

Значение * хорошо подойдет в случаях, когда

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

Опасные последствия от применения  такой конфигурации наступают, когда контент подается на частном сервере (то есть за брандмауэрами или VPN). Когда вы подключены через VPN, у вас есть доступ к файлам в сети компании:


Сверхупрощение VPN 

Теперь, когда взломщик захостит dangerous.com, который содержит ссылку файла с VPN, то (в теории) он может создать скрипт в их сайте, который сможет иметь доступ к этому файлу:


Утечка файла

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

Всё в семью

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

Проще всего это сделать, периодически отправляя данные, реализовав в браузере асинхронные запросы с помощью JavaScript. На машинном интерфейсе у нас есть простой API, который берет эти запросы из браузеров наших пользователей и хранит данные на машине для последующей обработки.

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

В данных случаях мы хотим, чтобы наш API установил заголовок Access-Control-Allow-Origin к URL нашего сайта. Это обеспечит нас тем, что браузеры никогда не отправят запросы нашему API с других страниц.

Если пользователи или другие сайты попробуют взломать данные нашего аналитического  API, то набор заголовков Access-Control-Allow-Origin, установленный на нашем API, не пропустит  запрос.

Null источник 

Другим интересным случаем является null источник. Это происходит, когда ресурс получает доступ от браузера, который отображает локальный файл. Например, запросы, исходящие из определенного JavaScript, работающего в статическом файле на вашем ПК, имеют заголовок Origin со значением null

В таких случаях, если ваш сервер разрешает доступ к ресурсам для null источников, то это может мешать продуктивности разработчика. Разрешение null источников в политике CORS должно быть сделано намеренно, и только если пользователями вашего сайта/продукта пока являются только его разработчики.

Пропускай куки, если возможно

Как мы уже видели с Access-Control-Allow-Credentials, куки не включены по умолчанию. Чтобы разрешить отправку куки с разных источников, нужно просто вернуть Access-Control-Allow-Credentials: true. Этот заголовок сообщит браузерам, что им разрешается пересылать удостоверяющие данные (то есть куки) в запросах между разными источниками.

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

Куки между разными источниками полезнее всего в ситуациях, когда вы точно знаете какие именно клиенты будут иметь доступ к вашему серверу. Именно поэтому семантика CORS не позволяет нам установить Access-Control-Allow-Origin: *, когда удостоверяющие данные между разными источниками разрешены.

В то время как комбинация из Access-Control-Allow-Origin: * и Access-Control-Allow-Credentials: true технически разрешается, она является анти-паттерном и ее следует безусловно избегать.

Если вы хотите, чтобы к вашим серверам имели доступ разные клиенты и источники, то вам стоит рассмотреть возможность создания API с аутентификацией через пароль вместо использования куков. Но если вариант с API не является оптимальным, то обеспечьте себя защитой от фальсификатора межсайтовых запросов (CSRF).

Дополнительная литература

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

  • Cross-Origin Resource Sharing (CORS)
  • Access-Control-Allow-Credentials header on MDN Web Docs
  • Authoritative guide to CORS (Cross-Origin Resource Sharing) for REST APIs
  • The «CORS protocol» section of the Fetch API spec
  • Same-origin policy on MDN Web Docs
  • Quentin’s great summary of CORS on StackOverflow


Наши серверы можно использовать для разработки и хостинга сайтов любой сложности.

Зарегистрируйтесь по ссылке выше или кликнув на баннер и получите 10% скидку на первый месяц аренды сервера любой конфигурации!

There are several online apps available nowadays. The majority of systems now offer some form of online user interface. These interfaces deal with the client-side presentation or how to display it to the user. The user can interact with the web apps and request information or updates from the server. The data gets saved in a database that the server may access. The client-side web application requests information from the web server, which the server responds with by retrieving it from the database. Because the data or information may be sensitive, a security safeguard must be in place to ensure its integrity. CORS, or Cross-Origin Resource Sharing, is a security procedure to provide this data‘s integrity.

We shall study what CORS is and how it works in general in this article. We will learn what preflight requests are and how CORS relies on them. Furthermore, we’ll go through how we can use CORS and solve the issues that arise from it in our apps.

What is the same-origin policy?

In internet security, the same-origin policy restricts the interaction of a document or script loaded from one origin with a resource loaded from another origin.

What is CORS?

CORS stands for Cross-Origin Resource Sharing. When one domain requests resources from another, it is called a cross-domain request. Due to security concerns, we may only want a few domains (other than our own) to have access to the server’s resources. That is where CORS comes in. The CORS technique allows a server to specify resources it will load from other origins (domains, schemes, or ports) other than HTTP headers.

Prior to CORS, there was no ability to call an API endpoint in a separate domain for security concerns. The Same-Origin Policy prevents this.

Why do we need CORS?

This method stops hackers from installing malicious scripts on different websites. For instance, a hacker may call example.com through AJAX and make modifications on behalf of the signed-in user.

Cross-origin access is also beneficial or even required in some other genuine circumstances, though. For instance, if our React web application calls an API backend set up on a separate domain. It won’t be possible without CORS.

How does CORS work?

CORS enables the server to explicitly allow specific sources, allowing it to override the same-origin restriction. If we set up our CORS server, each response will include an additional header with the key “Access-Control-Allow-Origin.”

What are simple requests?

A simple request is one that does not begin a preflight request before sending the actual request. A simple request fits all of the following requirements:

  1. The request uses one of the permitted methods, such as GET, HEAD, or POST.
  2. Aside from the user-agent generated headers, the only headers that may be manually set are,
    1. Accept
    2. Accept-Language
    3. Content-Language
    4. Content-Type
  3. The Content-Type header can only include one of the following values:
    1. application/x-www-form-urlencoded
    2. multipart/form-data
    3. text/plain
  4. There is no event listener associated with XMLHttpRequest.upload.
  5. The request makes no use of a ReadableStream object.

What is a preflight request?

A CORS preflight request examines the server’s ability to employ particular methods and headers and the server’s knowledge of the CORS protocol.

Browsers automatically generate preflight requests. Therefore, front-end developers often don’t need to write them.

Using the “Access-Control-Max-Age” header, it is possible to selectively cache the preflight responses for requests made at the same URL. The browser employs a unique cache for preflight responses distinct from the browser’s standard HTTP cache.

Credentialed requests

CORS is also capable of making “credentialed” requests. In these requests, the server and client can communicate via cookies (which may hold essential credentials).

CORS does not contain cookies on cross-origin requests by default. Including cookies in the cross-origin request can result in a vulnerability known as cross-site request forgery, or CSRF. CORS needs both the server and the client to confirm that it is okay to include cookies on requests in order to decrease the possibility of CSRF vulnerabilities.

The HTTP response headers used in CORS

We explained how CORS works by including additional headers with the response indicating whether the origin is on the server’s allowlist. Let’s have a look at some of the headers that CORS employs for this reason.

Access-Control-Allow-Origin

The Access-Control-Allow-Origin header defines an origin and instructs browsers to permit that origin to access server resources for requests without credentials. It may also include a wildcard *, which instructs the browser that any origin can access the server’s resources for requests without credentials.

Access-Control-Allow-Origin: *

Code language: plaintext (plaintext)

However, we cannot use a wildcard in the Access-Control-Allow-Origin header for requests containing credentials or cookies in general. Only one origin should be provided in this situation.

Access-Control-Allow-Origin: www.example.com

Code language: plaintext (plaintext)

Access-Control-Max-Age

The browser can store a preflight request for a given length of time using the Access-Control-Max-Age header.

Access-Control-Max-Age: 1800

Code language: plaintext (plaintext)

Access-Control-Allow-Methods

It is used in response to a preflight request to specify the method or methods that are allowed to access the resource.

Access-Control-Allow-Methods: GET, POST, PUT

Code language: plaintext (plaintext)

Access-Control-Allow-Headers

As part of a preflight request, the Access-Control-Allow-Headers header specifies which HTTP headers the client can use during the actual request.

Access-Control-Allow-Headers: Content-Type

Code language: plaintext (plaintext)

How to fix the CORS errors in Node.js and Express.js applications?

You may have encountered the CORS error “no ‘access-control-allow-origin’ header is present on the requested site” when constructing a full-stack web application. It occurs because no headers are sent to the browser in the preflight request informing the browser if the origin is permitted to access the resource.

There are several solutions to this problem in a Node.js and Express.js web server. We will be discussing them one by one.

Setting the correct headers manually

To address the CORS problem, we may manually add the necessary headers to each request. We will use middleware to set these headers whenever our server receives a request for resources. Create a middleware using the code below to set the needed headers to address the CORS error.

app.use((req, res, next) => { res.setHeader("Access-Control-Allow-Origin", "*"); res.setHeader("Access-Control-Allow-Methods", "POST, GET, PUT"); res.setHeader("Access-Control-Allow-Headers", "Content-Type"); next(); })

Code language: JavaScript (javascript)

Here we have set the origin to *. It means for simple requests like GET, HEAD, or POST; the server allows all the origins to access the server’s resources.

There might be a problem if the client’s browser sends a preflight request. The origin should not be a wildcard or * for handling preflight requests. Therefore, we can update the code a little to address preflight requests.

app.use((req, res, next) => { res.setHeader("Access-Control-Allow-Origin", "https://example.com"); res.setHeader("Access-Control-Allow-Methods", "POST, GET, PUT"); res.setHeader("Access-Control-Allow-Headers", "Content-Type"); next(); })

Code language: JavaScript (javascript)

As an alternative to middleware, we may use the app.options method over a specific endpoint to listen for preflight requests. The preflight request is an OPTIONS request (rather than a GET, POST, or PUT).

app.options("/", (req, res) => { res.setHeader("Access-Control-Allow-Origin", "https://example.com"); res.setHeader("Access-Control-Allow-Methods", "POST, GET, PUT"); res.setHeader("Access-Control-Allow-Headers", "Content-Type"); res.sendStatus(204); });

Code language: JavaScript (javascript)

Using the cors NPM package

Express.js created the cors package. We use it to simplify CORS handling by abstracting the complexities of setting up the correct headers, managing preflight requests, and so on behind an easy-to-use API.

Install the cors package using the NPM package manager.

npm install cors

Code language: Bash (bash)

To use the cors middleware provided by the cors library, we write the following code.

const cors = require("cors"); app.use(cors());

Code language: JavaScript (javascript)

The “Access-Control-Allow-Origin” header is set to wildcard or * , by default in the response delivered.

Without providing any additional arguments to the cors middleware.

Without providing any additional arguments to the cors middleware.

We may optionally supply additional arguments to the cors middleware to modify the default behavior. Let’s look at an example.

app.use(cors({ origin: 'https://example.com' }));

Code language: JavaScript (javascript)

Providing an origin to the cors middleware.

Without providing any additional arguments to the cors middleware.

We may optionally supply additional arguments to the cors middleware to modify the default behavior. Let’s look at an example.

app.use(cors({ origin: 'https://example.com' }));

Code language: JavaScript (javascript)

Providing an origin to the cors middleware.

Providing an origin to the cors middleware.

Conclusion

This article taught us what CORS is and how it generally works. Furthermore, we looked at what preflight requests are and how CORS relies on them. Finally, we learned how to use CORS and solve the issues arising from it in our apps.

Thank you so much for reading ?

In this article we are going to few possible fixes we can apply when we get an error “Response to preflight request doesn’t pass access control check: No ‘Access-Control-Allow-Origin’ header is present on the requested resource. Origin ‘http://localhost:58018’
is therefore not allowed access”. We get this error when we are trying to get some data from another origin may be via an AJAX call.
In this post, we will discuss the solutions for this error in detail and we will also discuss Cross Origin Requests. Here I am going to use Visual
Studio 2015, Web API 2. Hope you will like this.

Background

Hosted
Web API on a server, and what that API does is, it will just return the data in JSON format.
But when we try to consume this Web API via an Ajax call, was getting the error “No ‘Access-Control-Allow-Origin’ header is present on the requested resource”. Solved the same issues in different ways. Here we are going to share those.

Using the code

Assume that you have created a Web API and hosted it on your server. If you are new to Web API, you can always get some information from here Articles
Related To Web API .

We all will have some situations where we need to fetch some data from another domain or another site, right? If it is from the same site, you won’t be facing any issues at all. Like you are calling an Ajax call from the page www.SibeeshPassion.com/Receiver.html
to www.SibeeshPassion.com/Sender.html to get the data, here the origin is same. and therefore you will get the data. What happens is when the sender and receiver are not of the same origin. Like you need to get the data from www.Microsoft.com by an Ajax call
in www.SibeeshPassion.com/Receiver.html. The browser will not allow you to get the sensitive data from other domain, for the security purpose your browser will return you “No ‘Access-Control-Allow-Origin’”. To overcome this, we have something called Cross
Origin Resource Sharing (CORS). Basically, the process of allowing other sites to call your Web API is called CORS. According to W3
Org CORS is a standard which tells server to allow the calls from other origins given. It is much secured than using JSONP(Previously we had been using JSON for getting the data from other domains.).

Fix To No Access-Control-Allow-Origin header is present

We can fix this issue in two ways,

  • By using Microsoft.AspNet.WebApi.Cors
  • By adding header information in Web.config

    We will explain both now.

    By using Microsoft.AspNet.WebApi.Cors

    To work with this fix, you must include the package By using Microsoft.AspNet.WebApi.Cors from
    Manage Nuget window.

    CORS_In_Manage_NuGet_Package

    CORS_In_Manage_NuGet_Package

    Now got to App_Start folder from your solution. Then click on the file WebApiConfig.cs,
    this is the file where we set the configuration for our Web API.

    Web_API_Config_Class_File

    Web_API_Config_Class_File

    Then you can add the preceding codes in the static function Register.

    var cors = new EnableCorsAttribute("*", "*", "*");

               config.EnableCors(cors);

    IF you do this, the CORS will be applied globally for all the Web API controller you have. This is the easiest way of doing it. Now if you want to see the metadata of EnableCorsAttribute,
    you can see find it below.

    // Summary:

           //     Initializes a new instance of the System.Web.Http.Cors.EnableCorsAttribute class.

           //

           // Parameters:

           //   origins:

           //     Comma-separated list of origins that are allowed to access the resource. Use

           //     "*" to allow all.

           //

           //   headers:

           //     Comma-separated list of headers that are supported by the resource. Use "*" to

           //     allow all. Use null or empty string to allow none.

           //

           //   methods:

           //     Comma-separated list of methods that are supported by the resource. Use "*" to

           //     allow all. Use null or empty string to allow none.

           public
    EnableCorsAttribute(string
    origins, string
    headers, string
    methods);

    As it is mentioned, it accepts the parameters origins, headers, methods. Here we pass * to all the three parameters to make everything to be allowable.

    You can also try the same as below in the Register function. Here we are going to apply CORS for a particular controller, which means it will be applied for all the actions in the controller. Before that make sure you have added the preceding code in your WebApiConfig.cs
    file

    And in the API controller, you need to set the origins, headers, methods as preceding.

    using
    System;

    using
    System.Collections.Generic;

    using
    System.IO;

    using
    System.Linq;

    using
    System.Net;

    using
    System.Net.Http;

    using
    System.Web.Http;

    using
    Newtonsoft.Json;

    using
    Newtonsoft.Json.Converters;

    using
    System.Configuration;

    using
    System.Data;

    using
    System.Data.SqlClient;

    using
    System.Runtime.Serialization;

    using
    System.Text;

    using
    System.Web;

    using
    System.Web.Http.Cors;

    namespace
    APIServiceApplication.Controllers

    {

        [EnableCors(origins:
    "*", headers:
    "*", methods:
    "*")]

        public
    class
    DefaultController : ApiController

        {

        }

    }

    Make sure that you have added namespace using System.Web.Http.Cors; to use
    CORS. You can always disable CORS in an action by using [DisableCors].

    namespace
    APIServiceApplication.Controllers

    {

        [EnableCors(origins:
    "*", headers:
    "*", methods:
    "*")]

        public
    class
    DefaultController : ApiController

        {

            [DisableCors]

            public
    string
    XMLData(
    string
    id)

            {

                return
    "Your requested product"
    + id;

            }

        }

    }

    Here we have disabled CORS for the action XMLData. And again if you need to
    apply CORS only in a single action, you can do that as follows.

    namespace
    APIServiceApplication.Controllers

    {

        public
    class
    DefaultController : ApiController

        {

            [EnableCors(origins:
    "*", headers:
    "*", methods:
    "*")]

            public
    string
    XMLData(
    string
    id)

            {

                return
    "Your requested product"
    + id;

            }

        }

    }

    Hope you are aware of how to enable CORS now.

    By adding header information in Web.config

    Another fix we can do is that add some tags in our Web.config file.

    <system.webServer>

          <httpProtocol>

            <customHeaders>

              <add
    name="Access-Control-Allow-Origin"
    value="*"
    />

              <add
    name="Access-Control-Allow-Headers"
    value="Content-Type"
    />

              <add
    name="Access-Control-Allow-Methods"
    value="GET,POST,PUT,DELETE,OPTIONS"
    />

             <add
    name="Access-Control-Allow-Credentials"
    value="true"
    />

            </customHeaders>

          </httpProtocol>

      </system.webServer>

    As you can see we have added keys with value for the listed items.

  • Access-Control-Allow-Origin (For Origin)
  • Access-Control-Allow-Headers (For Headers)
  • Access-Control-Allow-Methods (For Methods)

    Now if you go to your server and check, you can see that all the things are configured perfectly. Configured the API on the server IIS, so going to see Response Header settings in IIS.

    Go to the command window and type inetmgr and click OK, your IIS will open shortly, now find your Web API which you have already configured under Default Web Site. Before doing this, please make sure that you have configured IIS in your windows. If you don’t
    know how to configure, strongly recommend you to read Configure IIS in Windows.

    Configured_Web_API_in_IIS

    Configured_Web_API_in_IIS

    Go to Features View and double click on HTTP Response Headers under IIS category.

    HTTP_Response_Headers_In_IIS

    HTTP_Response_Headers_In_IIS

    You can see all the settings has been configured there.

    HTTP_Response_Headers_Available

    HTTP_Response_Headers_Available

    That’s all, now if you run your application, you will be able to fetch the data from your Web API.

    Conclusion

    Miss anything that you may think which is needed? Have you ever faced this issue? Did you try Web API yet? Hope you liked this article. Please share your valuable suggestions and feedback.

Hello, this is my request:

axios({ method: 'POST', url:${SERVER_ADDRESS}/api/v1/manager/restaurant/${restaurantName}/payment-methods, crossDomain: true, data: { payment_methods: checkedPayments }, }) .then(() => { dispatch(loadPayments(restaurantName)); }).catch((error) => { console.log(error); dispatch(paymentsError()); });

the server is laravel 5, it is responding with:

XMLHttpRequest cannot load http://localhost:8000/api/v1/manager/restaurant/accusamus/payment-methods. Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:3000' is therefore not allowed access.

Server headers are set with CORS middleware like this:

    return $next($request)
        ->header("Access-Control-Expose-Headers", "Access-Control-*")
        ->header("Access-Control-Allow-Headers", "Access-Control-*, Origin, X-Requested-With, Content-Type, Accept")
        ->header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS, HEAD')
        ->header('Access-Control-Allow-Origin', '*')
        ->header('Allow', 'GET, POST, PUT, DELETE, OPTIONS, HEAD');

Theese are the response headers, which I get when I use postman:

Access-Control-Allow-Headers →Access-Control-, Origin, X-Requested-With, Content-Type, Accept
Access-Control-Allow-Methods →GET, POST, PUT, DELETE, OPTIONS, HEAD
Access-Control-Allow-Origin →

Access-Control-Expose-Headers →Access-Control-*
Allow →GET, POST, PUT, DELETE, OPTIONS, HEAD
Cache-Control →no-cache
Connection →close
Content-Type →text/html; charset=UTF-8
Date →Sat, 03 Dec 2016 10:33:04 GMT
Host →localhost:8000
X-Powered-By →PHP/7.0.13
X-RateLimit-Limit →60
X-RateLimit-Remaining →59
phpdebugbar-id →0ff14bef1824f587d8f278c87ab52544

AXIOS sends preflight which is:

Request URL:http://localhost:8000/api/v1/manager/restaurant/accusamus/payment-methods
Request Method:OPTIONS
Status Code:200 OK
Remote Address:[::1]:8000
Response Headers
view source
Allow:GET,HEAD,POST
Cache-Control:no-cache
Connection:close
Content-Type:text/html; charset=UTF-8
Date:Sat, 03 Dec 2016 10:25:27 GMT
Host:localhost:8000
X-Powered-By:PHP/7.0.13

Request Headers
view source
Accept:/
Accept-Encoding:gzip, deflate, sdch, br
Accept-Language:en-US,en;q=0.8
Access-Control-Request-Headers:content-type
Access-Control-Request-Method:POST
Connection:keep-alive
Host:localhost:8000
Origin:http://localhost:3000
Referer:http://localhost:3000/restaurant-profile/payment
User-Agent:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.98 Safari/537.36

Am I doing something wrong? I can’t figure it how to do this. Witch Chrome plugin CORS everything works fine, but this is not the way.

Please help, help is really appreciated, spent hours with this.

Понравилась статья? Поделить с друзьями:
  • Ошибка ac4bfsp exe что делать
  • Ошибка ac4bfsp exe в assassins creed 4 как исправить
  • Ошибка ac3sp exe как исправить assassins creed 3
  • Ошибка abs шкода октавия тур
  • Ошибка abs форд фокус 2 рестайлинг