[Admin Extension] CORS errors using fetch, despite response headers in place

Topic summary

CORS failure on fetch from a Shopify Admin Extension: the browser’s preflight OPTIONS request was reported as a CORS error by Shopify, preventing the subsequent GET. Screenshots and ngrok logs showed valid response headers, yet the frontend still flagged the preflight.

The user asked whether this was a known bug/limitation and considered workarounds (using a different HTTP client and accessing a session token via a hook). No workaround was needed after identifying the server-side header issue.

Resolution: add “authorized” to the Access-Control-Allow-Headers in the server’s CORS response. After including this specific header, the preflight succeeded and requests proceeded as expected.

Technical notes: CORS (Cross-Origin Resource Sharing) uses an OPTIONS preflight to verify which methods/headers are permitted. If a required request header (here, “authorized”) is not listed in Access-Control-Allow-Headers, the preflight fails and the main request is blocked.

Status: Resolved via server header change; no further action items or unanswered questions reported.

Summarized with AI on December 26. AI used: gpt-5.

Hello,

We’re attempting to send a fetch request to our backend API to retrieve some data.

We’ve set the appropriate CORS headers on the response for the OPTIONS pre-flight request. However, Shopify is still interpreting the response as a CORS failure.

Thanks to ngrok, I can tell our backend is in fact returning valid CORS response headers to the OPTIONS call:

Yet, in the Shopify frontend, the OPTIONS request is being falsely reported as a CORS error:

CleanShot 2024-06-27 at 13.38.24.png

Since the OPTIONS request “fails”, there’s no subsequent GET request.

I know our CORS headers are sound because we reuse this same middleware for other OPTIONS requests.

Is this a known bug/limitation? If so, can we work around it by using a different HTTP client and somehow accessing the session token over a hook?

Turns out you have to add authorized to the Access-Control-Allow-Headers response header.

I didn’t have this specific header added before, so it wasn’t included in the CORS policy.