GuidesWIX

Wix CORS errors and external APIs

On Wix, fetch() in page code runs in the visitor's browser with your site's origin, and code in an Embed HTML element ran in an iframe on a separate filesusr.com origin in our October 2026 tests, so both fail against APIs that send no CORS headers. The native fix is a backend web module: wix-fetch runs on Wix's servers, where CORS does not apply and secrets stay out of the page. For code you paste into an embed or Custom Code, prefix public GET requests with https://proxy.cors.dev/ (free, about 600 requests a minute per IP); POST and Authorization need cors.dev Pro at $5 a month for 500,000 requests.

Where your code runs on Wix

Wix runs custom code in four places: page code (Velo frontend code), backend code (web modules), the Embed HTML element, and Custom Code added in the site's Settings. Wix's wix-fetch docs: "In frontend code, the browser's native Fetch API is used. In backend code, the node-fetch module is used." Calling an API from backend code "avoids CORS issues that can occur when sending some requests from the frontend".

Page code runs in the visitor's browser. On two live Wix sites checked on 28 October 2026 with a headless browser, page code ran in a web worker loaded from the site's own domain, so its requests carried the site's origin, such as https://www.wix.com or https://gaweb1.wixsite.com. Custom Code needs a published site with a connected domain and runs in the page itself, with the same origin.

Where Wix runs your code, checked October 2026
PlaceRuns inOrigin an API seesCORS applies
Page codeVisitor's browser, in a web workerYour site, such as https://www.yourdomain.comYes
Backend web moduleWix serversNone, it is a server requestNo
Embed HTML elementSandboxed iframeA filesusr.com host, such as https://www-wix-com.filesusr.comYes
Custom Code in SettingsVisitor's browser, in the pageYour connected domainYes

The native fix: a backend web module

If you write Velo page code, move the API call into a backend web module. Wix documents web modules as the way to avoid CORS restrictions and says a request blocked by CORS in frontend code should move to backend code. Backend code is also where a secret key stays private, and it can send POST requests to any API without a proxy.

  1. Create the module

    Add a file ending in .web.js to the backend. Wrap each exported function in webMethod() with a permission such as Permissions.Anyone, both imported from wix-web-module (in the Wix SDK, @wix/web-methods).

  2. Call the API

    getJSON() from wix-fetch fetches a URL and parses the JSON in one call, on Wix's servers.

  3. Import it in page code

    import { getPlayerCount } from 'backend/steam.web' gives page code a function that Wix runs on the server.

backend/steam.web.js: the API call runs on Wix's servers
// backend/steam.web.js
import { Permissions, webMethod } from 'wix-web-module';
import { getJSON } from 'wix-fetch';

const PLAYERS_URL = 'https://api.steampowered.com/ISteamUserStats/GetNumberOfCurrentPlayers/v1/';

export const getPlayerCount = webMethod(Permissions.Anyone, async (appId) => {
  const data = await getJSON(`${PLAYERS_URL}?appid=${encodeURIComponent(appId)}`);
  return data.response.player_count;
});

Page code then awaits the web method like any async function and writes the result into an element.

Page code: call the web method and show the result
// Page code on the page that shows #playerCount
import { getPlayerCount } from 'backend/steam.web';

$w.onReady(async () => {
  const players = await getPlayerCount(730);
  $w('#playerCount').text = `${players.toLocaleString()} playing now`;
});

Keep API keys in the Wix Secrets Manager and read them in backend code with secrets.getSecretValue() from wix-secrets-backend.v2, wrapped in elevate() from wix-auth; it replaces the deprecated getSecret(). Backend code never ships to the browser, so a secret read there stays private. cors.dev cannot do that: anything in page code or an embed is visible to visitors.

Embed HTML runs on a filesusr.com origin

Wix's docs say the HTML element "creates a sandboxed environment (an iframe) that doesn't have direct access to the other elements on your page". Page code and the iframe exchange data through postMessage and the element's onMessage handler.

On 28 October 2026 the Embed HTML iframe on https://www.wix.com/velo-examples/hello-html-comp loaded from https://www-wix-com.filesusr.com, with a sandbox attribute that includes allow-same-origin and allow-scripts. On https://gaweb1.wixsite.com the iframe origin was https://gaweb1-wixsite-com.filesusr.com. On both sites the host was the site's host name with dots replaced by hyphens, followed by .filesusr.com. Requests from code inside the element carry that origin, which an API almost never allows.

