In the browser's console window, I see one of the below messages:
- Failed to load ... No 'Access-Control-Allow-Origin' header
- Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource ... Reason: CORS header 'Access-Control-Allow-Origin' missing
- Failed to load ... Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header present on the requested resource. Origin ... is therefore not allowed access
One of the keywords usually mentioned in messages is
A browser-based web application possibly an Apps framework app is attempting to make a cross-origin call to get a shared resource from an external web service. This is known as a CORS request (Cross-Origin-Resource-Sharing).
There is a browser-based CORS standard that manages such cross-origin calls. When certain conditions are not met, errors such as the above happen.
This isn't necessarily a bug because it may relate to a use case that is intentionally not allowed by the user's web application and remote external service.
When one origin (www.origin1.com) calls another origin (www.origin2.com), that is called a cross-origin request. Certain conditions have to be in place for this request to work. The external service being called (www.origin2.com) needs to return the HTTP header
Access-Control-Allow-Origin in its response.
If the external service does not return this header, then the browser's adherence to the CORS specification stops the request and one of the above errors is returned.
Ask these questions
- What is the starting point URL of the call (or the origin)? This sometimes is in the error message itself.
- What is the external service's URL being called? This is sometimes in the console's error message.
- What is being retrieved and why? Is it a PNG file? A script, CSS, or font file? What exactly is being retrieved and what is it being used for? This can give some insights into the use case and why this remote location's asset is important.
- Does this external resource require authentication to get? If a redirect is required, the
Access-Control-Allow-Originresponse header may not be returned and the call will fail. Copy the resource's URL directly into a new incognito browser tab. This can be a good test of whether it can be accessed under general circumstances but is not a guarantee that it will work in the web app's code.
- Can you see OPTIONS HTTP method call in the browser's Network tab? When custom request headers, authentication, or other conditions exist in the cross-origin request, the browser makes an additional HTTP call. This is also called a preflight call. The web app's code doesn't explicitly make it. The browser in the background creates and makes it as it's part of the CORS specification standard.
When this OPTIONS call is made, certain values need to be in this call's response for it to succeed and for the actual HTTP call for the resource to happen. If the OPTIONS call fails, then the resource is not retrieved and a CORS error should appear in the browser's console.
Note down if you see an OPTIONS call. Also, note down if you see a redirect (status 302) call happening right before the OPTIONS call.
If a redirect is happening on an OPTIONS call, then the OPTIONS call will most likely fail. This means the call to get the resource will also fail and trigger a CORS error.
- What is the use case for making getting external resource? Find out why this external resource is being retrieved in the first place. This may be important in coming up with workarounds or needed changes.
- Generate a HAR file. Getting a snapshot of the failed call and what happened immediately before and after may help debug the issue and avoid the user from having to reproduce it. Headers in requests and responses can be examined, along with identifying OPTIONS calls and redirects.
Possible next steps
- Who owns the external server? Perhaps the server can be modified to adhere to the CORS specification standard to return the
Access-Control-Allow-Originheader. However, even if the server is something controlled in-house, this isn't necessarily a cure-all. There may be good reasons that a particular external service does not want to share a resource.
If the external server isn't something controlled in-house, then maybe work with that vendor or come up with another workaround, assuming the use-case is considered valid.
- Is the app written using Zendesk Apps framework? A back-end proxy server is available via the client.request() call. Use this proxy by setting cors:false in the client.request's settings. Note that "false" is also the setting's default value. Because the proxy service is a back-end service, it doesn't need to adhere to the browser-based CORS specification, so the cross-origin call may possibly succeed using the proxy.
This also isn't always a cure-all. The proxy service does not support retrieving binary files or binary information from external services. There may be other app-specific reasons this is not a solution, too.
- Can the resource be embedded directly into the web app? Instead of going cross-origin to get a resource, consider including it with the web app. This avoids the cross-origin call completely (as it's now a local resource) and whatever CORS issues may go away. This isn't always a fix, however. Sometimes external resource URLs are not known ahead of time, or the resource is too large to fit as a local resource, or the resource changes too often to download it as a local static resource.
- What is the browser version? Despite the CORS specification being a standard, the error messages returned by browsers can be different. Chrome returns different console messages than Firefox.
- Sometimes there is no fix. Sometimes a resource request from an external service is not meant to be shared in the context of a browser web app. The resource owner decides if it shares the resource or not. It's not up to the web app. This might be as designed.
For more information, see these resources: