The response to the CORS request is missing the required
Access-Control-Allow-Origin
header, which is used to determine whether
or not the resource can be accessed by content operating within the current origin.
If the server is under your control, add the origin of the requesting site to the set
of domains permitted access by adding it to the Access-Control-Allow-Origin
header’s value.
For example, to allow a site at https://amazing.site
to access the resource using CORS,
the header should be:
Access-Control-Allow-Origin: https://amazing.site
You can also configure a site to allow any site to access it by using the
*
wildcard. You should only use this for public APIs. Private APIs should
never use *
, and should instead have a specific domain or domains set. In
addition, the wildcard only works for requests made with the
crossorigin
attribute set to anonymous
, and it prevents
sending credentials like cookies in requests.
Access-Control-Allow-Origin: *
Warning: Using the wildcard to allow all sites to access a private
API is a bad idea.
To allow any site to make CORS requests without using the *
wildcard (for example, to enable credentials), your server must read the value of the
request’s Origin
header and use that value to set
Access-Control-Allow-Origin
, and must also set a Vary: Origin
header to indicate that some headers are being set dynamically depending on the origin.
The exact directive for setting headers depends on your web server. In Apache, add a
line such as the following to the server’s configuration (within the appropriate
<Directory>
, <Location>
,
<Files>
, or <VirtualHost>
section). The
configuration is typically found in a .conf
file (httpd.conf
and apache.conf
are common names for these), or in an
.htaccess
file.
Header set Access-Control-Allow-Origin 'origin-list'
For Nginx, the command to set up this header is:
add_header 'Access-Control-Allow-Origin' 'origin-list';
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.
В ходе использования программного интерфейса приложения (API) XMLHttpRequest скриптового языка браузеров JavaScript, обычно в тандеме с технологией AJAX (Asynchronous JavaScript And XML), могут возникать различные ошибки CORS (Cross-origin resource sharing) при попытке доступа к ресурсам другого домена.
Здесь мы условились о том, что:
- src.example.org — это домен, с которого отправляются XMLHttpRequest кросс-доменные запросы
- target.example.com/example.php — это домен и скрипт /example.php, на который XMLHttpRequest кросс-доменные запросы отправляются
Перечисленные ниже CORS ошибки приводятся в том виде, в котором они выдавались в консоль веб-браузера.
https://target.example.com/example.php в данном примере фактически был размещён на сервере страны отличной от Украины и посредством CURL предоставлял доступ к российскому сервису проверки правописания от Яндекса, — как извесно доступ к сервисам Яндекса из Украины был заблокирован большинством Интернет-провайдеров.
При попытке проверить правописание в редакторе выдавалась ошибка: «The spelling service was not found: (https://target.example.com/example.php)«
Причина: отсутствует заголовок CORS ‘Access-Control-Allow-Origin’
«NetworkError: 403 Forbidden — https://target.example.com/example.php»
example.php
Запрос из постороннего источника заблокирован: Политика одного источника запрещает чтение удаленного ресурса на https://target.example.com/example.php. (Причина: отсутствует заголовок CORS ‘Access-Control-Allow-Origin’).
Запрос из постороннего источника заблокирован: Политика одного источника запрещает чтение удаленного ресурса на https://target.example.com/example.php. (Причина: не удалось выполнить запрос CORS).
Решение: отсутствует заголовок CORS ‘Access-Control-Allow-Origin’
CORS для src.example.org должно быть разрешено на стороне сервера принимающего запросы — это можно сделать в .htaccess следующим образом:
<Files ~ "(example)+.php"> Header set Access-Control-Allow-Origin "https://src.example.org" </Files>
Причина: неудача канала CORS preflight
«NetworkError: 403 Forbidden — https://target.example.com/example.php» example.php
Запрос из постороннего источника заблокирован: Политика одного источника запрещает чтение удаленного ресурса на https://target.example.com/example.php. (Причина: неудача канала CORS preflight).
Запрос из постороннего источника заблокирован: Политика одного источника запрещает чтение удаленного ресурса на https://target.example.com/example.php. (Причина: не удалось выполнить запрос CORS).
Решение: неудача канала CORS preflight
Причины здесь могут быть разные, среди которых может быть запрет некоторых ИП на стороне брандмауэра, либо ограничения на методы запроса в том же .htaccess строкой: «RewriteCond %{REQUEST_METHOD} !^(post|get) [NC,OR]
«.
Во-втором случае это может быть зафиксировано в лог-файле ошибок сервера:
xxx.xxx.xx.xxx [07/May/2018:09:55:15 +0300] «OPTIONS /example.php HTTP/1.1» 403 «Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:52.0) Gecko/20100101 Firefox/52.0» «Referer: -«
Примечание:
Нужно помнить, что ИП-адрес в лог-файле сервера принадлежит клиенту/браузеру, а не удалённому серверу src.example.org на страницах которого в браузере клиента был инициирован CORS (кросс-доменный) запрос!
В случае с .htaccess
строку подправим до такого состояния: «RewriteCond %{REQUEST_METHOD} !^(post|get|options) [NC,OR]
«.
Причина: отсутствует токен ‘x-requested-with’ в заголовке CORS ‘Access-Control-Allow-Headers’ из канала CORS preflight
Запрос из постороннего источника заблокирован: Политика одного источника запрещает чтение удаленного ресурса на https://target.example.com/example.php. (Причина: отсутствует токен ‘x-requested-with’ в заголовке CORS ‘Access-Control-Allow-Headers‘ из канала CORS preflight).
Запрос из постороннего источника заблокирован: Политика одного источника запрещает чтение удаленного ресурса на https://target.example.com/example.php. (Причина: не удалось выполнить запрос CORS).
Решение: отсутствует токен ‘x-requested-with’ в заголовке CORS ‘Access-Control-Allow-Headers’ из канала CORS preflight
Посредством .htaccess
добавим заголовок Access-Control-Allow-Headers:
<Files ~ "(example)+.php"> Header set Access-Control-Allow-Origin "https://src.example.org" Header set Access-Control-Allow-Headers: "X-Requested-With" </Files>
Access-Control-Allow-Origin для множества доменов
Заголовок Access-Control-Allow-Origin допускает установку только одного источника, однако при необходимости разрешения CORS для множества источников в .htaccess можно извратится следующим образом:
##### ---+++ BEGIN MOD HEADERS CONFIG +++--- <IfModule mod_headers.c> <Files ~ "(example)+.php"> #Header set crossDomain: true # ## Allow CORS from one domain #Header set Access-Control-Allow-Origin "https://src.example.org" ## Allow CORS from multiple domain SetEnvIf Origin "http(s)?://(www.)?(example.org|src.example.org)$" AccessControlAllowOrigin=$0 Header set Access-Control-Allow-Origin %{AccessControlAllowOrigin}e env=AccessControlAllowOrigin # ## Origin, X-Requested-With, Content-Type, Accept, X-Auth-Token Header set Access-Control-Allow-Headers: "X-Requested-With" # ## "GET, POST" Header set Access-Control-Allow-Methods "POST" # #Header set Access-Control-Allow-Credentials true #Header set Access-Control-Max-Age 60 </Files> </IfModule> ##### ---+++// END MOD HEADERS CONFIG +++---
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.js
to 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.
Last updated: 2022-06-16
I’m getting the CORS error «No ‘Access-Control-Allow-Origin'» on my requested resource in Amazon CloudFront. Why am I getting this and how can I resolve it?
Resolution
Note: If you receive errors when running AWS Command Line Interface (AWS CLI) commands, make sure that you’re using the most recent AWS CLI version.
Run the following command to confirm the origin server returns the Access-Control-Allow-Origin header. Replace example.com with the required origin header. Replace https://www.example.net/video/call/System.generateId.dwr with the URL of the resource that’s returning the header error.
curl -H "Origin: example.com" -v "https://www.example.net/video/call/System.generateId.dwr"
If the CORS policy allows the origin server to return the Access-Control-Allow-Origin header, you see a response similar to the following:
HTTP/1.1 200 OK
Server: nginx/1.10.2
Date: Mon, 01 May 2018 03:06:41 GMT
Content-Type: text/html
Content-Length: 3770
Last-Modified: Thu, 16 Mar 2017 01:50:52 GMT
Connection: keep-alive
ETag: "58c9ef7c-eba"
Access-Control-Allow-Origin:
example.com
Accept-Ranges: bytes
After you set up a CORS policy on your origin server, configure your CloudFront distribution to forward the origin headers to the origin server. If your origin server is an Amazon S3 bucket, then configure your distribution to forward the following headers to Amazon S3:
- Access-Control-Request-Headers
- Access-Control-Request-Method
- Origin
To forward the headers to the origin server, CloudFront has two pre-defined policies depending on your origin type: CORS-S3Origin and CORS-CustomOrigin.
To add a pre-defined policy to your distribution:
- Open your distribution from the CloudFront console.
- Choose the Behaviors tab.
- Choose Create Behavior. Or, select an existing behavior, and then choose Edit.
- Under Cache key and origin requests, choose Cache policy and origin request policy. Then, for Origin request policy, choose CORS-S3Origin or CORS-CustomOrigin from the dropdown list. For more information, see Using the managed origin request policies.
Note: To create your own cache policy instead, see Creating cache policies. - Choose Create Behavior. Or, choose Save changes if you’re editing an existing behavior.
To forward the headers using a cache policy:
- Create a cache policy.
- Under Cache key settings, for Headers, select Include the following headers. From the Add header dropdown list, choose one of the headers required by your origin.
- Fill in the cache policy settings as required by the behavior that you’re attaching the policy to.
- Attach the cache policy to the behavior of your CloudFront distribution.
To forward the headers using legacy cache settings:
- Open your distribution from the CloudFront console.
- Choose the Behaviors tab.
- Choose Create Behavior. Or, select an existing behavior, and then choose Edit.
- Under Cache key and origin requests, select Legacy cache settings.
- In the Headers dropdown list, choose the headers required by your origin. Choose Add custom to add headers required by your origin that don’t appear in dropdown list.
- Choose Create Behavior. Or, choose Save changes if you’re editing an existing behavior.
Note: Be sure also to forward the header as part of your client request to CloudFront, which CloudFront forwards to the origin.
Configure the CloudFront distribution’s cache behavior to allow the OPTIONS method for HTTP requests
If you still see errors after updating your CORS policy and forwarding the appropriate headers, allow the OPTIONS HTTP method in your distribution’s cache behavior. By default, CloudFront only allows the GET and HEAD methods. However, some web browsers can issue requests for the OPTIONS method. To turn on the OPTIONS method on your CloudFront distribution:
- Open your distribution from the CloudFront console.
- Choose the Behaviors tab.
- Choose Create Behavior. Or, select an existing behavior, and then choose Edit.
- For Allowed HTTP Methods, select GET, HEAD, OPTIONS.
- Choose Create Behavior. Or, choose Save changes if you’re editing an existing behavior.
If the origin server isn’t accessible or can’t be set up to return the appropriate CORS headers, configure a CloudFront to return the required CORS headers. To configure, create response headers policies:
- Open your distribution from the CloudFront console.
- Choose the Behaviors tab.
- Choose Create Behavior. Or, select an existing behavior, and then choose Edit.
- For Response headers policy:
Select an existing response policy from the dropdown list.
-or-
Choose Create policy to create a new response headers policy . In the new policy, under Cross-origin resource sharing, turn on CORS. - Fill in other settings as needed and choose Create policy.
- From the Create Behavior page, choose the policy you created from the dropdown list.
- Choose Create Behavior. Or, choose Save changes if you’re editing an existing behavior.
Note: CloudFront typically deploys changes to distributions within five minutes. After you edit your distribution, invalidate the cache to clear previously cached responses.
Did this article help?
Do you need billing or technical support?
AWS support for Internet Explorer ends on 07/31/2022. Supported browsers are Chrome, Firefox, Edge, and Safari.
Learn more »
Errors: CORSMissingAllowOrigin
Причина:CORS-заголовок ‘Access-Control-Allow-Origin’ отсутствует
Reason
Что пошло не так?
В ответе на запрос CORS отсутствует требуемый заголовок Access-Control-Allow-Origin
, который используется для определения того, доступен ли ресурс для контента, работающего в текущем источнике.
Если сервер находится под вашим контролем, добавьте источник запрашивающего сайта в набор доменов, которым разрешен доступ, добавив его к значению заголовка Access-Control-Allow-Origin
.
Например, чтобы разрешить сайту https://amazing.site
доступ к ресурсу с помощью CORS, заголовок должен быть таким:
Access-Control-Allow-Origin: https://amazing.site
Вы также можете настроить сайт так, чтобы любой сайт мог получить к нему доступ, используя подстановочный знак *
. Вы должны использовать это только для общедоступных API. Частные API никогда не должны использовать *
, а вместо этого должны иметь определенный домен или домены. Кроме того, подстановочный знак работает только для запросов, сделанных с атрибутом crossorigin
, установленным на anonymous
, и предотвращает отправку учетных данных, таких как файлы cookie, в запросах.
Access-Control-Allow-Origin: *
Предупреждение: использование подстановочного знака для разрешения всем сайтам доступа к частному API — плохая идея.
Чтобы разрешить любому сайту делать запросы CORS без использования подстановочного знака *
(например, для включения учетных данных), ваш сервер должен прочитать значение заголовка Origin
запроса и использовать это значение для установки Access-Control-Allow-Origin
, а также должен установите заголовок Vary: Origin
чтобы указать, что некоторые заголовки устанавливаются динамически в зависимости от источника.
Точная директива для установки заголовков зависит от вашего веб-сервера. В Apache добавьте следующую строку в конфигурацию сервера (в соответствующем разделе <Directory>
, <Location>
, <Files>
или <VirtualHost>
). Конфигурация обычно находится в файле .conf
( обычно для них используются httpd.conf
и apache.conf
) или в файле .htaccess
.
Header set Access-Control-Allow-Origin 'origin-list'
Для Nginx это команда установки этого заголовка:
add_header 'Access-Control-Allow-Origin' 'origin-list';
See also
- CORS errors
- Glossary: CORS
- CORS introduction
HTTP
-
Errors: CORSMIssingAllowCredentials
Запрос CORS требует, чтобы сервер разрешал использование учетных данных, но для значения заголовка сервера Access-Control-Allow-Credentials не установлено значение true enable
-
Errors: CORSMissingAllowHeaderFromPreflight
Access-Control-Allow-Headers отправляется сервером,чтобы сообщить клиенту,что он поддерживает CORS-запросы.
-
Errors: CORSMultipleAllowOriginNotAllowed
Сервером было отправлено более одного заголовка Access-Control-Allow-Origin.
-
Errors: CORSNotSupportingCredentials
Запрос CORS был предпринят с установленным флагом учетных данных, но сервер настроен с использованием подстановочного значения Access-Control-Allow-Origin, которое не использует