Has been blocked by cors policy как исправить

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

Время прочтения
8 мин

Просмотры 12K

В этой статье мы с вами разберемся, что такое CORS, CORS-ошибки и из-за чего мы можем с ними сталкиваться. Я также продемонстрирую возможные решения и объясню, что такое предварительные (preflight) запросы, CORS-заголовки и в чем заключается их важность при обмене данными между сторонами. Эта статья рассчитана на тех, у кого уже есть базовые познания в области веб-разработки и некоторый опыт с протоколом HTTP. Я старался писать статью так, чтобы она была понятна и новичкам, будучи наполненной знаниями, но при этом стараясь избегать слишком большого количества технических нюансов, не связанных с темой CORS. Если вы заметите какие-либо ошибки или у вас будут предложения, не стесняйтесь писать мне. В некоторых местах я нарочно делал упрощения, говоря “служба”, подразумевая “сервер”, и наоборот.

Что такое CORS?

Cross-Origin Resource Sharing (CORS или “совместное использование ресурсов различными источниками”) — это контролируемый и применяемый в принудительном порядке клиентом (браузером) механизм обеспечения безопасности на основе HTTP. Он позволяет службе (API) указывать любой источник (origin), помимо себя, из которого клиент может запрашивать ресурсы. Он был разработан в соответствии с same-origin policy (SOP или “политика одинакового источника”), которая ограничивает взаимодействие сайта (HTML-документа или JS-скрипта), загруженного из одного источника, с ресурсом из другого источника. CORS используется для явного разрешения определенных cross-origin запросов и отклонения всех остальных.

В основном CORS реализуют веб-браузеры, но как вариант его также можно использовать в API-клиентах. Он присутствует во всех популярных браузерах, таких как Google Chrome, Firefox, Opera и Safari. Этот стандарт был принят в качестве рекомендации W3C в январе 2014 года. Исходя из этого, можно смело предполагать, что он реализован во всех доступных в настоящее время браузерах, которые не были перечислены выше.

Как это работает?

Все начинается на стороне клиента, еще до отправки основного запроса. Клиент отправляет в службу с ресурсами предварительный (preflight) CORS-запрос с определенными параметрами в заголовках HTTP (CORS-заголовках, если быть точнее). Служба отвечает, используя те же заголовки с другими или такими же значениями. На основе ответа на предварительный CORS-запрос клиент решает, может ли он отправить основной запрос к службе. Браузер (клиент) выдаст ошибку, если ответ не соответствует требованиям предварительной проверки CORS.

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

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

Что такое предварительная проверка CORS?

Когда браузер отправляет запрос на сервер, он сначала отправляет Options HTTP-запрос. Это и есть предварительным CORS-запрос. Затем сервер отвечает списком разрешенных методов и заголовков. Если браузеру разрешено сделать фактический запрос, то тогда он незамедлительно отправит его. Если нет, он покажет пользователю ошибку и не выполнит основной запрос.

CORS-заголовки

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

  • Access-Control-Allow-Origin указывает, какой источник может получать ресурсы. Вы можете указать один или несколько источников через запятую, например: https://foo.io,http://bar.io.

  • Access-Control-Allow-Methods указывает, какие HTTP-методы разрешены. Вы можете указать один или несколько HTTP-методов через запятую, например: GET,PUT,POST.

  • Access-Control-Allow-Headers указывает, какие заголовки запросов разрешены. Вы можете указать один или несколько заголовков через запятую, например: Authorization,X-My-Token.

  • Access-Control-Allow-Credentials указывает, разрешена ли отправка файлов cookie. По умолчанию: false.

  • Access-Control-Max-Age указывает в секундах, как долго должен кэшироваться результат запроса. По умолчанию: 0.

Если вы решите использовать Access-Control-Allow-Credentials=true, то вам нужно знать, что вы не сможете использовать символы * в заголовках Access-Control-Allow-*. Необходимо будет явно перечислить все разрешенные источники, методы и заголовки.

Полный список CORS-заголовков вы можете найти здесь.

Почему запрос может быть заблокирован политикой CORS?

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

Access to XMLHttpRequest at 'http://localhost:8080/' from origin
'http://localhost:3000' has been blocked by CORS policy:
Response to preflight request doesn't pass access control
check: No 'Access-Control-Allow-Origin' header is present
on the requested resource.

