I am using Beego/Golang as my backend and having an issue with No 'Access-Control-Allow-Origin' header when trying to fetch a URL from my domain. I searched on Google and put this in func main() but it still does not work, I still have the same error.
// (my own code) FilterUser is used to redirect users to login
// when they try to access some pages without logging in
beego.InsertFilter("/*", beego.BeforeExec, FilterUser)
// This is what I found on Google
beego.InsertFilter("*", beego.BeforeRouter, cors.Allow(&cors.Options{
AllowAllOrigins: true,
AllowMethods: []string{"GET, POST, PUT, DELETE, OPTIONS"},
AllowHeaders: []string{"Origin"},
ExposeHeaders: []string{"Content-Length"},
AllowCredentials: true,
}))
You're setting both AllowCredentials and AllowAllOrigins. A casual examination of the source code of Beego's cors package indicates that, as a result, responses to preflight requests contain the following combination of headers:
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true
However, the Fetch standard (which defines how CORS works) instructs browsers to reject this combination—because honouring it would be very insecure. See this relevant passage of the MDN Web Docs about CORS:
When responding to a credentialed request, the server must specify an origin in the value of the Access-Control-Allow-Origin header, instead of specifying the "*" wildcard.
One way to fix the issue would be to allow, not all origins, but only the origin of your frontend; I used https://example.com as a placeholder below:
beego.InsertFilter("*", beego.BeforeRouter, cors.Allow(&cors.Options{
AllowOrigins: []string{"https://example.com"}, // <---
// -snip-
AllowCredentials: true,
}))
Related
I have a working application with a front end in vuejs and a backend in go. I'm in the process of adding the final features to the authentication and I'm running into an error.
When I try to add any http header to the vuejs request I get an error.
This is what I'm adding to the vue component:
http: {
headers: {
Authorization: 'Bearer 123'
}
}
And I'm getting a preflight error:
XMLHttpRequest cannot load http://localhost:8000/api/investments?limit=6. 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:8080' is therefore not allowed access.
Now I know this is usually a server side problem but I am adding the response header resp.Header().Set("Access-Control-Allow-Origin", "*"). cors. If I remove the the http property in the vue object everything works fine. Its pretty bizzare I even tried handling the OPTIONS request with go but my handler for the api doesn't even get hit.
Ok so I found the answer it is a server problem the Authorization header was not authorized. So just add it as a middleware to allow for it.
c := cors.New(cors.Options{
AllowedHeaders: []string{"Origin", "Accept", "Content-Type", "Authorization"},
})
handler := c.Handler(router)
log.Fatal(http.ListenAndServe(":8000", handler))
I am using the rs/cors package
With my NGinx setup I was able to intercept OPTIONS requests from ajax preflight and respond with the correct CORS headers and a 200 response so the request could continue onwards. I am attempting to consolidate my frontend proxies into HAProxy and am having some issues getting this piece of the puzzle working.
My particular issue is that while I am able to add the proper CORS options when there is a server able to respond properly to an OPTIONS request, a few of the backends cannot handle/respond with a 405 error when the preflight request is issued. My haproxy.cfg included the following lines for adding the headers:
capture request header origin len 128
http-response add-header Access-Control-Allow-Origin %[capture.req.hdr(0)] if { capture.req.hdr(0) -m found }
rspadd Access-Control-Allow-Credentials:\ true if { capture.req.hdr(0) -m found }
rspadd Access-Control-Allow-Headers:\ Origin,\ X-Requested-With,\ Content-Type,\ Origin,\ User-Agent,\ If-Modified-Since,\ Cache-Control,\ Accept if { capture.req.hdr(0) -m found }
rspadd Access-Control-Allow-Methods:\ GET,\ POST,\ PUT,\ DELETE,\ OPTIONS if { capture.req.hdr(0) -m found }
rspadd Access-Control-Max-Age:\ 1728000 if { capture.req.hdr(0) -m found }
The solution given in:
How to send a response with HAProxy without passing the request to web servers works when you set all of the correct headers from a client's request, but is not dynamic which is not an ideal solution.
Any help would be appreciated!
Based on the great answer from anine.io I came up with the following solution which allows to define a list of allowed origins and it also adds the missing Acccess-Control-Allow-Origin Header for all HTTP requests. The answer from anine.io only showed the CORS preflight, but didn't consider the normal requests.
In haproxy.cfg load the cors.lua file (adapt the path if necessary) in the global section
global
lua-load /usr/local/etc/haproxy/cors.lua
Add the CORS configuration to your frontend definition
frontend http-in
# CORS configuration
# capture origin HTTP header
capture request header origin len 128
# add Access-Control-Allow-Origin HTTP header to response if origin matches the list of allowed URLs
http-response add-header Access-Control-Allow-Origin %[capture.req.hdr(0)] if !METH_OPTIONS { capture.req.hdr(0) -m reg -f /usr/local/etc/haproxy/cors-origins.lst }
# if a preflight request is made, use CORS preflight backend
http-request use-service lua.cors-response if METH_OPTIONS { capture.req.hdr(0) -m reg -f /usr/local/etc/haproxy/cors-origins.lst }
Create a file called cors.lua and store it under the path you specified above. The file contains the CORS preflight and if there's no good reason, don't restrict Methods or Headers because you would have to include any restrictions regarding methods or headers in the ACLs defined in the CORS configuration in haproxy.conf. Note: Currently Browsers do not support wildcard * for the Access-Control-Allow-Methods header. The cors.lua file should contain the following content
core.register_service("cors-response", "http", function(applet)
applet:set_status(200)
applet:add_header("Content-Length", "0")
applet:add_header("Access-Control-Allow-Origin", applet.headers["origin"][0])
applet:add_header("Access-Control-Allow-Credentials", "true")
applet:add_header("Access-Control-Allow-Headers", "*")
applet:add_header("Access-Control-Allow-Methods", "GET, HEAD, POST, PUT, DELETE, PATCH, OPTIONS")
applet:add_header("Access-Control-Max-Age", "1728000")
applet:start_response()
end)
Create a file called cors-origins.lst and store it under the path you specified above in the CORS configuration. The file should contain regular expressions (or just simple strings). If the client sends an Origin header, it will be validated against these regular expressions and only if they match, the CORS Preflight from cors.lua will be returned (for HTTP OPTIONS requests) or the Access-Control-Allow-Origin with the value of the origin header of the client request will be added to the response. An example of the content of cors-origins.lst could be
example.com
localhost.*
.*\.mydomain\.com:[8080|8443]
Test the configuration with http://test-cors.org/. For GET requests there should be no CORS Preflight. For requests other than GET, a CORS Preflight request should be done by the client first (e.g. an HTTP OPTIONS call) to check if the intended method, headers and authorization is allowed.
See HTTP access control (CORS) for further details regarding CORS.
You can use Lua, but you need to make sure HAproxy is built with USE_LUA, by checking haproxy -vv.
This is an example configuration, I haven't tried it myself, but it will give you an idea of what you can do:
# haproxy.cfg
global
lua-load cors.lua
frontend foo
...
http-request use-service lua.cors-response if METH_OPTIONS { req.hdr(origin) -m found } { ... }
# cors.lua
core.register_service("cors-response", "http", function(applet)
applet:set_status(200)
applet:add_header("Content-Length", "0")
applet:add_header("Access-Control-Allow-Origin", applet.headers["origin"][0])
applet:add_header("Access-Control-Allow-Credentials", "true")
applet:add_header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Origin, User-Agent, If-Modified-Since, Cache-Control, Accept")
applet:add_header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
applet:add_header("Access-Control-Max-Age", "1728000")
applet:start_response()
end)
I want to throw my own answer in the ring here. We've gone through quite some pain to arrive at a working setup. The other answers to this question here were very helpful, but didn't yield a fully working configuration for us.
We wanted to allow any origin. If you need whitelisted origins, see the answer from #Florian Feldhaus for a useful regex trick. Instead of using a whitelist, we echo back the location header:
http-request set-header Access-Control-Allow-Origin %[capture.req.hdr(0)] if { capture.req.hdr(0) -m found }
We also needed to explicitly set Access-Control-Allow-Headers and Access-Control-Expose-Headers. Browser support for wildcards in theses headers is not quite there yet.
So here's what our configuration does:
handles preflight requests using this cors.lua script
handles normal request using http-response set-header to add Access-Control-Allow-* headers
adjust tune.maxrewrite to fit our CORS headers (which are > 1 KB)
The steps for 1) and 2) are explained in the other answers here, but step 3) took us a long time to figure out. I have documented the full config and the journey that took us there in this blog post. The post contains links to the gist on github.
To intercept OPTIONS like in my case, I want to add Access-Control-Max-Age header to response. I modify my haproxy.cfg and add this line to the backend block.
backend api
balance roundrobin
http-response set-header Access-Control-Max-Age 600 if METH_OPTIONS
HAProxy version: 2.2.6
Reference Doc: https://cbonte.github.io/haproxy-dconv/2.2/configuration.html#http-response%20set-header
In my ng-resource files, I enable the ajax header:
var app = angular.module('custom_resource', ['ngResource'])
app.config(['$httpProvider', function($httpProvider) {
//enable XMLHttpRequest, to indicate it's ajax request
//Note: this disables CORS
$httpProvider.defaults.headers.common["X-Requested-With"] = 'XMLHttpRequest';
}])
app.factory('Article', ['$resource', function($resource) {
return $resource('/article/api/:articleId', {articleId: '#_id'}, {
update: {method: 'PUT'},
query: {method: 'GET', isArray: true}
})
}])
So that I can separate ajax and non-ajax request and response accordingly (to send json data like res.json(data), or to send the entire html page like res.render('a.html')
for example, in my error handler, I need to decide to render error.html page or to just send a error message:
exports.finalHandler = function(err, req, res, next) {
res.status(err.status || 500)
var errorMessage = helper.isProduction() ? '' : (err.message || 'unknown error')
if (req.xhr) {
res.json({message: errorMessage})
}
else {
res.render(dir.error + '/error_page.ejs')
}
}
But now I need to do CORS request to other sites. Is it possible to do CORS request while keeping the ajax header? or other ways I can identify ajax and non-ajax request from server?
In case my question is not clear, heres a relevant article about angular and CORS
http://better-inter.net/enabling-cors-in-angular-js/
Basically, we need to delete xhr header to enable cors for other server, but I need the header for my own server
EDIT 2:
today I tried integrating google map and I got this error:
XMLHttpRequest cannot load http://maps.googleapis.com/maps/api/geocode/json?address=Singapore&sensor=false. Request header field X-Requested-With is not allowed by Access-Control-Allow-Headers.
Setting custom headers on XHR requests triggers a preflight request.
So, it doesn't disable CORS but your server is most likely not handling the preflight request.
Inspired from this post: https://remysharp.com/2011/04/21/getting-cors-working
The solution should be to use the cors module and add the following to your node.js code before your routes:
var corsOptions = {
origin: true,
methods: ['GET', 'PUT', 'POST'],
allowedHeaders: ['X-Requested-With','Content-Type', 'Authorization']
};
app.options('*', cors(corsOptions)); //You may also be just fine with the default options
You can read more at: https://github.com/expressjs/cors
you may try to use cors package
First, to address you primary concern is it possible to do CORS request while keeping the ajax header?: the answer is YES, provided the sites you are accessing allow requests from you or any other external clients at all.
You wrote:
//Note: this disables CORS
$httpProvider.defaults.headers.common["X-Requested-With"] = 'XMLHttpRequest';
But I don't understand what you mean by, it "disables CORS". The X-Requested-With header is not a standard header, and the known effect of adding a non-standard header to a request (made from a browser) is the triggering of a pre-flight request [3].
If the other sites you are interested in would set their servers to refuse processing of requests that do not originate from their own domain, then whether you set that header or not, your request should fail.
It seems everything is working fine for you, for requests sent to you own server. Otherwise you can solve the problem by appending the Access-Control-Allow-Origin header in your server responses as follows:
if you need to allow requests from specific domains
response.set("Access-Control-Allow-Origin", "one-host-domain, your-host-domain, some-other-host-domain"); // second argument is a comma-delimited list of allowed domains
(It may be better for you to actually check the request object for the origin, and if it's permitted based on presence in a pre-determined list, then send back the exact same origin).
If you need to permit all requests regardless of its origin
response.set("Access-Control-Allow-Origin", "*");
That should do, and I hope it clears your doubts for you.
More info on handling CORS when using AJAX: 0, 1 & 2.
EDIT
Following exchanges in the comment, I add the following points to support this answer further.
As it is today, the only side that needs disabling/enabling CORS in the client-server system is the server. All modern browsers allow cross origin requests by default and you don't need to do anything additional to support that capability. I understood that you're adding a custom header to distinguish AJAX requests from the rest?? AFAIK, that header changes nothing about how requests are made by browsers.
Here is how all cross-origin requests are handled by browsers today: for all request methods (but usually with the exception of GET), browsers send a pre-flight request with the OPTION method. If the destination server allows it, the actual request is then sent, otherwise the request fails. In the case where the servers, responds with a refusal there's nothing you nor whatever library you use can do about it. This is the fact from my own experience.
There are 3 solutions that come to my mind:
1. Ask site's admin to enable x-requested-with header in CORS.
2. Use proxy server.
3. Send request without x-requested-with header.
This article should make it clear how CORS works and how to make CORS requests.
Particularly "Simple requests" section and "Access-Control" section, especially access-control-allow-headers description is important in this case.
As it says: for simple requests access-control-allow-origin is enough. However if the request includes custom header (a header which is not included by default, such as x-requested-with header), the preflight request is triggered, and server's response to this request should enable this custom header in access-control-allow-headers by setting its value to either "*" or to the name of a custom header (x-requested-with).
Hope it makes it a little bit clearer.
I'm implementing CORS with credentials and a preflight request and I'm a bit mystified why the preflight request consistently fails in Firefox 30 but works in Safari (7.0.2) and Chrome 35. I think this issue is different from "Why does the preflight OPTIONS request of an authenticated CORS request work in Chrome but not Firefox?" because I am not getting a 401, but rather a CORS-specific message from the browser client:
"Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://myurl.dev.com. This can be fixed by moving the resource to the same domain or enabling CORS."
Without showing source code, here's what I'm doing:
On the server:
Headers for OPTIONS response:
Access-Control-Allow-Origin: [[copy origin from the request here]]
Access-Control-Allow-Methods: "POST GET OPTIONS"
Access-Control-Allow-Headers: "X-Requested-With"
Access-Control-Allow-Credentials: "true"
Headers for POST response:
Access-Control-Allow-Origin: [[copy origin from the request here]]
Access-Control-Allow-Credentials: "true"
In the browser client:
jQuery.ajax({
url: requestUrl,
type: 'POST',
data: getData(),
xhrFields: {
withCredentials: true
}
});
Per the spec, this will trigger a OPTIONS preflight request which needs to have the CORS headers in its response. I've read through the W3C spec several times and I can't identify what I'm doing wrong, if anything, in that preflight response.
QUESTION: "Why is this CORS request failing only in Firefox?"
ANSWER: While unrelated to the OP's specific case, it may help you to know that Firefox does not trust CA's (certificate authorities) in the Windows Certificate Store by default, and this can result in failing CORS requests in Firefox (as was alluded to by Svish in the question comments).
To allow Firefox to trust CA's in the Windows Certificate Store:
In Firefox, type about:config in the address bar
If prompted, accept any warnings
Right-click to create a new boolean value, and enter security.enterprise_roots.enabled as the Name
Set the value to true
Then re-test the failing request
Answer source: https://support.umbrella.com/hc/en-us/articles/115000669728-Configuring-Firefox-to-use-the-Windows-Certificate-Store
Note that Firefox is the only browser that is compliant here. If parsing of Access-Control-Allow-Methods fails per https://fetch.spec.whatwg.org/#cors-preflight-fetch a network error needs to be returned. And per the ABNF for the header value it is most definitely a comma-separated value.
Disabling HTTPS-Only Mode in Firefox solved the issue for me.
I was trying to access a remote resource using HTTP from http://localhost.
Since Firefox 87 (released in March 2021), it's possible to set the below preference in about:config, namely the Firefox Configuration Editor:
network.cors_preflight.allow_client_cert: true
From Firefox for Enterprise 87 - Release notes:
Corporations that use TLS client certificates can flip the network.cors_preflight.allow_client_cert preference to get Google Chrome-compatible handling of the CORS protocol. In particular, contrary to the Fetch Standard, this will transmit TLS client certificates along with a CORS preflight. See bug 1511151 for more information.
References:
https://support.mozilla.org/en-US/kb/firefox-enterprise-87-release-notes
https://bugzilla.mozilla.org/show_bug.cgi?id=1511151
I have noticed that when you a send CORS(Cross Origin Resource Sharing) request with cookies set, Firefox doesn't send the required response headers.
Solution:
Below solution adds headers only for OPTIONS requests and accepts requests only from example.com. You can change the implementation for other request methods and expected hosts.
JS CODE
var xmlhttp = new XMLHttpRequest();
xmlhttp.withCredentials = true;
xmlhttp.onreadystatechange = function () {
if (xmlhttp.readyState == XMLHttpRequest.DONE) {
if (xmlhttp.status == 200) {
success_callback(xmlhttp.responseText);
} else {
error_callback(xmlhttp.statusText);
}
}
};
xmlhttp.open("DELETE", url);
xmlhttp.send(null);
When you send a DELETE request, the browser send a pre-flight request for OPTIONS, which expects Access-Control-Allow-Methods in the response headers. Based on this header value the actual DELETE request is sent. In Firefox, when you send a DELETE request the pre-flight request's response headers do not have expected headers, therefore it fails to send the actual DELETE request.
To overcome this problem use the below NGINX server config.
NGINX CODE
#handle CORS requests by adding required headers
if ($http_origin ~* .example.com) {
set $cors "CORS-${request_method}";
}
if ($cors = "CORS-OPTIONS") {
add_header 'Access-Control-Allow-Credentials' 'true';
add_header 'Access-Control-Allow-Headers' 'Content-Type';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, DELETE';
add_header 'Access-Control-Allow-Origin' $http_origin;
}
Good read on CORS: https://www.html5rocks.com/en/tutorials/cors/
I am trying to make an AJAX request to download a JSON file from S3 using the following code:
$.ajax({
'type':'GET',
'url':this.baseUrl + publicationId + "/document.json",
'crossDomain': true,
'dataType':'json',
'success':function(data) {
console.log('got document.json');
}
});
This works fine in Firefox, and it works if the baseUrl is http ://<s3-url>, but if it is https://, then Chrome cancels the request with the error:
XMLHttpRequest cannot load
https ://s3.amazonaws.com/<bucket-name>/<pub-id>/document.json. No
'Access-Control-Allow-Origin' header is present on the requested
resource. Origin 'https ://localhost.office.lucidpress.com' is
therefore not allowed access.
Making the same request with CURL does have the following header: "Access-Control-Allow-Origin: *".
Why doesn't this work for Chrome, and is there any workaround?
Note: I put spaces in the URLs to prevent them from becoming links.
Chrome will send a pre-flight OPTIONS request to see if the GET can be performed. What headers do you receive if you curl -X OPTIONS -i https://s3.amazonaws.com/<bucket-name>/<pub-id>/document.json? You most likely need to add Access-Control-Allow-Headers to your server response to also allow any extra headers Chrome may be sending on the OPTIONS request, i.e. Access-Control-Allow-Headers: Origin, X-Requested-With, Content-Type, Accept
Alternatively, as this is a https request, your answer may be Why Chrome cancel CORS OPTION request