Mod note: This question is about why XMLHttpRequest/fetch/etc. on the browser are subject to the Same Access Policy restrictions (you get errors mentioning CORB or CORS) while Postman is not. This question is not about how to fix a "No 'Access-Control-Allow-Origin'..." error. It's about why they happen.
Please stop posting:
CORS configurations for every language/framework under the sun. Instead find your relevant language/framework's question.
3rd party services that allow a request to circumvent CORS
Command line options for turning off CORS for various browsers
I am trying to do authorization using JavaScript by connecting to the RESTful API built-in Flask. However, when I make the request, I get the following error:
XMLHttpRequest cannot load http://myApiUrl/login.
No 'Access-Control-Allow-Origin' header is present on the requested resource.
Origin 'null' is therefore not allowed access.
I know that the API or remote resource must set the header, but why did it work when I made the request via the Chrome extension Postman?
This is the request code:
$.ajax({
type: 'POST',
dataType: 'text',
url: api,
username: 'user',
password: 'pass',
crossDomain: true,
xhrFields: {
withCredentials: true,
},
})
.done(function (data) {
console.log('done');
})
.fail(function (xhr, textStatus, errorThrown) {
alert(xhr.responseText);
alert(textStatus);
});
If I understood it right you are doing an XMLHttpRequest to a different domain than your page is on. So the browser is blocking it as it usually allows a request in the same origin for security reasons. You need to do something different when you want to do a cross-domain request.
When you are using Postman they are not restricted by this policy. Quoted from Cross-Origin XMLHttpRequest:
Regular web pages can use the XMLHttpRequest object to send and receive data from remote servers, but they're limited by the same origin policy. Extensions aren't so limited. An extension can talk to remote servers outside of its origin, as long as it first requests cross-origin permissions.
WARNING: Using Access-Control-Allow-Origin: * can make your API/website vulnerable to cross-site request forgery (CSRF) attacks. Make certain you understand the risks before using this code.
It's very simple to solve if you are using PHP. Just add the following script in the beginning of your PHP page which handles the request:
<?php header('Access-Control-Allow-Origin: *'); ?>
If you are using Node-red you have to allow CORS in the node-red/settings.js file by un-commenting the following lines:
// The following property can be used to configure cross-origin resource sharing
// in the HTTP nodes.
// See https://github.com/troygoode/node-cors#configuration-options for
// details on its contents. The following is a basic permissive set of options:
httpNodeCors: {
origin: "*",
methods: "GET,PUT,POST,DELETE"
},
If you are using Flask same as the question; you have first to install flask-cors
pip install -U flask-cors
Then include the Flask cors package in your application.
from flask_cors import CORS
A simple application will look like:
from flask import Flask
from flask_cors import CORS
app = Flask(__name__)
CORS(app)
#app.route("/")
def helloWorld():
return "Hello, cross-origin-world!"
For more details, you can check the Flask documentation.
Because
$.ajax({type: "POST" - calls OPTIONS
$.post( - calls POST
Both are different. Postman calls "POST" properly, but when we call it, it will be "OPTIONS".
For C# web services - Web API
Please add the following code in your web.config file under the <system.webServer> tag. This will work:
<httpProtocol>
<customHeaders>
<add name="Access-Control-Allow-Origin" value="*" />
</customHeaders>
</httpProtocol>
Please make sure you are not doing any mistake in the Ajax call.
jQuery
$.ajax({
url: 'http://mysite.microsoft.sample.xyz.com/api/mycall',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
type: "POST", /* or type:"GET" or type:"PUT" */
dataType: "json",
data: {
},
success: function (result) {
console.log(result);
},
error: function () {
console.log("error");
}
});
Note: If you are looking for downloading content from a third-party website then this will not help you. You can try the following code, but not JavaScript.
System.Net.WebClient wc = new System.Net.WebClient();
string str = wc.DownloadString("http://mysite.microsoft.sample.xyz.com/api/mycall");
Deep
In the below investigation as API, I use http://example.com instead of http://myApiUrl/login from your question, because this first one working. I assume that your page is on http://my-site.local:8088.
NOTE: The API and your page have different domains!
The reason why you see different results is that Postman:
set header Host=example.com (your API)
NOT set header Origin
Postman actually not use your website url at all (you only type your API address into Postman) - he only send request to API, so he assume that website has same address as API (browser not assume this)
This is similar to browsers' way of sending requests when the site and API has the same domain (browsers also set the header item Referer=http://my-site.local:8088, however I don't see it in Postman). When Origin header is not set, usually servers allow such requests by default.
This is the standard way how Postman sends requests. But a browser sends requests differently when your site and API have different domains, and then CORS occurs and the browser automatically:
sets header Host=example.com (yours as API)
sets header Origin=http://my-site.local:8088 (your site)
(The header Referer has the same value as Origin). And now in Chrome's Console & Networks tab you will see:
When you have Host != Origin this is CORS, and when the server detects such a request, it usually blocks it by default.
Origin=null is set when you open HTML content from a local directory, and it sends a request. The same situation is when you send a request inside an <iframe>, like in the below snippet (but here the Host header is not set at all) - in general, everywhere the HTML specification says opaque origin, you can translate that to Origin=null. More information about this you can find here.
fetch('http://example.com/api', {method: 'POST'});
Look on chrome-console > network tab
If you do not use a simple CORS request, usually the browser automatically also sends an OPTIONS request before sending the main request - more information is here. The snippet below shows it:
fetch('http://example.com/api', {
method: 'POST',
headers: { 'Content-Type': 'application/json'}
});
Look in chrome-console -> network tab to 'api' request.
This is the OPTIONS request (the server does not allow sending a POST request)
You can change the configuration of your server to allow CORS requests.
Here is an example configuration which turns on CORS on nginx (nginx.conf file) - be very careful with setting always/"$http_origin" for nginx and "*" for Apache - this will unblock CORS from any domain (in production instead of stars use your concrete page adres which consume your api)
location ~ ^/index\.php(/|$) {
...
add_header 'Access-Control-Allow-Origin' "$http_origin" always;
add_header 'Access-Control-Allow-Credentials' 'true' always;
if ($request_method = OPTIONS) {
add_header 'Access-Control-Allow-Origin' "$http_origin"; # DO NOT remove THIS LINES (doubled with outside 'if' above)
add_header 'Access-Control-Allow-Credentials' 'true';
add_header 'Access-Control-Max-Age' 1728000; # cache preflight value for 20 days
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'My-First-Header,My-Second-Header,Authorization,Content-Type,Accept,Origin';
add_header 'Content-Length' 0;
add_header 'Content-Type' 'text/plain charset=UTF-8';
return 204;
}
}
Here is an example configuration which turns on CORS on Apache (.htaccess file)
# ------------------------------------------------------------------------------
# | Cross-domain Ajax requests |
# ------------------------------------------------------------------------------
# Enable cross-origin Ajax requests.
# http://code.google.com/p/html5security/wiki/CrossOriginRequestSecurity
# http://enable-cors.org/
# <IfModule mod_headers.c>
# Header set Access-Control-Allow-Origin "*"
# </IfModule>
# Header set Header set Access-Control-Allow-Origin "*"
# Header always set Access-Control-Allow-Credentials "true"
Access-Control-Allow-Origin "http://your-page.com:80"
Header always set Access-Control-Allow-Methods "POST, GET, OPTIONS, DELETE, PUT"
Header always set Access-Control-Allow-Headers "My-First-Header,My-Second-Header,Authorization, content-type, csrf-token"
Applying a CORS restriction is a security feature defined by a server and implemented by a browser.
The browser looks at the CORS policy of the server and respects it.
However, the Postman tool does not bother about the CORS policy of the server.
That is why the CORS error appears in the browser, but not in Postman.
The error you get is due to the CORS standard, which sets some restrictions on how JavaScript can perform ajax requests.
The CORS standard is a client-side standard, implemented in the browser. So it is the browser which prevent the call from completing and generates the error message - not the server.
Postman does not implement the CORS restrictions, which is why you don't see the same error when making the same call from Postman.
Why doesn't Postman implement CORS? CORS defines the restrictions relative to the origin (URL domain) of the page which initiates the request. But in Postman the requests doesn't originate from a page with an URL so CORS does not apply.
Solution & Issue Origins
You are making a XMLHttpRequest to different domains, example:
Domain one: some-domain.com
Domain Two: some-different-domain.com
This difference in domain names triggers CORS (Cross-Origin Resource Sharing) policy called SOP (Same-Origin Policy) that enforces the use of same domains (hence Origin) in Ajax, XMLHttpRequest and other HTTP requests.
Why did it work when I made the request via the Chrome extension
Postman?
A client (most Browsers and Development Tools) has a choice to enforce the Same-Origin Policy.
Most browsers enforce the policy of Same-Origin Policy to prevent issues related to CSRF (Cross-Site Request Forgery) attack.
Postman as a development tool chooses not to enforce SOP while some browsers enforce, this is why you can send requests via Postman that you cannot send with XMLHttpRequest via JS using the browser.
For browser testing purposes:
Windows - Run:
chrome.exe --user-data-dir="C://Chrome dev session" --disable-web-security
The command above will disable chrome web security. So for example if you work on a local project and encounter CORS policy issue when trying to make a request, you can skip this type of error with the above command. Basically it will open a new chrome session.
You might also get this error if your gateway timeout is too short and the resource you are accessing takes longer to process than the timeout. This may be the case for complex database queries etc. Thus, the above error code can be disguishing this problem. Just check if the error code is 504 instead of 404 as in Kamil's answer or something else. If it is 504, then increasing the gateway timeout might fix the problem.
In my case the CORS error could be removed by disabling the same origin policy (CORS) in the Internet Explorer browser, see How to disable same origin policy Internet Explorer. After doing this, it was a pure 504 error in the log.
To resolve this issue, write this line of code in your doGet() or doPost() function whichever you are using in backend
response.setHeader("Access-Control-Allow-Origin", "*");
Instead of "*" you can type in the website or API URL endpoint which is accessing the website else it will be public.
Your IP address is not whitelisted, so you are getting this error.
Ask the backend staff to whitelist your IP address for the service you are accessing.
Access-Control-Allow-Headers
For me I got this issue for different reason, the remote domain was added to origins the deployed app works perfectly except one end point I got this issue:
Origin https://mai-frontend.vercel.app is not allowed by Access-Control-Allow-Origin. Status code: 500
and
Fetch API cannot load https://sciigo.herokuapp.com/recommendations/recommendationsByUser/8f1bb29e-8ce6-4df2-b138-ffe53650dbab due to access control checks.
I discovered that my Heroku database table does not contains all the columns of my local table after updating Heroku database table everything worked well.
It works for me by applying this middleware in globally:
<?php
namespace App\Http\Middleware;
use Closure;
class Cors {
public function handle($request, Closure $next) {
return $next($request)
->header('Access-Control-Allow-Origin', '*')
->header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS')
->header('Access-Control-Allow-Headers', "Accept,authorization,Authorization, Content-Type");
}
}
Related
Background:
Let's have a WebAssembly (wasm) originating from .net code.
This wasm uses HttpClient and HttpClientHandler to access a backend API at https://api.uri.
The actual backend API location might change in time (like https://api.uri/version-5), but there is still this fixed endpoint, which provides redirection (3xx response) to the current location (which is in the same domain).
The API allows CORS, meaning it sends e.g. Access-Control-Allow-Origin: * headers in the responses.
In the normal (non-wasm) world, one just:
Plainly GETs the https://api.uri with no additional headers (CORS safe).
Retrieve the Location: header (containing e.g. https://api.uri/version-5) from the 3xx response as the final URI.
GETs/POSTs the final URI with additional headers (as needed, e.g. custom, auth, etc.).
Note: In ideal world, the redirection is handled transparently and the first two steps can just be omitted.
Although in the wasm world:
You are not allowed to (let the wasm/browser) send the OPTIONS pre-flight requests to a redirecting endpoint (https://api.uri).
You can't send any non-cors headers, when wanting to prevent pre-flight requests (reason for two stages, plain and full, described above).
You can't see the Location: header value (like https://api.uri/version-5) when trying the manual redirection (HttpClientHandler.AllowAutoRedirect = false), because the response is just artificially crafted with HTTP status code of 0 and ReasonPhrase == "opaqueredirect" - adoption to browser's Fetch API. What a nonsense! #1...
You can't see the auto-followed Location: header value in response.RequestMessage?.RequestUri, when trying the (default) automatic redirection (HttpClientHandler.AllowAutoRedirect = true), because there is still the original URI (https://api.uri) instead of the very expected auto-followed one (https://api.uri/version-5). What a nonsense! #2...
You can't send the full blown request with all the headers and rely on the automatic redirection, because it would trigger pre-flight, which is sill not allowed on redirecting endpoint.
So, the obvious question is:
Is there ANY way, how to handle such simple scenario from the Web Assembly?
(and not crash on CORS)
GET https://api.uri => 3xx, Location: https://api.uri/version-5
GET https://api.uri/version-5, Authorization: Basic BlaBlaBase64= ; Custom: Cool-Value => 200
Note: All this has been discovered within the Uno Platform wasm head, but I believe it applies for any .net wasm.
Note: I also guess "disabled" CORS (on the request side, via Sec-Fetch-Mode: no-cors) wouldn't help either, as then such request is not allowed to have additional headers/methods, right?
I have a Rails API with a React client side. I have had everything in the app setup for a long time now and today while I was working on it I suddenly started getting the error:
Access to XMLHttpRequest at 'http://localhost:3000/api/v1/user/authed'
from origin 'http://localhost:8000' has been blocked by CORS policy:
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'. The credentials mode of requests initiated by the
XMLHttpRequest is controlled by the withCredentials attribute.
Now none of the requests in my application work at all.
The request does go through from the React app to the Rails API and the Rails API responds properly as well (I can see this in the terminal) but nothing actually happens on the Client side because I am assuming it gets blocked for the CORS reason.
Is there something I can do to fix this? Could it be that some package is somehow updated on my system and different from the project so now it breaks?
URL to make request to:
const ENDPOINT = '/api/v1',
PORT = 3000,
URL = window.location.protocol + '//' + window.location.hostname + ':' + PORT + ENDPOINT;
The request
$.ajax({
url: URL + '/' + resource,
type: verb,
data: params,
xhrFields: { withCredentials: true }
})
.done(callback)
.fail(errcallback);
Request functions have the format:
static get(resource, params, callback, errcallback) {
API.send('GET', resource, params, callback, errcallback);
}
If your API doesn't require credentials you should remove withCredentials: true.
More about withCredentials:
The XMLHttpRequest.withCredentials property is a Boolean that indicates whether or not cross-site Access-Control requests should be made using credentials such as cookies, authorization headers or TLS client certificates. Setting withCredentials has no effect on same-site requests.
https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/withCredentials
So I am deploying an Angular 5 app with a Rails 5 back-end. I can get the data to flow properly between the two locally, but trying to connect to the deployed version of the API (which is on Heroku) I run into some authorization issue. The error is:
Failed to load https://my_api.herokuapp.com/data.json: No 'Access-Control-Allow-Origin' header is present on the requested resource.
Origin 'http://localhost:4200' is therefore not allowed access.
The response had HTTP status code 404.
Cross-Origin Read Blocking (CORB) blocked cross-origin response <URL> with MIME type application/json.
See <URL> for more details.
Is this something I need to change within the Rails API or in Angular? The deployed Rails API is essentially the same as the local version so I'm not sure where the disconnect is coming from.
There are only two refrences to the API in Angular. I connect to it the same way that I do to the local server:
Angular, app-module.ts
providers: [Angular2TokenService, AuthService, AuthGuard,
// {provide: 'api', useValue: 'http://localhost:3000/'}
{provide: 'api', useValue: 'https://my_ api.herokuapp.com/data.json'}
]
Perhaps it's my use of Angular2TokenService?
Angular, environment.ts:
export const environment = {
production: false,
token_auth_config: {
// apiBase: 'http://localhost:3000'
apiBase: 'https://my_api.herokuapp.com/data.json'
}};
Thanks! Let me know of any suggestions you might have or if you need clarification.
It's issue with CORS(cross-origin-resource-sharing). You can handle it by adding callback in your API like below:
def cors_set_access_control_headers
headers['Access-Control-Allow-Origin'] = ENV['SERVER_URL'] || '*'
end
where SERVER_URL is your front-end server URL
Else you can use gem 'rack-cors' as suggested in comments by #Kedarnag Mukanahallipatna
I have a grails 2.2.4 application. I wanted to enable CORS
So I installed cors plugin by having the following line in build config.
plugins {
runtime ':cors:1.1.8'
}
Then in the config.groovy
cors.headers = ['Access-Control-Allow-Origin': '*']
But after this when I run the application, CORS in not enabled. So I debugged the CORS plugin. The issue seems to be in CorsFilter class in the following method
private boolean checkOrigin(HttpServletRequest req, HttpServletResponse resp) {
String origin = req.getHeader("Origin");
if (origin == null) {
//no origin; per W3C spec, terminate further processing for both preflight and actual requests
return false;
}
The origin parameter in the above line is always null as the request does not have the parameter 'Origin'. Is there something i'm doing wrong? I'm not looking for the answer which says add a manual header with the name "Origin" since that is not exactly a proper fix
I'm quite new to CORS so appriciate the help.
In addition to Access-Control-Allow-Origin, and in addition to setting the Origin header on request, you probably need to specify these response headers as well:
Access-Control-Allow-Headers: accept
Access-Control-Allow-Headers: origin
Access-Control-Allow-Headers: content-type
Access-Control-Allow-Method: GET
Access-Control-Allow-Method: POST
Also make sure you respond to HTTP OPTIONS requests with these headers and a blank 200 OK response.
For now, let's assume that RestClient is sending the Origin header properly. It may still be getting stripped by your application. You can prevent this using the Access-Control-Allow-Headers: Origin header.
Most of the problems I have had with my web services is that the right headers are being sent, but they are stripped from the message by my web server. So I tend to adopt a shotgun approach of "allow everything" and then one by one remove what I don't need. My allow-headers header usually is pretty long and I end up having to include stuff like Content-Type, X-Requested-With and other junk before my requests will finally go through.
I further recommend that you test using something besides RestClient, if only as a sanity check. I use Postman, a free Chrome app, for all my messaging tests. It looks to me like the problem is with RestClient not sending the proper Origin header.
I've installed Luracast's Restler API framework and am having marvelous success with it all except when sending PUT or DELETE across domains. The below works fine when all on the same server, but when I cross domains, Firebug shows the the PUT or GET as OPTIONS, and it is not found on the server. Am baffled how to stop "OPTIONS" being sent instead of PUT or DELETE.
$.ajax({
url: url,
type: 'PUT',
data: "thename="+ $('#TheName').val(),
success: function(xhr, status) {
console.info(xhr);
},
error: function(xhr, status) {
console.info(xhr.responseText);
},
complete: function(xhr, status) {
$('#showResponse').val(xhr.responseText);
}
});
Per another thread somewhere, I've added the below to the Restler output:
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: GET, POST, DELETE, PUT, OPTIONS');
You've got the right response headers, but you have to have your server respond to an OPTIONS request with those headers, too.
This is a cross-origin request, and is subject to something called preflighting. Before making the PUT or DELETE request the browser asks the target web server if it's safe to do so from a web page at another domain. It asks that using the OPTIONS method. Unless the target server says it's okay, the web browser will never make the PUT or DELETE request. It has to preflight the request, because once it's made the PUT or DELETE, it's too late to honor the response; sensitive information may have been leaked.
GET and POST are a bit more complicated, as sometimes the browser decides they are safe without asking first, while other times the browser will also do a preflight check. It depends on whether certain headers are used in the request.
The CORS spec has the gory details. The bottom line is that the code on your web page will not be allowed to make these requests unless the target web server supports the OPTIONS method, and the response to the OPTIONS method includes the headers saying that such requests are allowed.