I have a site that is loading image tiles from openstreetmap.
These images are served out from a number of sub-domains of openstreetmap.org, such as:
a.tile.openstreetmap.org
b.tile.openstreetmap.org
c.tile.openstreetmap.org
To allow this I added an img-src to my Content-Security-Policy *.openstreetmap.orgso that all subdomains should be valid.
The full img-src now reads:
img-src https: data: *.openstreetmap.org;
And for the sake of completeness the full Content-Security-Policy is:
default-src 'self'; script-src https: 'unsafe-inline' 'unsafe-eval'; style-src https: 'unsafe-inline'; img-src https: data: *.openstreetmap.org;
This seems to work as intended when the site first loads, and the page containing the maps is navigated to. There are actually multiple pages containing maps and both work.
However upon the user hitting "Refresh" the following errors occur:
Refused to connect to 'https://a.tile.openstreetmap.org/12/2011/1356.png' because it violates the document's Content Security Policy.
GET https://a.tile.openstreetmap.org/12/2010/1355.png net::ERR_FAILED
If I open Chrome dev tools and use the "Right Click on Refresh" -> "Empty Cache and Hard Reload" option to reload the page, then the image tiles load again, but only once subsequent "normal" refreshes produce the same error.
This site is using a 'serviceworker.js' and the locally servered pages are cached, but the tiles from openstreetmap.org are not. I have however manually emptied this cache, and verified that the cached versions have the correct Content-Security-Policy, so maybe the existence of the serviceworker.js is a misnomer.
EDIT 1
Posting the question made me investigate the serviceworker.js further. I removed it, and the issue was no longer present. Reinstating the service worker brought the issue back. So that is the cause.
There is nothing from openstreetmap.org in the Application cache.
EDIT 2
Further investigation has tracked down the cause, but has got me no closer to the solution. The first load works as my 'serviceworker.js' is operating on a cache-first, then network basis. So the first load theres no cache. Subsequent loads there is a cache, but the images from openstreetmap.org aren't in it.
The relevant section is:
self.addEventListener('fetch', function (event) {
event.respondWith(
// cache first then network
// but not for different origin calls, this includes API
caches.open("my-cache-" + self.sw_version)
.catch((msg) => { console.log("caches.open(): ", msg); })
.then(cache => {
return cache.match(event.request)
.catch((msg) => { console.log("cache.match(): ", msg); })
.then(response => {
return response || fetch(event.request)
.catch((msg) => { console.log("fetch(event.request): ", msg); })
.then(response => {
// if it was from a differnt origin - simply return it
if (!event.request.url.match(event.request.referrer))
return response;
console.log("ServiceWorker.caching: ", event.request.url);
cache.put(event.request, response.clone());
return response;
});
});
})
);
});
Which has identified the problem as being when the service worker makes the fetch() request after not finding it in the cache.
fetch(event.request): TypeError: Failed to fetch
Which implies that the fetch() from within the serviceworker.js isn't using the correct Content-Security-Policy, but is using one that the request violates..
The network tab of the dev tools, is showing this for the request / response..
Request
Request URL: https://b.tile.openstreetmap.org/12/2011/1355.png
Referrer Policy: no-referrer-when-downgrade
Response
Origin: https://localhost:9443
Referer: https://localhost:9443/my-app/?
Sec-Fetch-Mode: cors
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.132 Safari/537.36
Edit 3
Forcing the fetch() to use the same Content-Security-Policy by explicitly setting it allowed the request to work. But this is a hackey fix, as its now declared in two places (the website as a whole, and in the serviceworker.js) so if there is a "proper" fix, or even just an explanation as to why this is required, I would still be interested.
Edit 4
I've come back to this today to have another stab.
I realized that there was an issue with my checking for remote urls - I was passing the referrer URL to the match function unescaped so it wasn't behaving as expected. I've corrected that now, and moved some code around to make it clearer (to myself).
self.addEventListener('fetch', function (event) {
// https://stackoverflow.com/a/6969486 - escape a string to use in regex
if (!event.request.url.match(event.request.referrer.replace(/[-[\]/{}()*+?.\\^$|]/g, '\\$&'))) {
fetch(event.request)
.catch((msg) => { console.log("fetch(event.request).remoteorigin: ", { event: event, error: msg }); })
.then(response => {
return response;
});
}
else {
event.respondWith(
// cache first then network
// but not for different origin calls, this includes API
caches.open("crt-scada-" + self.sw_version)
.catch((msg) => { console.log("caches.open(): ", msg); })
.then(cache => {
return cache.match(event.request)
.catch((msg) => { console.log("cache.match(): ", msg); })
.then(response => {
return response || fetch(event.request)
.catch((msg) => { console.log("fetch(event.request).sameorigin: ", { event: event, error: msg }); })
.then(response => {
cache.put(event.request, response.clone());
return response;
});
});
})
);
}
});
Now I have the weird situation where it reports the same error - but also loads the tiles from openstreetmap.
Any advice on why this might be happening would be gratefully accepted.
Related
I build an app use vue and codeigniter, but I have a problem when I try to get api, I got this error on console
Access to XMLHttpRequest at 'http://localhost:8888/project/login'
from origin 'http://localhost:8080' has been blocked by CORS policy:
Request header field access-control-allow-origin is not allowed
by Access-Control-Allow-Headers in preflight response.
I have been try like this on front-end (main.js)
axios.defaults.headers.common['Content-Type'] = 'application/x-www-form-urlencoded'
axios.defaults.headers.common['Access-Control-Allow-Origin'] = '*';
and this on backend (controller)
header('Access-Control-Allow-Origin: *');
header("Access-Control-Allow-Methods: GET, POST, OPTIONS, PUT, DELETE");
and vue login method
this.axios.post('http://localhost:8888/project/login', this.data, {
headers: {
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Methods": "GET, POST, PATCH, PUT, DELETE, OPTIONS",
"Access-Control-Allow-Headers": "Origin, Content-Type, X-Auth-Token"
}
}).then(res => {
console.log(res);
}).catch(err => {
console.log(err.response);
});
I've searched and tried in stackoverflow but does not work, how I can solve it? thank you so much for your help
CORS is the server telling the client what kind of HTTP requests the client is allowed to make. Anytime you see a Access-Control-Allow-* header, those should be sent by the server, NOT the client. The server is "allowing" the client to send certain headers. It doesn't make sense for the client to give itself permission. So remove these headers from your frontend code.
axios.defaults.headers.common['Access-Control-Allow-Origin'] = '*';
this.axios.post('http://localhost:8888/project/login', this.data, {
headers: {
// remove headers
}
}).then(res => {
console.log(res);
}).catch(err => {
console.log(err.response);
});
For example, imagine your backend set this cors header.
header("Access-Control-Allow-Methods: GET");
That means a client from a different origin is only allowed to send GET requests, so axios.get would work, axios.post would fail, axios.delete would fail, etc.
This may occur you are trying call another host for ex- You Vue app is running on localhost:8080 but your backend API is running on http://localhost:8888
In this situation axios request looking for this localhost:8080/project/login instead of this http://localhost:8888/project/login
To solve this issue you need to create proxy in your vue app
Follow this instruction Create js file vue.config.js or webpack.config.js if you haven't it yet inside root folder
then include below
module.exports = {
devServer: {
proxy: 'https://localhost:8888'
} }
If you need multiple backends use below
module.exports = {
devServer: {
proxy: {
'/V1': {
target: 'http://localhost:8888',
changeOrigin: true,
pathRewrite: {
'^/V1': ''
}
},
'/V2': {
target: 'https://loclhost:4437',
changeOrigin: true,
pathRewrite: {
'^/V2': ''
}
},
}
}
If you select the second one in front of the end point use the V1 or V2
ex - your end point is /project/login before it use V1/project/login or V2/project/login
as per the host
Check this Vue project - https://github.com/ashanoulu/helsinki_city_bike_app/tree/main/Front_End/app-view
Version - Vue3
For more details visit - Vue official documentation
in my case
curl && postman works but not vue axios.post
Access to XMLHttpRequest at 'http://%%%%:9200/lead/_search' from origin 'http://%%%%.local' has been blocked by CORS policy: Request header field access-control-allow-origin is not allowed by Access-Control-Allow-Headers in preflight response.
So, the issue is on vue side not the server!
The server response contains "access-control-allow-origin: *" header
I had the same problem even everything was fine on the server side..
The solution to the problem was that API link I hit was missing the slash (/) at the end so that produced CORS error.
in my case adding this in my php backend API function it worked
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: GET, PUT, POST, DELETE, OPTIONS, post, get');
header("Access-Control-Max-Age", "3600");
header('Access-Control-Allow-Headers: Origin, Content-Type, X-Auth-Token');
header("Access-Control-Allow-Credentials", "true");
You may try :
At the backend,
npm install cors
then, at the backend app.js , add the following,
const cors = require('cors')
app.use(cors({
origin: ['http://localhost:8082'],
}))
Hopefully, It may help.
Dev Proxy is your solution
With DevProxy you define a specific path, or a wildcard (non static) that Node (the server runs vue-cli dev server) will route traffic to.
Once defined (a single entry in vue.config.js), you call your api with the same URI as your UI (same host and port) and Vue is redirecting the request to the API server while providing the proper CORS headers.
look more at https://cli.vuejs.org/config/#devserver-proxy
I'm building an app in Vue.js and added global headers in the main.js file
Example:
axios.defaults.headers.get['header-name'] = 'value'
For handling CORS issues you may now have to make changes on the client side, it is not just a server issue.
Chrome has a few plugins: https://chrome.google.com/webstore/search/cors?hl=en
for some cases, it is not vue issue. sometimes it's back-end issue.. in my case I've made API in nest JS, and I didn't enable CORS = true.. That's why I am getting CORS policy error.
in my case, the API would return CORS policy, but the problem lied with my url.
my calls were like "https://api.com//call", that extra slash was causing the problem.
changing the url to "https://api.com/call" fixed the error.
I am developing a Chrome extension which makes requests from certain websites to an API I control. Until Chrome 73, the extension worked correctly. After upgrading to Chrome 73, I started getting the following error:
Cross-Origin Read Blocking (CORB) blocked cross origin response
http://localhost:3000/api/users/1 with MIME type application/json
According to Chrome's documentation on CORB, CORB will block the response of a request if all of the following are true:
The resource is a "data resource". Specifically, the content type is HTML, XML, JSON
The server responds with an X-Content-Type-Options: nosniff header, or if this header is omitted, Chrome detects the content type is one of HTML, XML, or JSON from inspecting the file
CORS does not explicitly allow access to the resource
Also, according to "Lessons from Spectre and Meltdown" (Google I/O 2018), it seems like it may be important to add mode: cors to fetch invocations, i.e., fetch(url, { mode: 'cors' }).
To try to fix this, I made the following changes:
First, I added the following headers to all responses from my API:
Access-Control-Allow-Credentials: true
Access-Control-Allow-Headers: Content-Type
Access-Control-Allow-Methods: GET, POST
Access-Control-Allow-Origin: https://www.example.com
Second, I updated my fetch() invocation on the extension to look like this:
fetch(url, { credentials: 'include', mode: 'cors' })
However, these changes didn't work. What can I change to make my request not be blocked by CORB?
Based on the examples in "Changes to Cross-Origin Requests in Chrome Extension Content Scripts", I replaced all invocations of fetch with a new method fetchResource, that has a similar API, but delegates the fetch call to the background page:
// contentScript.js
function fetchResource(input, init) {
return new Promise((resolve, reject) => {
chrome.runtime.sendMessage({input, init}, messageResponse => {
const [response, error] = messageResponse;
if (response === null) {
reject(error);
} else {
// Use undefined on a 204 - No Content
const body = response.body ? new Blob([response.body]) : undefined;
resolve(new Response(body, {
status: response.status,
statusText: response.statusText,
}));
}
});
});
}
// background.js
chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
fetch(request.input, request.init).then(function(response) {
return response.text().then(function(text) {
sendResponse([{
body: text,
status: response.status,
statusText: response.statusText,
}, null]);
});
}, function(error) {
sendResponse([null, error]);
});
return true;
});
This is the smallest set of changes I was able to make to my app that fixes the issue. (Note, extensions and background pages can only pass JSON-serializable objects between them, so we cannot simply pass the Fetch API Response object from the background page to the extension.)
Background pages are not affected by CORS or CORB, so the browser no longer blocks the responses from the API.
See https://www.chromium.org/Home/chromium-security/extension-content-script-fetches
To improve security, cross-origin fetches from content scripts are disallowed in Chrome Extensions since Chrome 85. Such requests can be made from extension background script instead, and relayed to content scripts when needed.
You can do that to avoid Cross-Origin.
Old content script, making a cross-origin fetch:
var itemId = 12345;
var url = "https://another-site.com/price-query?itemId=" +
encodeURIComponent(request.itemId);
fetch(url)
.then(response => response.text())
.then(text => parsePrice(text))
.then(price => ...)
.catch(error => ...)
New content script, asking its background page to fetch the data instead:
chrome.runtime.sendMessage(
{contentScriptQuery: "queryPrice", itemId: 12345},
price => ...);
New extension background page, fetching from a known URL and relaying data:
chrome.runtime.onMessage.addListener(
function(request, sender, sendResponse) {
if (request.contentScriptQuery == "queryPrice") {
var url = "https://another-site.com/price-query?itemId=" +
encodeURIComponent(request.itemId);
fetch(url)
.then(response => response.text())
.then(text => parsePrice(text))
.then(price => sendResponse(price))
.catch(error => ...)
return true; // Will respond asynchronously.
}
});
Allow the URL in manifest.json (more info):
ManifestV2 (classic): "permissions": ["https://another-site.com/"]
ManifestV3 (upcoming): "host_permissions": ["https://another-site.com/"]
Temporary solution: disable CORB with run command browser
--disable-features=CrossSiteDocumentBlockingAlways,CrossSiteDocumentBlockingIfIsolating
Example run command on Linux.
For Chrome:
chrome %U --disable-features=CrossSiteDocumentBlockingAlways,CrossSiteDocumentBlockingIfIsolating
For Chromium:
chromium-browser %U --disable-features=CrossSiteDocumentBlockingAlways,CrossSiteDocumentBlockingIfIsolating
Similar question.
Source.
I'm using js fetch API to retreive datas from JSON.
It's working fine (even in IE 11), except in Edge 17 i get 302, the response header are :
Content-length : 0
Content-type: text/plain; charset=utf-8
Location: http://local.mysite.com/xxx
My local website is on a Mac, i'm using BrowserSync to make it reachable via 192.168.100.X:3000
Then i've updated my PC hosts file like this :
192.168.100.X http://local.mysite.com
Here's my fetch call :
fetch('/fr/fil-actualites-json', { mode: 'cors' })
.then(
function(response) {
console.log('code :' +response.status);
if (response.status !== 200) {
console.log('Looks like there was a problem. Status Code: ' +
response.status);
return;
}
// Examine the text in the response
response.json().then(function(data) {
// do some stuff
});
}
)
.catch(function(err) {
console.log('Fetch Error :-S', err);
});
Thx for your help ;)
Safari was throwing this error :
unhandled promise rejection syntaxerror the string did not match the expected pattern
I found the answer:
The default value for credentials is "same-origin".
The default for credentials wasn't always the same, though. The
following versions of browsers implemented an older version of the
fetch specification where the default was "omit":
Firefox 39-60 Chrome 42-67 Safari 10.1-11.1.2 If you target these
browsers, it's advisable to always specify credentials: 'same-origin'
explicitly with all fetch requests instead of relying on the default:
fetch('/users', {
credentials: 'same-origin'
})
I'm leaning Angular, trying to use HTTP get to a json file on localhost url http://api.test/work.but I get the error bellow:
Failed to load http://api.test/work: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:4200' is therefore not allowed access.
by the way I use laravel to generate json
This means that, http://api.test/work.but does not allow cross origin request. If this link is hosted by you, you will have to allow cross origin request.
In your response headers, you will have to send:
Access-Control-Allow-Origin: *
This means, any body request to your server.
If you want to allow access to specific domains, you can specify them.
Read more abouut CORS here: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS
I was getting the same error; "Failed to load http://web-api_link: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:4200' is therefore not allowed access.". I already had CORS installed on my chrome browser. It was on. I clicked on it and checked for "Enable cross-origin resource sharing". I disabled it, and enabled it again. It worked for me!
private callServiceGet(url: string, wC?: boolean): Promise<any> {
return this.http.get<any>(url, { observe: 'response', withCredentials: wC })
.catch(err => [ err ])
.map(data => ({ status: data.status, data: data.body }))
.toPromise();
}
with wC you have credentials to allow localhost too. It's simply a boolean, so set it to true if you want a complete access.
for example you could use it like this:
getJSON(): Promise<any> {
return this.callServiceGet(`http://api.test/work`, true);
}
I am using HTML5 fetch API.
var request = new Request('https://davidwalsh.name/demo/arsenal.json');
fetch(request).then(function(response) {
// Convert to JSON
return response.json();
}).then(function(j) {
// Yay, `j` is a JavaScript object
console.log(JSON.stringify(j));
}).catch(function(error) {
console.log('Request failed', error)
});
I am able to use normal json but unable to fetch the data of above api url.
It throws error:
Fetch API cannot load https://davidwalsh.name/demo/arsenal.json. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost' is therefore not allowed access. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
Like epascarello said, the server that hosts the resource needs to have CORS enabled. What you can do on the client side (and probably what you are thinking of) is set the mode of fetch to CORS (although this is the default setting I believe):
fetch(request, {mode: 'cors'});
However this still requires the server to enable CORS as well, and allow your domain to request the resource.
Check out the CORS documentation, and this awesome Udacity video explaining the Same Origin Policy.
You can also use no-cors mode on the client side, but this will just give you an opaque response (you can't read the body, but the response can still be cached by a service worker or consumed by some API's, like <img>):
fetch(request, {mode: 'no-cors'})
.then(function(response) {
console.log(response);
}).catch(function(error) {
console.log('Request failed', error)
});
This worked for me :
npm install -g local-cors-proxy
API endpoint that we want to request that has CORS issues:
https://www.yourdomain.com/test/list
Start Proxy:
lcp --proxyUrl https://www.yourdomain.com
Proxy Active
Proxy Url: http://www.yourdomain.com:28080
Proxy Partial: proxy
PORT: 8010
Then in your client code, new API endpoint:
http://localhost:8010/proxy/test/list
End result will be a request to https://www.yourdomain.ie/test/list without the CORS issues!
Solution to resolve issue in Local env's
I had my front-end code running in http://localhost:3000 and my API(Backend code) running at http://localhost:5000
Was using fetch API to call the API. Initially, it was throwing "cors" error.
Then added this below code in my Backend API code, allowing origin and header from anywhere.
let allowCrossDomain = function(req, res, next) {
res.header('Access-Control-Allow-Origin', "*");
res.header('Access-Control-Allow-Headers', "*");
next();
}
app.use(allowCrossDomain);
However you must restrict origins in case of other environments like stage, prod.
Strictly NO for higher environments.
I know this is an older post, but I found what worked for me to fix this error was using the IP address of my server instead of using the domain name within my fetch request.
So for example:
#(original) var request = new Request('https://davidwalsh.name/demo/arsenal.json');
#use IP instead
var request = new Request('https://0.0.0.0/demo/arsenal.json');
fetch(request).then(function(response) {
// Convert to JSON
return response.json();
}).then(function(j) {
// Yay, `j` is a JavaScript object
console.log(JSON.stringify(j));
}).catch(function(error) {
console.log('Request failed', error)
});
You need to set cors header on server side where you are requesting data from.
For example if your backend server is in Ruby on rails, use following code before sending back response. Same headers should be set for any backend server.
headers['Access-Control-Allow-Origin'] = '*'
headers['Access-Control-Allow-Methods'] = 'POST, PUT, DELETE, GET, OPTIONS'
headers['Access-Control-Request-Method'] = '*'
headers['Access-Control-Allow-Headers'] = 'Origin, X-Requested-With, Content-Type, Accept, Authorization'
If you are use nginx try this
#Control-Allow-Origin access
# Authorization headers aren't passed in CORS preflight (OPTIONS) calls. Always return a 200 for options.
add_header Access-Control-Allow-Credentials "true" always;
add_header Access-Control-Allow-Origin "https://URL-WHERE-ORIGIN-FROM-HERE " always;
add_header Access-Control-Allow-Methods "GET,OPTIONS" always;
add_header Access-Control-Allow-Headers "x-csrf-token,authorization,content-type,accept,origin,x-requested-with,access-control-allow-origin" always;
if ($request_method = OPTIONS ) {
return 200;
}
Look at https://expressjs.com/en/resources/middleware/cors.html
You have to use cors.
Install:
$ npm install cors
const cors = require('cors');
app.use(cors());
You have to put this code in your node server.