The Steam player count API (https://api.steampowered.com/ISteamUserStats/GetNumberOfCurrentPlayers/v1/?appid=730) sends no CORS header. Through https://proxy.cors.dev/ it answered 200 with Access-Control-Allow-Origin echoing https://www-wix-com.filesusr.com, so code pasted into an Embed HTML element can read it.

Embed HTML element: the same call through the free proxy
<p id="players">Loading player count...</p>

<script>
  (async () => {
    const output = document.getElementById('players');
    const target =
      'https://api.steampowered.com/ISteamUserStats/GetNumberOfCurrentPlayers/v1/?appid=730';

    try {
      const response = await fetch('https://proxy.cors.dev/' + target);
      if (!response.ok) {
        throw new Error(response.headers.get('X-Cors-Error') || 'HTTP ' + response.status);
      }

      const { response: stats } = await response.json();
      output.textContent = stats.player_count.toLocaleString() + ' playing now';
    } catch (error) {
      output.textContent = 'Player count unavailable: ' + error.message;
    }
  })();
</script>

When the keyless proxy is enough

The free tier needs no signup and no key: put https://proxy.cors.dev/ in front of the full target URL. It fits code in an Embed HTML element or Custom Code that reads public data.

  • Methods: GET and HEAD only; POST answers 405 method_not_allowed.
  • Rate: About 600 requests per minute per IP and no daily or monthly quota; past that, 429 with a Retry-After header that JavaScript can read.
  • Headers: Accept, Accept-Language, If-None-Match, If-Modified-Since and Range pass. Authorization, and names containing token, apikey, api-key, api_key, secret or password or ending in -key, answer 400 header_not_allowed. Other custom headers, including names containing csrf or xsrf such as X-CSRF-Token, are dropped and listed in X-Cors-Dropped-Headers.
  • Size: Responses up to 6 MiB within a 30 second upstream deadline. Cookies are never forwarded.

When the proxy refuses a request, the X-Cors-Error response header names the reason; X-Cors-Source: upstream marks status codes that came from the API.

Origins to register for cors.dev Pro

cors.dev Pro costs $5 a month for 500,000 requests, with a 7-day trial of 1,000 requests and no card. It adds POST, PUT, PATCH and DELETE with bodies up to 1 MiB, plus the Authorization, Content-Type and custom headers, and it can cache a GET for up to 300 seconds when the upstream response allows it; cache hits still count as requests. Throughput is 3,000 requests a minute per account and 100 in flight.

You sign up with Google, create a Connection with up to 10 exact page origins and up to 20 target hosts, and send its publishable key in an X-Cors-Key header. Register the exact origin your code runs on:

  • Page code and Custom Code: Your connected domain, for example https://www.yourdomain.com.
  • A free Wix address: https://username.wixsite.com.
  • Embed HTML: The exact filesusr.com origin; log location.origin inside the embed to read it.

A request from an origin that is not on the Connection answers 403 origin_not_allowed, and Origin: null never matches. For page code, a backend web module remains the better fix.

Good questions.

Why does wix-fetch work in backend code but fail in page code?

wix-fetch uses node-fetch on Wix's servers in backend code and the browser's native Fetch API in page code. Page code runs in a web worker in the visitor's browser, so its requests carry your site's origin and CORS applies. A backend web module makes a server request with no browser origin, so CORS never comes up.

What is filesusr.com and why is it in my CORS error?

In our October 2026 tests, Wix loaded the Embed HTML element in a sandboxed iframe from a filesusr.com host rather than your domain, such as https://gaweb1-wixsite-com.filesusr.com for gaweb1.wixsite.com. Requests from code inside that iframe carry the filesusr.com origin. External APIs almost never allow it, so the browser blocks the response.

Can code in an Embed HTML element read elements on my Wix page?

No. Wix's docs say the HTML element is a sandboxed iframe without direct access to the other elements on your page. Pass data between page code and the iframe with postMessage and the element's onMessage handler.

Where do I keep an API key on Wix?

In the Wix Secrets Manager, read inside a backend web module with secrets.getSecretValue() from wix-secrets-backend.v2. Backend code never ships to the browser, so the key stays private. Never put a secret key in page code, Custom Code or an Embed HTML element, with or without cors.dev, because visitors can read all of it.

Do I need a connected domain for Custom Code?

Yes. Custom Code in your site's Settings needs a published site with a connected domain. The code runs in the visitor's browser on that domain, so its requests carry your connected domain's origin.