Shopify Admin UI Extension - CORS Issue in Development (But Works in Production)

Hey everyone,

We’ve been debugging this issue for the past two days and could really use some insights.

We’re building a Shopify Admin UI Extension (TypeScript/Remix) following this example:
:link: Shopify Admin UI Extension Docs

Our extension needs to communicate with our backend, but we keep hitting a CORS error in development, even when properly setting CORS headers as described in the docs:
:link: Shopify CORS Docs

What We’ve Tried

  • Setting Access-Control-Allow-Origin: * and other required CORS headers.
  • Explicitly allowing https://partners.shopify.dev and https://admin.shopify.com.
  • Checking our tunnel (using trycloudflared) to ensure requests are routed correctly.
  • Running everything in production—and surprisingly, it works perfectly there.

The Problem

While the extension works in production, we cannot properly debug and test in development due to the persistent CORS rejection from the trycloudflared tunnel.

Has anyone encountered this before?
Is there a workaround to make CORS behave correctly during local development?

Would love to hear any best practices or solutions you’ve used! :rocket:

Thanks in advance!

2 Likes

Hey everyone,

Just wanted to follow up on this issue—huge thanks to @Tommy_Gaudreau from the Slack Community for pointing me in the right direction!

After days of debugging and trying different CORS headers with no success, the fix turned out to be a simple tweak in vite.config.ts:

Solution: Enable CORS in Vite

Adding cors: true to the Vite server configuration resolved the issue:

ts

CopyEdit

export default defineConfig({
  server: {
    port: Number(process.env.PORT || 3000),
    hmr: hmrConfig,
    fs: {
      // See https://vitejs.dev/config/server-options.html#server-fs-allow for more information
      allow: ["app", "node_modules"],
    },
    cors: true, // ✅ This fixed the CORS issue in development
  },
});

Recap of the Problem

For context, we were building a Shopify Admin UI Extension (TypeScript/Remix) and encountered persistent CORS errors only in development when trying to communicate with our backend via trycloudflared. Despite setting proper CORS headers, nothing seemed to work—until we explicitly enabled CORS in Vite’s server config.

Lessons Learned

  • Shopify’s local tunnel (trycloudflared) can behave differently from production.
  • Even if backend headers are correct, the local dev server may still block requests due to its own CORS settings.
  • If you’re using Vite for your frontend, double-check your vite.config.ts server settings.

Hope this helps someone avoid the time sink we went through! :rocket:


6 Likes

Thanks for taking the time to reply back with the answer. CORS trips up so many developers and having the actual solution searchable in this community makes it so much easier to find the answer.

3 Likes

Thank you so much!!! I have been struggling with this for days!!!

cors = true
partially worked for me.

using cloudflare tunnel (https ://contract-tim-exactly-illustration.trycloudflare.com/api/my-proxy) directly works for me instead of the app proxy

My client in this case is calling app_proxy is customer account ext.

What headers are you using?

Edit:
as per shopify cust acc ext documentation
“UI extension requests made to the App Proxy of password protected shops is not supported. Extension requests come from a web worker which does not share the same session as the parent window.”

so how do we work with dev stores that are password protected?

For context, after noticing that downgrading vite fixed the issue, I opened a ticket on the vite GitHub and the answer I got was that it was an expected breaking change to improve security (even though it’s in a patch update). I was pointed at the doc describing the change and options to correct the issue, which included adding the cors option in the config.

So if you’re running an older project and you suddenly get cors errors that you didn’t get before, this is probably why. That was my case and it was so confusing to figure it out :exploding_head:

1 Like