Catching 'Origin is not allowed by Access-Control-Allow-Origin' error
In browsers supporting crossOrigin requests, (which should be preferred ones), if you set the crossOrigin
to 'anonymous'
, and try to set your element's src
pointing to a file hosted on a improperly set-up server, the load
event won't fire, and instead, an error
event will.
It is important to understand that in the case of a failed crossOrigin request, the server will answer directly that it doesn't accept the request, so only headers are sent between your user and the distant server, while doing the other way around (first try without the crossOrigin request, then try with), you have to first download entirely* the resource , then download it again with the crossOrigin attribute set...
Same applies for audio, video, and xhr requests.
So one should first set the crossOrigin of cross-origin requests, then if it fails it means that the other hand is not properly configured.
var img = new Image();
if('crossOrigin' in img){
// an up to date browser
// make a single crossOrigin request
img.crossOrigin = 'anonymous';
img.onerror = handleCORSFailure;
}
else{
// for browser that don't support the crossOrigin request
var ctx = document.createElement('canvas').getContext('2d');
ctx.width = ctx.height = 1; // no need to use too much memory, 1*1 px is enough
img.addEventListener('load', function(){
ctx.drawImage(this,0,0);
try{
ctx.getImageData(0,0,1,1);
}
catch(e){
handleCORSFailure(e);
return;
}
});
}
img.src = 'https://i.stack.imgur.com/Kt3vI.png?s=48&g=1';
function handleCORSFailure(e){
if(e.target){
console.log('server not set correctly');
}
else{
console.log("browser doesn't support crossOrigin requests");
}
}
-* Actually, only images need to be downloaded entirely, all other resources can be tested before the end.
Ps : in case of same-origin request, the crossOrigin attribute should not hurt, so this check can still be performed.
As @TamasHegedus commented, the image can still be loaded with the CORS error, but it doesn't allow the image data to be manipulated. That means you can use the canvas to try to manipulate the image and catch any thrown errors.
This technique would work for canvas-supported images. See @Kaiido's answer if you want a simpler alternative using the Image#crossOrigin
property. His solution also detects whether the property is supported and uses canvas when necessary.
// Must work in IE9+.
var img = new Image;
img.onload = function() {
var canvas = document.createElement('canvas');
// resize the canvas, else img won't be rendered
canvas.width = img.width;
canvas.height = img.height;
// get the canvas rendering context 2d
var ctx = canvas.getContext('2d');
// draw the image first
ctx.drawImage(img, 0, 0);
try {
/* get first pixel */
ctx.getImageData(0, 0, 1, 1);
/* no error catched – no cors error */
alert("Cross-domain access available.");
} catch (e) {
alert("Cross-domain access blocked.");
}
};
img.src = 'https://i.stack.imgur.com/Kt3vI.png?s=48&g=1';