Приведенная выше CORS-ошибка уведомляет пользователя о том, что браузер не может получить доступ к ресурсу (https://localhost:8080) из источника (https://localhost:3000), поскольку сервер его не одобрил. Это произошло из-за того, что сервер не ответил заголовком Access-Control-Allow-Origin с этим источником или символом * в ответе на предварительный CORS-запрос.

Запрос может быть заблокирован политикой CORS не только из-за невалидного источника, но и из-за неразрешенных заголовка HTTP, HTTP-метода или заголовка Cookie.

Как исправить CORS-ошибку?

Фундаментальная идея “исправления CORS” заключается в том, чтобы отвечать на OPTIONS запросы, отправленные от клиента, корректными заголовками. Есть много способов начать отвечать корректными CORS. Вы можете использовать прокси-сервер или какое-нибудь middleware на своем сервере.

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

1. Настройка вашего сервера

По умолчанию, если вы являетесь владельцем сервера, вам необходимо настроить на своем сервере CORS-ответы, и это единственный способ правильно решить проблему. Вы можете добиться этого несколькими способами из нескольких слоев вашего приложения. Самый распространенный способ — использовать обратный прокси-сервер (reverse-proxy), API-шлюз или любой другой сервис маршрутизации, который позволяет добавлять заголовки к ответам. Для этого можно использовать множество сервисов, и вот некоторые из них: HAProxy, Linkerd, Istio, Kong, nginx, Apache, Traefik. Если ваша инфраструктура содержит только приложение без каких-либо дополнительных слоев, то вы можете добавить поддержку CORS в код самого приложения.

Вот несколько популярных примеров активации CORS:

  • Apache: отредактируйте файл .htaccess

  • Nginx: отредактируйте файл конфигурации,

  • Traefik: используйте middleware,

  • Spring Boot: используйте аннотацию @EnableCORS,

  • ExpressJS: используйте app.use(cors()),

  • NextJS: используйте реквест-хелперы.

Здесь вы можете найти больше примеров активации CORS для разных фреймворков и языков: enable-cors.org.

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

2. Установка расширения для браузера

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

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

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

3. Отключение CORS-проверок в браузере

Вы можете полностью отключить CORS-проверки в своем браузере. Чтобы отключить CORS-проверки в Google Chrome, нужно закрыть браузер и запустить его с флагами --disable-web-security и --user-data-dir. После запуска Google Chrome не будет отправлять предварительные CORS-запросы и не будет проверять CORS-заголовки.

# Windows
chrome.exe --user-data-dir="C://chrome-dev-disabled-security" --disable-web-security --disable-site-isolation-trials

# macOS
open /Applications/Google Chrome.app --args --user-data-dir="/var/tmp/chrome-dev-disabled-security" --disable-web-security --disable-site-isolation-trials

# Linux
google-chrome --user-data-dir="~/chrome-dev-disabled-security" --disable-web-security --disable-site-isolation-trials

Все команды, приведенные выше, запускают Google Chrome в изолированной безопасной среде. Они не затронут ваш основной профиль Chrome.

Список всех доступных флагов для Google Chrome можно найти здесь.

4. Настройка прокси-сервера

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

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

Ниже приведен список CORS сервисов с открытым исходным кодом, которые вы можете найти на просторах интернета:

  • https://github.com/Freeboard/thingproxy

  • https://github.com/bulletmark/corsproxy

  • https://github.com/Rob—W/cors-anywhere

Перед использованием любого из этих сервисов обязательно проверьте код самой последний версии версии.

Как протестировать CORS?

Использование браузера для проверки конфигурации CORS может оказаться на удивление утомительной задачей. В качестве альтернативы вы можете использовать такие инструменты, как CORS Tester, test-cors.org или, если вас не страшит командная строка, вы можете использовать curl для проверки конфигурации CORS.

curl -v -X OPTIONS https://simplelocalize.io/api/v1/translations

Остерегайтесь ложных CORS-ошибок

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

  • 401 unauthorized,

  • 403 forbidden,

  • 429 too many requests,

  • 500 internal server error,

  • любые, кроме 2XX или 3XX.

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

Заключение

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

Полезные ссылки

  • https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS/Errors

  • https://developer.mozilla.org/en-US/docs/Web/Security/Same-origin_policy

  • https://enable-cors.org/

  • https://stackoverflow.com/a/42024918/1133169


Материал подготовлен в преддверии старта онлайн-курса «JavaScript Developer. Professional». Недавно прошел открытый урок на тему «CSS-in-JS. Удобный способ управлять стилями», на котором рассмотрели Styled components, Linaria, Astroturf и другие инструменты упрощения работы со стилями. Посмотреть запись можно по ссылке.

Many websites have JavaScript functions that make network requests to a server, such as a REST API. The web pages and APIs are often in different domains. This introduces security issues in that any website can request data from an API. Cross-Origin Resource Sharing (CORS) provides a solution to these issues. It became a W3C recommendation in 2014. It makes it the responsibility of the web browser to prevent unauthorized access to APIs. All modern web browsers enforce CORS. They prevent JavaScript from obtaining data from a server in a domain different than the domain the website was loaded from, unless the REST API server gives permission.

From a developer’s perspective, CORS is often a cause of much grief when it blocks network requests. CORS provides a number of different mechanisms for limiting JavaScript access to APIs. It is often not obvious which mechanism is blocking the request. We are going to build a simple web application that makes REST calls to a server in a different domain. We will deliberately make requests that the browser will block because of CORS policies and then show how to fix the issues. Let’s get started!

NOTE: The code for this project can be found on GitHub.

Table of Contents

  • Prerequisites to Building a Go Application
  • How to Build a Simple Web Front End
  • How to Build a Simple REST API in Go
  • How to Solve a Simple CORS Issue
  • Allowing Access from Any Origin Domain
  • CORS in Flight
  • What Else Does CORS Block?
  • Restrictions on Response Headers
  • Credentials Are a Special Case
  • Control CORS Cache Configuration
  • How to Prevent CORS Issues with Okta
  • How CORS Prevents Security Issues

Prerequisites to Building a Go Application

First things first, if you don’t already have Go installed on your computer you will need to download and install the Go Programming Language.

Now, create a directory where all of our future code will live.

Finally, we will make our directory a Go module and install the Gin package (a Go web framework) to implement a web server.

go mod init cors
go get github.com/gin-gonic/gin
go get github.com/gin-contrib/static

A file called go.mod will get created containing the dependencies.

How to Build a Simple Web Front End

We are going to build a simple HTML and JavaScript front end and serve it from a web server written using Gin.

First of all, create a directory called frontend and create a file called frontend/index.html with the following content:

<html>
    <head>
        <meta charset="UTF-8" />
        <title>Fixing Common Issues with CORS</title>
    </head>
    <body>
        <h1>Fixing Common Issues with CORS</h1>
        <div>
            <textarea id="messages" name="messages" rows="10" cols="50">Messages</textarea><br/>
            <form id="form1">
                <input type="button" value="Get v1" onclick="onGet('v1')"/>
                <input type="button" value="Get v2" onclick="onGet('v2')"/>
            </form>
        </div>
    </body>
</html>

The web page has a text area to display messages and a simple form with two buttons. When a button is clicked it calls the JavaScript function onGet() passing it a version number. The idea being that v1 requests will always fail due to CORS issues, and v2 will fix the issue.

Next, create a file called frontend/control.js containing the following JavaScript:

function onGet(version) {
    const url = "http://localhost:8000/api/" + version + "/messages";
    var headers = {}
    
    fetch(url, {
        method : "GET",
        mode: 'cors',
        headers: headers
    })
    .then((response) => {
        if (!response.ok) {
            throw new Error(response.error)
        }
        return response.json();
    })
    .then(data => {
        document.getElementById('messages').value = data.messages;
    })
    .catch(function(error) {
        document.getElementById('messages').value = error;
    });
}

The onGet() function inserts the version number into the URL and then makes a fetch request to the API server. A successful request will return a list of messages. The messages are displayed in the text area.

Finally, create a file called frontend/.go containing the following Go code:

package main

import (
	"github.com/gin-contrib/static"
	"github.com/gin-gonic/gin"
)

func main() {
	r := gin.Default()
	r.Use(static.Serve("/", static.LocalFile("./frontend", false)))
	r.Run(":8080")
}

This code simply serves the contents of the frontend directory on requests on port 8080. Note that JavaScript makes a call to port http://localhost:8000 which is a separate service.

Start the server and point a web browser at http://localhost:8080 to see the static content.

How to Build a Simple REST API in Go

Create a directory called rest to contain the code for a basic REST API.

NOTE: A separate directory is required as Go doesn’t allow two program entry points in the same directory.

Create a file called rest/server.go containing the following Go code:

package main

import (
	"fmt"
	"strconv"
	"net/http"

	"github.com/gin-gonic/gin"
)

var messages []string

func GetMessages(c *gin.Context) {
	version := c.Param("version")
	fmt.Println("Version", version)
	c.JSON(http.StatusOK, gin.H{"messages": messages})
}

func main() {
	messages = append(messages, "Hello CORS!")
	r := gin.Default()
	r.GET("/api/:version/messages", GetMessages)
	r.Run(":8000")
}

A list called messages is created to hold message objects.

The function GetMessages() is called whenever a GET request is made to the specified URL. It returns a JSON string containing the messages. The URL contains a path parameter which will be v1 or v2. The server listens on port 8000.

The server can be run using:

How to Solve a Simple CORS Issue

We now have two servers—the front-end one on http://localhost:8080, and the REST API server on http://localhost:8000. Even though the two servers have the same hostname, the fact that they are listening on different port numbers puts them in different domains from the CORS perspective. The domain of the web content is referred to as the origin. If the JavaScript fetch request specifies cors a request header will be added identifying the origin.

Origin: http://localhost:8080

Make sure both the frontend and REST servers are running.

Next, point a web browser at http://localhost:8080 to display the web page. We are going to get JavaScript errors, so open your browser’s developer console so that we can see what is going on. In Chrome it is *View** > Developer > Developer Tools.

Next, click on the Send v1 button. You will get a JavaScript error displayed in the console:

Access to fetch at ‘http://localhost:8000/api/v1/messages’ from origin ‘http://localhost:8080’ has been blocked by CORS policy: No ‘Access-Control-Allow-Origin’ header is present on the requested resource. If an opaque response serves your needs, set the request’s mode to ‘no-cors’ to fetch the resource with CORS disabled.

The message says that the browser has blocked the request because of a CORS policy. It suggests two solutions. The second suggestion is to change the mode from cors to no-cors in the JavaScript fetch request. This is not an option as the browser always deletes the response data when in no-cors mode to prevent data from being read by an unauthorized client.

The solution to the issue is for the server to set a response header that allows the browser to make cross-domain requests to it.

Access-Control-Allow-Origin: http://localhost:8080

This tells the web browser that the cross-origin requests are to be allowed for the specified domain. If the domain specified in the response header matches the domain of the web page, specified in the Origin request header, then the browser will not block the response being received by JavaScript.

We are going to set the header when the URL contains v2. Change the GetMessages() function in cors/server.go to the following:

func GetMessages(c *gin.Context) {
	version := c.Param("version")
	fmt.Println("Version", version)
	if version == "v2" {
		c.Header("Access-Control-Allow-Origin", "http://localhost:8080")
	}
	c.JSON(http.StatusOK, gin.H{"messages": messages})
}

This sets a header to allow cross-origin requests for the v2 URI.

Restart the server and go to the web page. If you click on Get v1 you will get blocked by CORS. If you click on Get v2, the request will be allowed.

A response can only have at most one Access-Control-Allow-Origin header. The header can only specify only one domain. If the server needs to allow requests from multiple origin domains, it needs to generate an Access-Control-Allow-Origin response header with the same value as the Origin request header.

Allowing Access from Any Origin Domain

There is an option to prevent CORS from blocking any domain. This is very popular with developers!

Access-Control-Allow-Origin: *

Be careful when using this option. It will get flagged in a security audit. It may also be in violation of an information security policy, which could have serious consequences!

CORS in Flight

Although we have fixed the main CORS issue, there are some limitations. One of the limitations is that only the HTTP GET, and OPTIONS methods are allowed. The GET and OPTIONS methods are read-only and are considered safe as they don’t modify existing content. The POST, PUT, and DELETE methods can add or change existing content. These are considered unsafe. Let’s see what happens when we make a PUT request.

First of all, add a new form to client/index.html:

<form id="form2">
    <input type="text" id="puttext" name="puttext"/>
    <input type="button" value="Put v1" onclick="onPut('v1')"/>
    <input type="button" value="Put v2" onclick="onPut('v2')"/>
</form>

This form has a text input and the two send buttons as before that call a new JavaScript function.

Next, add the JavaScript funtion to client/control.js:

function onPut(version) {
    const url = "http://localhost:8000/api/" + version + "/messages/0";
    var headers = {}

    fetch(url, {
        method : "PUT",
        mode: 'cors',
        headers: headers,
        body: new URLSearchParams(new FormData(document.getElementById("form2"))),
    })
    .then((response) => {
        if (!response.ok) {
            throw new Error(response.error)
        }
        return response.json();
    })
    .then(data => {
        document.getElementById('messages').value = data.messages;
    })
    .catch(function(error) {
        document.getElementById('messages').value = error;
    });
}

This makes a PUT request sending the form parameters in the request body. Note that the URI ends in /0. This means that the request is to create or change the message with the identifier 0.

Next, define a PUT handler in the main() function of rest/server.go:

r.PUT("/api/:version/messages/:id", PutMessage)

The message identifier is extracted as a path parameter.

Finally, add the request handler function to rest/server.go:

func PutMessage(c *gin.Context) {
	version := c.Param("version")
	id, _ := strconv.Atoi(c.Param("id"))
	text := c.PostForm("puttext")
	messages[id] = text
	if version == "v2" {
	    c.Header("Access-Control-Allow-Origin", "http://localhost:8080")
	}
	c.JSON(http.StatusOK, gin.H{"messages": messages})
}

This updates the message from the form data and sends the new list of messages. The function also always sets the allow origin header, as we know that it is required.

Now, restart the servers and load the web page. Make sure that the developer console is open. Add some text to the text input and hit the Send v1 button.

You will see a slightly different CORS error in the console:

Access to fetch at ‘http://localhost:8000/api/v1/messages/0’ from origin ‘http://localhost:8080’ has been blocked by CORS policy: Response to preflight request doesn’t pass access control check: No ‘Access-Control-Allow-Origin’ header is present on the requested resource. If an opaque response serves your needs, set the request’s mode to ‘no-cors’ to fetch the resource with CORS disabled.

This is saying that a preflight check was made, and that it didn’t set the Access-Control-Allow-Origin header.

Now, look at the console output from the API server:

[GIN] 2020/12/01 - 11:10:09 | 404 |  447ns |  ::1 | OPTIONS  "/api/v1/messages/0"

So, what is happening here? JavaScript is trying to make a PUT request. This is not allowed by CORS policy. In the GET example, the browser made the request and blocked the response. In this case, the browser refuses to make the PUT request. Instead, it sent an OPTIONS request to the same URI. It will only send the PUT if the OPTIONS request returns the correct CORS header. This is called a preflight request. As the server doesn’t know what method the OPTIONS preflight request is for, it specifies the method in a request header:

Access-Control-Request-Method: PUT

Let’s fix this by adding a handler for the OPTIONS request that sets the allow origin header in cors/server.go:

func OptionMessage(c *gin.Context) {
	c.Header("Access-Control-Allow-Origin", "http://localhost:8080")
}

func main() {
	messages = append(messages, "Hello CORS!")
	r := gin.Default()
	r.GET("/api/:version/messages", GetMessages)
	r.PUT("/api/:version/messages/:id", PutMessage)
	r.OPTIONS("/api/v2/messages/:id", OptionMessage)
	r.Run(":8000")
}

Notice that the OPTIONS handler is only set for the v2 URI as we don’t want to fix v1.

Now, restart the server and send a message using the Put v2 button. We get yet another CORS error!

Access to fetch at ‘http://localhost:8000/api/v2/messages/0’ from origin ‘http://localhost:8080’ has been blocked by CORS policy: Method PUT is not allowed by Access-Control-Allow-Methods in preflight response.

This is saying that the preflight check needs another header to stop the PUT request from being blocked.

Add the response header to cors/server.go:

func OptionMessage(c *gin.Context) {
	c.Header("Access-Control-Allow-Origin", "http://localhost:8080")
	c.Header("Access-Control-Allow-Methods", "GET, OPTIONS, POST, PUT")
}

Restart the server and resend the message. The CORS issues are resolved.

What Else Does CORS Block?

CORS has a very restrictive policy regarding which HTTP request headers are allowed. It only allows safe listed request headers. These are Accept, Accept-Language, Content-Language, and Content-Type. They can only contain printable characters and some punctuation characters are not allowed. Header values can’t have more than 128 characters.

There are further restrictions on the Content-Type header. It can only be one of application/x-www-form-urlencoded, multipart/form-data, and text/plain. It is interesting to note that application/json is not allowed.

Let’s see what happens if we send a custom request header. Modify the onPut() function in frontend/control.jsto set a header:

var headers = { "X-Token": "abcd1234" }

Now, make sure that the servers are running and load or reload the web page. Type in a message and click the Put v1 button. You will see a CORS error in the developer console.

Access to fetch at ‘http://localhost:8000/api/v2/messages/0’ from origin ‘http://localhost:8080’ has been blocked by CORS policy: Request header field x-token is not allowed by Access-Control-Allow-Headers in preflight response.

Any header which is not CORS safe-listed causes a preflight request. This also contains a request header that specifies the header that needs to be allowed:

Access-Control-Request-Headers: x-token

Note that the header name x-token is specified in lowercase.

The preflight response can allow non-safe-listed headers and can remove restrictions on safe listed headers:

Access-Control-Allow-Headers: X_Token, Content-Type

Next, change the function in cors/server.go to allow the custom header:

func OptionMessage(c *gin.Context) {
	c.Header("Access-Control-Allow-Origin", "http://localhost:8080")
	c.Header("Access-Control-Allow-Methods", "GET, POST, OPTIONS, PUT")
	c.Header("Access-Control-Allow-Headers", "X-Token")
}

Restart the server and resend the message by clicking the Put v2 button. The request should now be successful.

CORS also places restrictions on response headers. There are seven whitelisted response headers: Cache-Control, Content-Language, Content-Length, Content-Type, Expires, Last-Modified, and Pragma. These are the only response headers that can be accessed from JavaScript. Let’s see this in action.

First of all, add a text area to frontend/index.html to display the headers:

<textarea id="headers" name="headers" rows="10" cols="50">Headers</textarea><br/>

Next, replace the first then block in the onPut function in frontend/control.js to display the response headers:

.then((response) => {
    if (!response.ok) {
        throw new Error(response.error)
    }
    response.headers.forEach(function(val, key) {
        document.getElementById('headers').value += 'n' + key + ': ' + val; 
    });
    return response.json();
})

Finally, set a custom header in rest/server.go:

func PutMessage(c *gin.Context) {
	version := c.Param("version")
	id, _ := strconv.Atoi(c.Param("id"))
	text := c.PostForm("puttext")
	messages[id] = text
	if version == "v2" {
	    c.Header("Access-Control-Allow-Origin", "http://localhost:8080")
	}
	c.Header("X-Custom", "123456789")
	c.JSON(http.StatusOK, gin.H{"messages": messages})
}

Now, restart the server and reload the web page. Type in a message and hit Put v2. You should see some headers displayed, but not the custom header. CORS has blocked it.

This can be fixed by setting another response header to expose the custom header in server/server.go:

func PutMessage(c *gin.Context) {
	version := c.Param("version")
	id, _ := strconv.Atoi(c.Param("id"))
	text := c.PostForm("puttext")
	messages[id] = text
	if version == "v2" {
        c.Header("Access-Control-Expose-Headers", "X-Custom")
	    c.Header("Access-Control-Allow-Origin", "http://localhost:8080")
	}
	c.Header("X-Custom", "123456789")
	c.JSON(http.StatusOK, gin.H{"messages": messages})
}

Restart the server and reload the web page. Type in a message and hit Put v2. You should see some headers displayed, including the custom header. CORS has now allowed it.

Credentials Are a Special Case

There is yet another CORS blocking scenario. JavaScript has a credentials request mode. This determines how user credentials, such as cookies are handled. The options are:

  • omit: Never send or receive cookies.
  • same-origin: This is the default, that allows user credentials to be sent to the same origin.
  • include: Send user credentials even if cross-origin.

Let’s see what happens.

Modify the fetch call in the onPut() function in frontend/Control.js:

fetch(url, {
    method : "PUT",
    mode: 'cors',
    credentials: 'include',
    headers: headers,
    body: new URLSearchParams(new FormData(document.getElementById("form2"))),
})

Now, make sure that the client and server are running and reload the web page. Send a message as before. You will get another CORS error in the developer console:

Access to fetch at ‘http://localhost:8000/api/v2/messages/0’ from origin ‘http://localhost:8080’ has been blocked by CORS policy: Response to preflight request doesn’t pass access control check: The value of the ‘Access-Control-Allow-Credentials’ header in the response is ‘’ which must be ‘true’ when the request’s credentials mode is ‘include’.

The fix for this is to add another header to both the OptionMessage(), and PutMessage() functions in cors/server.go as the header needs to be present in both the preflight request and the actual request:

c.Header("Access-Control-Allow-Credentials", "true")

The request should now work correctly.

The credentials issue can also be resolved on the client by setting the credentials mode to omit and sending credentials as request parameters, instead of using cookies.

Control CORS Cache Configuration

The is one more CORS header that hasn’t yet been discussed. The browser can cache the preflight request results. The Access-Control-Max-Age header specifies the maximum time, in seconds, the results can be cached. It should be added to the preflight response headers as it controls how long the Access-Control-Allow-Methods and Access-Control-Allow-Headers results can be cached.

Access-Control-Max-Age: 600

Browsers typically have a cap on the maximum time. Setting the time to -1 prevents caching and forces a preflight check on all calls that require it.

How to Prevent CORS Issues with Okta

Authentication providers, such as Okta, need to handle cross-domain requests to their authentication servers and API servers. This is done by providing a list of trusted origins. See Okta Enable CORS for more information.

How CORS Prevents Security Issues

CORS is an important security feature that is designed to prevent JavaScript clients from accessing data from other domains without authorization.

Modern web browsers implement CORS to block cross-domain JavaScript requests by default. The server needs to authorize cross-domain requests by setting the correct response headers.

CORS has several layers. Simply allowing an origin domain only works for a subset of request methods and request headers. Some request methods and headers force a preflight request as a further means of protecting data. The actual request is only allowed if the preflight response headers permit it.

CORS is a major pain point for developers. If a request is blocked, it is not easy to understand why, and how to fix it. An understanding of the nature of the blocked request, and of the CORS mechanisms, can easily overcome such issues.

If you enjoyed this post, you might like related ones on this blog:

  • What is the OAuth 2.0 Implicit Grant Type?
  • Combat Side-Channel Attacks with Cross-Origin Read Blocking

Follow us for more great content and updates from our team! You can find us on Twitter, Facebook, subscribe to our YouTube Channel or start the conversation below.

Cross-Origin Resource Sharing (CORS) is a mechanism based on HTTP headers that allows browsers to identify which request comes from allowed domain list, at the same time filter out unknown requests. Browsers make a “preflight” request to the server hosting the cross-origin resource in order to check that the server will permit the actual request. In that preflight, the browser sends information about the HTTP method and headers that will be used later in the actual request.

Fetching data from sites and APIs that enforced CORS policy without proper headers can lead to “Access to XMLHttpRequest has been blocked by CORS policy” error message in Chrome DevTools. The error comes in several forms, include useful information for debugging.

This article is going to show you how to fix “Access to XMLHttpRequest has been blocked by CORS policy” in various scenarios.

Access to XMLHttpRequest has been blocked by CORS policy in the wild

Just as what Chrome DevTools says, “No ‘Access-Control-Allow-Origin’ header is present on the requested response” error means the response does not have the proper Access-Control-Allow-Origin header in place.

In this case, you have a few options :

  • Use a Chrome extension to add Access-Control-Allow-Origin header into every response.

    To find one of them, just head over to Chrome Webstore and type in “CORS”, dozens will show up in the search result. Or you can install CORS Helper, CORS Unblock or dyna CORS right away.

  • Redirect your request through a CORS proxy.

    You can easily spin up your own proxy using open source code from https://github.com/Rob–W/cors-anywhere/. Deploying to Heroku takes only 2-3 minutes, or you can use the original developers demo for quick, one-time usage : https://cors-anywhere.herokuapp.com/.

    To use the proxy, you have to prefix your request URL with the proxy URL so that it would look like this : https://cors-anywhere.herokuapp.com/https://example.com. The proxy will then forward your request to the original server, then grabs the response, adds Access-Control-Allow-Origin header to the response before pass it to you.

Access to XMLHttpRequest has been blocked by CORS policy : Response to preflight request doesn’t pass access control check

“Response to preflight request doesn’t pass access control check” can comes with additional message about the actual error. If you encounter one of these, you may consider my recommended options in the next section.

The Access-Control-Allow-Origin header doesn’t allow for more than one origin to be specified by design. If you are a fellow web developer, my advice is to carefully review your code that involves setting up CORS headers. You may set the Access-Control-Allow-Origin twice, or adds too many values to it instead of replacing the value. Most of the time, the error is related to the source code.

If an opaque response serves your needs, set the request’s mode to ‘no-cors’ to fetch the resource with CORS disabled

An opaque response is for a request made for a resource on a different origin that doesn’t return CORS headers. With an opaque response we won’t be able to read the data returned or view the status of the request, meaning we can’t check if the request was successful or not.

In this situation, you need to modify your code so that the request to the different origin does not contain CORS headers. In JavaScript, the behaviour can be achieved by passing {mode: 'no-cors'} in fetch:

fetch('http://some-site.com/cors-disabled/some.json', {mode: 'no-cors'})

Code language: JavaScript (javascript)

Opaque responses can’t be accessed by JavaScript, but you can still cache them with the Cache API and respond with them in the fetch event handler in a service worker. So they’re useful for resources that you can’t control (e.g. resources on a CORS-less CDN) but still want to cache.

For a request that includes credentials, browsers won’t let your front-end JavaScript code access the response if the value of the Access-Control-Allow-Origin response header is set to *. Instead, the value of the header must exactly match your front-end origin, e.g http://where_you_send_request.com.

If you are using any “Easy CORS” Chrome extension like Allow CORS: Access-Control-Allow-Origin or CORS Unblock, disable it and the problem should disappear.

If you have access to the server, you can configure the server to grab the value of the Origin header the client sends, then echo it back to Access-Control-Allow-Origin response header. In nginx configuration language, the setting can be set by placing the line below into the config file.

add_header Access-Control-Allow-Origin $http_origin

Code language: PHP (php)

You can find more details at Credentialed requests and wildcards in the MDN HTTP access control (CORS) article.

В локальной сети между машинами, а также на одной машине в режиме отладки, делаю POST запросы XMLHttpRequest для выгрузки файла на второй сервер.

Со страницы https://xxx
(, где ключи и сертификат самоподписанные (уровня 1). Тестовый сервер Nginx, в продакшене будет другой, но там такие же ошибки.)

на сервер https://upserver:8888
(, где ключи и сертификаты самоподписанные (уровня 2, с использованием собственного центра сертификации, корневых, промежуточных и конечных сертификатов)
На порту 8888 работает Cowboy (Erlang/OTP v.23). Код с заголовками показан ниже.) Пока, там нет авторизации, а кросс-доменные запросы разрешены для всех (*).

Оба домена прописаны в hosts. В продакшене будут прописаны в DNS. Разрешения CORS установлены на обоих серверах, код показан ниже.

При первом открытии index.html на обоих серверах в браузере Chrome получаю ошибку сертификата, но после разрешения ресурса, страница открывается и далее работает. Хотелось бы чтобы она открывалась без этой ошибки и необходимости разрешения.

При загрузке файла на второй сервер в логе браузера (вкладка console) получаю следующие ошибки

Access to XMLHttpRequest at 'https://upserver:8888/upload' from origin 'https://xxx' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

upload.js:73 onreadystatechange Error 0 occurred when trying to upload your file.

upload.js:78 POST https://upserver:8888/upload net::ERR_FAILED
fileUpload @ upload.js:78
(anonymous) @ upload.js:103

upload.js:78 XHR failed loading: POST "https://upserver:8888/upload".
fileUpload @ upload.js:78
(anonymous) @ upload.js:103

Но файл успешно загружается на сервер.
Не могу понять, что ему еще надо, и как исправить эту CORS ошибку?

Код заголовков на upserver:8888

options(Req, Opts) ->
    Req1 = cowboy_req:set_resp_header(<<"access-control-allow-methods">>, <<"HEAD, OPTIONS, GET, PUT, POST">>, Req),
    Req2 = cowboy_req:set_resp_header(<<"access-control-allow-headers">>, <<"Origin, X-Requested-With, Content-Type, 
    Accept, x-client-key, x-client-token, x-client-secret">>, Req1),
    Req3 = cowboy_req:set_resp_header(<<"access-control-allow-origin">>, <<$*>>, Req2),
    {ok, Req3, Opts}.

init(Req, Opts) ->
    case cowboy_req:method(Req) of
        <<"POST">> ->
	   {ok, Req1, Opts1} = options(Req, Opts),
           {ok, Headers, Req2} = cowboy_req:read_part(Req1),
    	   {ok, Data, Req3} = cowboy_req:read_part_body(Req2),
    	   {file, <<"upfile">>, Filename, ContentType} = cow_multipart:form_data(Headers),
    	   io:format("Received file ~p of content-type ~p as follow:~n~p~n~n", [Filename, ContentType, Data]),
    	   Filepath = filename:join([code:priv_dir("upserver"), "upload", Filename]),
    	   file:write_file(Filepath, Data),
    	   {ok, Req3, Opts1};
        _ ->
    	   io:format("Method=~p~n",[cowboy_req:method(Req)]),
	   options(Req, Opts)
    end.

Код запроса на клиенте

function fileUpload(blob){
    var filename="test.png";
    var file=new File([blob],filename,{type:"image/png"});
    var formData = new FormData();
    formData.append('upfile', file, filename);
    const xhr = new XMLHttpRequest();
    xhr.fileinfo = {'filename': filename};
    xhr.enctype = "multipart/form-data";
    xhr.overrideMimeType('multipart/form-data');

// эта функция работает без ошибок и сообщений
    xhr.open("POST", "https://upserver:8888/upload", true);

// Это не помогло, бесполезно, поэтому закоментировано
// xhr.setRequestHeader("Access-Control-Allow-Origin","https://upserver:8888");
// xhr.setRequestHeader("Access-Control-Request-Header","X-Requested-With");


// Это сообщение показано в логе, смотрите лог, Error = xhr.status = 0, вместо 200
    xhr.onreadystatechange = function() {
	if (xhr.status == 200) {
	    console.log("onreadystatechange Uploaded!");
	} else {
	    console.log("onreadystatechange Error " + xhr.status + " occurred when trying to upload your file.");
	}
    };

// здесь выдается сообщение об ошибке CORS
    xhr.send(formData);

// В результате, formData успешно передается и правильно сохраняется на сервере, несмотря на все эти ошибки!

}

На стороне первого сервера https://xxx в конфигурации сайта под Nginx прописана следующая конфигурация

server {
        listen 443 ssl;
        listen [::]:443 ssl;
configuration.
        root /var/www/https;
        index index.html index.htm index.nginx-debian.html;

        server_name xxx;
        ssl_certificate /etc/nginx/xxx-list.crt;
        ssl_certificate_key /etc/nginx/xxx.key;
location / {
     if ($request_method = 'OPTIONS') {
        add_header 'Access-Control-Allow-Origin' 'https://yyy:8888';
        add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
        add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type';

        add_header 'Access-Control-Max-Age' 1728000;
        add_header 'Content-Type' 'text/plain charset=UTF-8';
        add_header 'Content-Length' 0;
        return 204;
     }
     if ($request_method = 'POST') {
        add_header 'Access-Control-Allow-Origin' 'https://upserver:8888';
#        add_header 'Access-Control-Allow-Credentials' 'true';
        add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
        add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type';
     }
     if ($request_method = 'GET') {
        add_header 'Access-Control-Allow-Origin' '*';
#        add_header 'Access-Control-Allow-Credentials' 'true';
        add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
        add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type';
     }
     try_files $uri $uri/ =404;
}

Видно, что кросс-доменные запросы разрешены для второго сервера upserver:8888

Вроде бы, везде всё настроено белее менее правильно, но ошибки мне очень не нравятся.
Хотелось бы разобраться, что не так, и избавиться от ошибок.

Background

UPDATE 1 August 2020

This article was written when Laravel 6 was out and before first class CORS support was built into Laravel 7. Due to the integrated nature of CORS to an application we generally recommend you rather follow the official documentation when debugging Laravel issues with CORS. If you have’t upgraded to Laravel 7 yet, you are going to rapidly fall behind so we seriously recommend you do that. We have much success with Laravel Shift, but sometimes it a good learning exercise just to do it yourself.

Original Article

This article was compiled during a Laravel Development troubleshooting session and might not apply to other CORS sessions.

You might find yourself in a situation where you are trying to post from a REMOTE host to a LOCALHOST, especially during testing.

HTTPS should be set up properly. But instead of the post coming in, you get the following:

Access to XMLHttpRequest at 'https://sitename.test/api/v1/endpoint' from origin 'https://yourdomain.com' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.

This is a challenging problem to Google due to conflicting information on the internet. Here are a few options:

Option 1 (challenging)

Don’t do the Axois request through your browser. Do a server API and then use a CURL / Guzzlehttp request.

Option 2 (how to troubleshoot)

This error can come from many locations. Most notably you have to establish if you are having this problem because the server is blocking you, or if you can simply do something on the client to avoid it. If you don’t have control over the server then you will have limited options. So in this troubleshooting, try to determine if the server or the client is causing the problem.

If you are using Laravel, and you have server control, then the solution might be the Laravel CORS library by Barry vd Heuvel:

https://github.com/barryvdh/laravel-cors

Here are brief instructions for installing this package

composer require barryvdh/laravel-cors

Then:

protected $middleware = [
    // ...
    BarryvdhCorsHandleCors::class,
];

Then:

$ php artisan vendor:publish --provider="BarryvdhCorsServiceProvider"

Make sure your Laravel app is setup with Passport. Here is a summary of most of the steps.:

composer require laravel/passport
php artisan migrate
php artisan passport:install

Add HasApiTokens to the user model:

class User extends Authenticatable
{
    use HasApiTokens, Notifiable;
}

In boot method of AuthServiceProvider add the Password::routes(); line as below:

class AuthServiceProvider extends ServiceProvider
{
    /**
     * The policy mappings for the application.
     *
     * @var array
     */
    protected $policies = [
        'AppModel' => 'AppPoliciesModelPolicy',
    ];

    /**
     * Register any authentication / authorization services.
     *
     * @return void
     */
    public function boot()
    {
        $this->registerPolicies();

        Passport::routes();
    }
}

Update the guards in config/auth.php the ‘api’  one make the driver ‘passport’.

'guards' => [
    'web' => [
        'driver' => 'session',
        'provider' => 'users',
    ],

    'api' => [
        'driver' => 'passport',
        'provider' => 'users',
    ],
],

If you’ve done all of the above, and you’re consuming your own API via your front-end, you’re in good stead because all you have to do now is:

https://laravel.com/docs/5.7/passport#consuming-your-api-with-javascript

'web' => [
    // Other middleware...
    LaravelPassportHttpMiddlewareCreateFreshApiToken::class,
],

Check the docs if it’s still not working 🙂

When not Consuming Own API

If you’re not consuming your own API and coming from another URL, e.g. a remote URL to your local testing URL.

You have to continue the Laravel guide, including getting those Vue components and tokens working.

php artisan vendor:publish tag=passportmigrations

php artisan vendor:publish --tag=passport-components

resources/js/app/js

Vue.component(
    'passport-clients',
    require('./components/passport/Clients.vue').default
);

Vue.component(
    'passport-authorized-clients',
    require('./components/passport/AuthorizedClients.vue').default
);

Vue.component(
    'passport-personal-access-tokens',
    require('./components/passport/PersonalAccessTokens.vue').default
);

npm run dev 

Then create a new page and add these components:

<passport-clients></passport-clients>
<passport-authorized-clients></passport-authorized-clients>
<passport-personal-access-tokens></passport-personal-access-tokens>

Then login, and create a user and a token. Below is a code sample but please note we have removed the very long bearer token and substituted it with very_long_bearer_token.

In your code:

axios.defaults.headers.common['Authorization'] = "Bearer " + 'very_long_bearer_token';

axios.post('https://project.test/api/v1/subscribe', {
   name: this.name,
   email: this.email
})
.then(function (response) {
   page.output = response.data;
})

.catch(function (error) {
   page.output = error;
});

}

If you’ve made it this far, congratulations! You’ve just digested a lot of information and I hope this will assist you in fixing the CORS issue. Please leave a comment or get in touch if you need additional help.

Introduction

Access to fetch at ‘https://www.yourapi.com’ has been blocked by CORS policy: Response to preflight request doesn’t pass access control check: No ‘Access-Control-Allow-Origin’ header is present on the requested resource. If an opaque response serves your needs, set the request’s mode to ‘no-cors’ to fetch the resource with CORS disabled.

If you have ever built a web app that wants to interact with a REST API, you might be familiar with this error. If you also built the API, you might have wondered: why did it work with Postman when you were testing your API, but not in the browser? There must be something wrong with my web app. However, this is probably not the case.

This article will give you an overall idea of what CORS is, and how to ‘fix‘ it.

What is CORS

CORS stands for Cross-Origin Resource Sharing. It’s a mechanism that restricts requests coming from a different origin (domain). A request coming from a different origin is known as a cross-origin request. Cross-origin requests are vital for when your site needs to load data from other services.

CORS allows servers to specify who can access their resources and how. Browsers follow the servers’ policies by sending a test request (preflight) to the server and checking whether it’s allowed. I’ll go into a bit more detail in the following sections.

Why CORS

The rationale behind it is to allow the server (API) on one origin to restrict behaviour for other origins. For example, to allow one origin to read and write data, but other origins only to read data. As a result, requests like GET are usually allowed by default. However, PUT, DELETE and sometimes POST would be restricted.

How does CORS work

When the browser is about to send a request that will trigger CORS to a different origin, e.g. a PUT request, it will not send the actual request immediately. Instead, it will send what’s called a preflight request, which serves as a test as to whether the server will allow the communication. If the server responds with a successful status, the browser will then send the actual request. Otherwise, it will reject the request, returning a 405 status code and the error message shown at the start of this post.

More about CORS

So far, I’ve provided a very brief explanation of CORS. For those who wants to dig deeper, you can find a more thorough explanation of CORS on the MDN: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS.

Another good read would be What is CORS?

How to make it work

The CORS issue can be very annoying, especially for learners. This article will sum up the solutions which may save your hours of searching on StackOverflow. The solution depends on your particular scenario: more specifically, on how much control you have over the server. We will go from the best case scenario to the worst case.

Scenario 1: full control over the server

If you are the one that deploys the API onto your own server, then you are in the best case scenario. All you need to do is to configure your server, assuming that you’re using Nginx. If you’re not, you can still check out the following scenarios which may also fix this issue.

Here’s a code snippet from https://enable-cors.org/server_nginx.html which shows how to configure a wide-open CORS policy.

if ($request_method = 'OPTIONS') {
  add_header 'Access-Control-Allow-Origin' '*';
  add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';

  # Custom headers and headers various browsers *should* be OK with but aren't
  add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';

  # Tell client that this pre-flight info is valid for 20 days
  add_header 'Access-Control-Max-Age' 1728000;
  add_header 'Content-Type' 'text/plain; charset=utf-8';
  add_header 'Content-Length' 0;
  return 204;
}
if ($request_method = 'POST') {
  add_header 'Access-Control-Allow-Origin' '*';
  add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
  add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
  add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range';
}

You can tailor it to your needs. For example, instead of using a wildcard for all origins, you can permit only your web app domain. You can also choose to only allow specific methods and headers to go through.

This solution allows you to configure CORS without tampering with your API code, which is ideal in most cases. If this is not want you want, or you do not have control over the server, you may find your solution in the next scenario. For example if you deployed your API via Heroku.

Scenario 2: my API but not my server

In this scenario, you fully deployed your API, but you might be using a hosting service like Heroku so you do not have control over the server configuration. This section guides you through adding the necessary header in your backend code.

There is no universal server-side language or framework, but we can at least show a solution using one very popular option.

Here I’m going to use a Flask app for demonstration, which is a very popular Python framework (sorry Django lovers!).

app = Flask(__name__)

@app.after_request
def after_request(response):
    response.headers.add('Access-Control-Allow-Origin', '*')
    response.headers.add('Access-Control-Allow-Headers', 'Content-Type')
    return response

The above code block shows how to add the additional header after the request has been handled and before the response is sent. The same applies here as with Nginx: you can tailor the solution to be less open, and only allow certain origins, methods, or headers.

Scenario 3: not my API and not my server

Front-end developers might be in the ‘worst’ scenario here. However, it’s not your fault and it’s quite common situation to find yourself in. You might just want to interact with a third party API and it just keeps complaining about CORS. What can we do about this?

The ideal solution would be inform the API owner about this issue, since technically this is their fault. However, you might not get an immediate response. Since they found no issue with using it, it’s your own misconfiguration. Please refer them to this post if this is your situation. However, in the meantime you could try the following solution.

You can build a proxy API that serves as a bridge to the third party API. It sounds a bit hacky but it might be the only working solution you available if you wish your web app to work. The proxy API would receive the requests from your web app and send them to the third party API. When the third party API responds, it would just forward the response to your web app. Since the requests to the third party API don’t go through the browser, it circumvents the CORS protection. You can then configure your own proxy API as in the previous two scenarios.

The last solution would be using browser plugins to inject the needed headers into the response. This is not recommended since it’s only ‘fooling yourself’. Browser plugins would not work on other computers (such as your users’) unless they have the plugins too. However, it is still useful if you’re testing things around locally and would like to see some results ASAP. There are usually plenty of such plugins for each browser. One of them is the Allow-Control-Allow-Origin extension for Chrome.

There are also tutorials on how to turn off the CORS check on browsers, but we won’t not cover that here since it’s not scalable either.

Conclusion

This post provided a very high-level view on the CORS policy. There’re a lot more to learn, which can be found in the More bout CORS section at the top. I’ve tailored the solutions to different scenarios to help you find the solution that suits your needs. I hope you find this article helpful, since I’ve personally encountered these scenarios and wished for an article like this one. Cheers!

Понравилась статья? Поделить с друзьями:

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

  • Harvia ошибка error 3
  • Heater error код ошибки на скания автономка
  • Harvia ошибка er 3 устранение
  • Heater error codes
  • Harvia c150 ошибка er2

  • 0 0 голоса
    Рейтинг статьи
    Подписаться
    Уведомить о
    guest

    0 комментариев
    Старые
    Новые Популярные
    Межтекстовые Отзывы
    Посмотреть все комментарии