How does Access-Control-Allow-Origin header work?
Access-Control-Allow-Origin
is a CORS (Cross-Origin Resource Sharing) header.
When Site A tries to fetch content from Site B, Site B can send an Access-Control-Allow-Origin
response header to tell the browser that the content of this page is accessible to certain origins. (An origin is a domain, plus a scheme and port number.) By default, Site B's pages are not accessible to any other origin; using the Access-Control-Allow-Origin
header opens a door for cross-origin access by specific requesting origins.
For each resource/page that Site B wants to make accessible to Site A, Site B should serve its pages with the response header:
Access-Control-Allow-Origin: http://siteA.com
Modern browsers will not block cross-domain requests outright. If Site A requests a page from Site B, the browser will actually fetch the requested page on the network level and check if the response headers list Site A as a permitted requester domain. If Site B has not indicated that Site A is allowed to access this page, the browser will trigger the XMLHttpRequest
's error
event and deny the response data to the requesting JavaScript code.
Non-simple requests
What happens on the network level can be slightly more complex than explained above. If the request is a "non-simple" request, the browser first sends a data-less "preflight" OPTIONS request, to verify that the server will accept the request. A request is non-simple when either (or both):
- using an HTTP verb other than GET or POST (e.g. PUT, DELETE)
- using non-simple request headers; the only simple requests headers are:
Accept
Accept-Language
Content-Language
Content-Type
(this is only simple when its value isapplication/x-www-form-urlencoded
,multipart/form-data
, ortext/plain
)
If the server responds to the OPTIONS preflight with appropriate response headers (Access-Control-Allow-Headers
for non-simple headers, Access-Control-Allow-Methods
for non-simple verbs) that match the non-simple verb and/or non-simple headers, then the browser sends the actual request.
Supposing that Site A wants to send a PUT request for /somePage
, with a non-simple Content-Type
value of application/json
, the browser would first send a preflight request:
OPTIONS /somePage HTTP/1.1
Origin: http://siteA.com
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: Content-Type
Note that Access-Control-Request-Method
and Access-Control-Request-Headers
are added by the browser automatically; you do not need to add them. This OPTIONS preflight gets the successful response headers:
Access-Control-Allow-Origin: http://siteA.com
Access-Control-Allow-Methods: GET, POST, PUT
Access-Control-Allow-Headers: Content-Type
When sending the actual request (after preflight is done), the behavior is identical to how a simple request is handled. In other words, a non-simple request whose preflight is successful is treated the same as a simple request (i.e., the server must still send Access-Control-Allow-Origin
again for the actual response).
The browsers sends the actual request:
PUT /somePage HTTP/1.1
Origin: http://siteA.com
Content-Type: application/json
{ "myRequestContent": "JSON is so great" }
And the server sends back an Access-Control-Allow-Origin
, just as it would for a simple request:
Access-Control-Allow-Origin: http://siteA.com
See Understanding XMLHttpRequest over CORS for a little more information about non-simple requests.
How is access-control-allow-origin * different from omitting the header?
Access-Control-Allow-Origin: *
the value *
tells browsers to allow requesting code from any origin to access the resource.
If this header is not set, the only origin allowed to access the resource would have to be on the same domain.
More info about it here: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Origin
Access Control Origin Header error using Axios
If your backend support CORS, you probably need to add to your request this header:
headers: {"Access-Control-Allow-Origin": "*"}
[Update] Access-Control-Allow-Origin is a response header - so in order to enable CORS - you need to add this header to the response from your server.
But for the most cases better solution would be configuring the reverse proxy, so that your server would be able to redirect requests from the frontend to backend, without enabling CORS.
You can find documentation about CORS mechanism here:
https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS
No 'Access-Control-Allow-Origin' header is present on the requested resource—when trying to get data from a REST API
This answer covers a lot of ground, so it’s divided into three parts:
- How to use a CORS proxy to avoid “No Access-Control-Allow-Origin header” problems
- How to avoid the CORS preflight
- How to fix “Access-Control-Allow-Origin header must not be the wildcard” problems
How to use a CORS proxy to avoid “No Access-Control-Allow-Origin header” problems
If you don’t control the server your frontend code is sending a request to, and the problem with the response from that server is just the lack of the necessary Access-Control-Allow-Origin
header, you can still get things to work—by making the request through a CORS proxy.
You can easily run your own proxy with code from https://github.com/Rob--W/cors-anywhere/.
You can also easily deploy your own proxy to Heroku in just 2-3 minutes, with 5 commands:
git clone https://github.com/Rob--W/cors-anywhere.git
cd cors-anywhere/
npm install
heroku create
git push heroku master
After running those commands, you’ll end up with your own CORS Anywhere server running at, e.g., https://cryptic-headland-94862.herokuapp.com/
.
Now, prefix your request URL with the URL for your proxy:
https://cryptic-headland-94862.herokuapp.com/https://example.com
Adding the proxy URL as a prefix causes the request to get made through your proxy, which:
- Forwards the request to
https://example.com
. - Receives the response from
https://example.com
. - Adds the
Access-Control-Allow-Origin
header to the response. - Passes that response, with that added header, back to the requesting frontend code.
The browser then allows the frontend code to access the response, because that response with the Access-Control-Allow-Origin
response header is what the browser sees.
This works even if the request is one that triggers browsers to do a CORS preflight OPTIONS
request, because in that case, the proxy also sends the Access-Control-Allow-Headers
and Access-Control-Allow-Methods
headers needed to make the preflight succeed.
How to avoid the CORS preflight
The code in the question triggers a CORS preflight—since it sends an Authorization
header.
https://developer.mozilla.org/docs/Web/HTTP/Access_control_CORS#Preflighted_requests
Even without that, the Content-Type: application/json
header will also trigger a preflight.
What “preflight” means: before the browser tries the POST
in the code in the question, it first sends an OPTIONS
request to the server, to determine if the server is opting-in to receiving a cross-origin POST
that has Authorization
and Content-Type: application/json
headers.
It works pretty well with a small curl script - I get my data.
To properly test with curl
, you must emulate the preflight OPTIONS
the browser sends:
curl -i -X OPTIONS -H "Origin: http://127.0.0.1:3000" \
-H 'Access-Control-Request-Method: POST' \
-H 'Access-Control-Request-Headers: Content-Type, Authorization' \
"https://the.sign_in.url"
…with https://the.sign_in.url
replaced by whatever your actual sign_in
URL is.
The response the browser needs from that OPTIONS
request must have headers like this:
Access-Control-Allow-Origin: http://127.0.0.1:3000
Access-Control-Allow-Methods: POST
Access-Control-Allow-Headers: Content-Type, Authorization
If the OPTIONS
response doesn’t include those headers, the browser will stop right there and never attempt to send the POST
request. Also, the HTTP status code for the response must be a 2xx—typically 200 or 204. If it’s any other status code, the browser will stop right there.
The server in the question responds to the OPTIONS
request with a 501 status code, which apparently means it’s trying to indicate it doesn’t implement support for OPTIONS
requests. Other servers typically respond with a 405 “Method not allowed” status code in this case.
So you’ll never be able to make POST
requests directly to that server from your frontend JavaScript code if the server responds to that OPTIONS
request with a 405 or 501 or anything other than a 200 or 204 or if doesn’t respond with those necessary response headers.
The way to avoid triggering a preflight for the case in the question would be:
- if the server didn’t require an
Authorization
request header but instead, e.g., relied on authentication data embedded in the body of thePOST
request or as a query param - if the server didn’t require the
POST
body to have aContent-Type: application/json
media type but instead accepted thePOST
body asapplication/x-www-form-urlencoded
with a parameter namedjson
(or whatever) whose value is the JSON data
How to fix “Access-Control-Allow-Origin header must not be the wildcard” problems
I am getting another error message:
The value of the 'Access-Control-Allow-Origin' header in the response
must not be the wildcard '*' when the request's credentials mode is
'include'. Origin 'http://127.0.0.1:3000
' is therefore not allowed
access. The credentials mode of requests initiated by the
XMLHttpRequest is controlled by the withCredentials attribute.
For requests that have credentials, browsers won’t let your frontend JavaScript code access the response if the value of the Access-Control-Allow-Origin
header is *
. Instead the value in that case must exactly match your frontend code’s origin, http://127.0.0.1:3000
.
See Credentialed requests and wildcards in the MDN HTTP access control (CORS) article.
If you control the server you’re sending the request to, a common way to deal with this case is to configure the server to take the value of the Origin
request header, and echo/reflect that back into the value of the Access-Control-Allow-Origin
response header; e.g., with nginx:
add_header Access-Control-Allow-Origin $http_origin
But that’s just an example; other (web) server systems have similar ways to echo origin values.
I am using Chrome. I also tried using that Chrome CORS Plugin
That Chrome CORS plugin apparently just simplemindedly injects an Access-Control-Allow-Origin: *
header into the response the browser sees. If the plugin were smarter, what it would be doing is setting the value of that fake Access-Control-Allow-Origin
response header to the actual origin of your frontend JavaScript code, http://127.0.0.1:3000
.
So avoid using that plugin, even for testing. It’s just a distraction. To test what responses you get from the server with no browser filtering them, you’re better off using curl -H
as above.
As far as the frontend JavaScript code for the fetch(…)
request in the question:
headers.append('Access-Control-Allow-Origin', 'http://localhost:3000');
headers.append('Access-Control-Allow-Credentials', 'true');
Remove those lines. The Access-Control-Allow-*
headers are response headers. You never want to send them in requests. The only effect of that is to trigger a browser to do a preflight.
How does Access-Control-Expose-Headers work?
I figured it out. The reason I was receiving my custom header was that I was reading the response headers in the Network tab of Chrome Dev Tools. When I run this script:
fetch('http://127.0.0.1:3000/')
.then(r => {console.log(response.headers.get('foo'))})
It prints null
. So the header is not actually accessible to the fetch request, only to the Dev Tools.
I get No 'Access-Control-Allow-Origin' header error even though I set it
Your problem is related with the fact that 'Access-Control-Allow-Origin': '*' needs to be set at the server. By doing this you enable CORS (Cross-Origin Resource Sharing)
Browsers have a security policy that prevents your javascript code to make requests from services that are not within your domain. For example if your javascript code is executed in http://example.com and the service you are targeting is found at http://example.com/api/myservice then your request will go through normally. If however, the service you are trying to access is at http://someotherdomain.net then the browser will not complete your request successfully even if the server responds normally.
You will have to read documentation about whatever web server software you're using on how to set headers on it. When you set 'Access-Control-Allow-Origin': '*' on your server your are essentially saying to the world - "You can load my data onto any browser application that lives in any domain". This means anyone in the world can call your service and if you wish to prevent this, you will have to implement authentication (common for this are API key models).
Access-Control-Allow-Origin Multiple Origin Domains?
Sounds like the recommended way to do it is to have your server read the Origin header from the client, compare that to the list of domains you would like to allow, and if it matches, echo the value of the Origin
header back to the client as the Access-Control-Allow-Origin
header in the response.
With .htaccess
you can do it like this:
# ----------------------------------------------------------------------
# Allow loading of external fonts
# ----------------------------------------------------------------------
<FilesMatch "\.(ttf|otf|eot|woff|woff2)$">
<IfModule mod_headers.c>
SetEnvIf Origin "http(s)?://(www\.)?(google.com|staging.google.com|development.google.com|otherdomain.example|dev02.otherdomain.example)$" AccessControlAllowOrigin=$0
Header add Access-Control-Allow-Origin %{AccessControlAllowOrigin}e env=AccessControlAllowOrigin
Header merge Vary Origin
</IfModule>
</FilesMatch>
Related Topics
How to Get the Background Color Code of an Element in Hex
Removing or Replacing a Stylesheet (A ≪Link≫) With JavaScript/Jquery
Animating Addclass/Removeclass With Jquery
Add Active Navigation Class Based on Url
Select All Elements That Have a Specific Css, Using Jquery
How to Do Fade-In and Fade-Out With JavaScript and Css
How to Retrieve the Display Property of a Dom Element
Canvas2D Todataurl() Different Output on Different Browser
Change CSS of Selected Text Using JavaScript
List Every Font a User'S Browser Can Display
Get a CSS Value With JavaScript
How to Change the Style of Alert Box
How to Make the Browser Wait to Display the Page Until It's Fully Loaded
How to Get the Actual Rendered Font When It's Not Defined in Css
Fetch(), How to Make a Non-Cached Request
Select All Div Text With Single Mouse Click