CORS issues for DatoCMS Plugins
Introduction: What is CORS?
If you're not already familiar with Cross-Origin Resource Sharing (CORS), we recommend reading the following explainers first:
MDN: Cross-Origin Resource Sharing for a basic technical background
"How to win at CORS" by Jake Archibald for a longer history of CORS and why it exists
In brief, CORS is a browser-side security mechanism that lets a domain declare which other domains are allowed to make clientside API calls to itself. For example, my-cors-protected-domain.com can tell browsers that only official-partner-site.com can make clientside API calls to it, while rejecting all other attempts.
In context for you as a plugin author, this means:
Your plugin would normally be hosted on
plugins-cdn.datocms.com(if you published it to the DatoCMS Marketplace)If you try to make a clientside browser
fetch()toanother-domain.com, the other domain gets to decide whether this is allowed. If they say no (or don't return a CORS header at all), the browser will fail your request with a CORS error
As a plugin author, when do I need to worry about CORS?
In the context of plugin authoring, CORS is only an issue when all the following criteria are met:
Your plugin lives on one domain (like our default
plugins-cdn.datocms.com) but needs to make an API call to another domain, like a third-party translation provider, an LLM API, etc.The other API endpoint does not natively support CORS, or does not allow our originating domain (
plugins-cdn.datocms.com) in its CORS setup.The API call is made via a clientside JavaScript
fetch()orXMLHttpRequest
Only when ALL of these conditions are true will you need to work around CORS.
When CORS is NOT an issue
You do NOT have to worry about CORS if your plugin:
Is only making calls to DatoCMS endpoints (like the Content Management API endpoints). We handle CORS for you when a plugin makes calls to other Dato-owned endpoints.
Is proxying its requests serverside (such as via a Next API route, serverless/lambda function, or your own backend). CORS only affects cross-origin browser (clientside) requests, not server-to-server.
Is making requests to the same origin (scheme + domain + port) it's hosted on. This is most common for private, internal-use plugins, such as one hosted on
www.your-domain.com/pluginand making API calls towww.your-domain.com/api. Note that different subdomains (likeplugin.your-domain.com) DO count as cross-origin traffic, and ARE subject to CORS rules. Different ports have the same problem (your-domain.com:443is a different origin thanyour-domain.com:1234). In those cases, see below for solutions.
How do I fix CORS issues with my plugin?
Scenario 1: Add CORS headers to an API endpoint under your control
If you control the destination endpoint (i.e., it's your own backend API you want to fetch from), then the proper solution is to add the correct CORS headers to that endpoint so that it will allow fetch() from plugins-cdn.datocms.com.
If you're not sure how to add the correct CORS headers, these resources may help (or just ask your favorite LLM):
"Will it CORS?", an interactive wizard that walks you through CORS troubleshooting step-by-step
Scenario 2: Use a serverside CORS proxy for third-party endpoints
(We also provide an official proxy for DatoCMS plugins! See below.)
If you do not control the destination endpoint (it's a third-party API), you will need to create a workaround, usually via a serverside proxy that injects the proper CORS headers into the third-party API's response before returning it to the browser.
You can either make your own (see Cloudflare Workers CORS proxy example), or use the proxy DatoCMS provides specifically for this use, below.
Your own proxy doesn't need be on Cloudflare or serverless or any particular server. CORS is enforced only by web browsers, so any server-side environment (Node.js, a serverless function, your existing backend) can make the request on your plugin's behalf. The proxy just has to do two things: forward the request to the target API, and return the response with its own CORS headers injected. The Cloudflare Workers example is just to show you the basic concept. Any modern LLM can write a CORS proxy for you in the environment of your choice.
DatoCMS CORS Proxy for Plugins
For plugin authors running into CORS issues, we provide an official proxy to help bypass them. This is similar in spirit to the Cloudflare Workers proxy example, above, but tweaked specifically for the needs of DatoCMS plugins.
How to use
Usage is simple: Replace your existing fetch() calls to the third-party endpoint with our proxy URL, cors-proxy.datocms.com, and pass it the original target URL as the url search param, URL-encoded.
It is just an HTTP proxy. There's nothing for you to download or install, just a simple rewrite in your code.
Example
// The API you're actually trying to reachconst targetUrl = 'https://www.third-party.com/api';
// But instead of calling it directly, route it through our proxy// The `url` param must be URL-encoded, since the target URL contains// characters (:, /, ?) that would otherwise break the proxy URL.const response = await fetch( `https://cors-proxy.datocms.com/?url=${encodeURIComponent(targetUrl)}`,);
// Continue processing the response, e.g.// const data = await response.json()// doSomethingWith(data);This is a simple proxy that sits between your plugin and the destination API endpoint. Its sole job is to inject the necessary CORS headers into the third-party API response before returning it to the browser, thereby fooling the browser and bypassing the CORS errors.
Limitations
Our official proxy has some limitations you should be aware of:
Only Marketplace-hosted plugins on
plugins-cdn.datocms.comandlocalhost(any port) are allowed. If your plugin is hosted internally or on another domain, our proxy will not work.Supported request headers (that you send): Only
Content-TypeandAuthorization. Other headers will cause an error.Supported HTTP methods: Only
GET,POST,PUT,HEAD,DELETE, andOPTIONS.PATCHand other methods are not supported and will cause an error.No SLA, performance, or uptime guarantee: The proxy is provided as-is, on a best-effort basis. We make no guarantee as to its availability or performance at any time, regardless of your plan or other agreements with us. It is not considered a part of your regular service level agreement (SLA), if you have one. That said, it's usually stable and rarely has issues.
If you need functionality not supported by our official proxy, you should write your own (see the Cloudflare Workers example in Scenario 2, above).
Privacy
When you use the CORS proxy, your request will go through DatoCMS servers and our upstream providers. We may log some information for debugging purposes (such as the target URL); such logs are only used for troubleshooting and to fix issues with the CORS proxy. However, we don't take any special effort to scrub any information that may be in these logs.
We recommend only using our proxy for non-sensitive data — any trade secrets, personally identifiable information, or other sensitive information should not go through this proxy.
The destination API will see the request as coming from our IP address instead of yours.