Skip to content

Fix theme dev ignoring SHOPIFY_HTTP(S)_PROXY - #8530

Open
ujjawal-yadav wants to merge 2 commits into
Shopify:mainfrom
ujjawal-yadav:fix/5890-theme-proxy-support
Open

Fix theme dev ignoring SHOPIFY_HTTP(S)_PROXY#8530
ujjawal-yadav wants to merge 2 commits into
Shopify:mainfrom
ujjawal-yadav:fix/5890-theme-proxy-support

Conversation

@ujjawal-yadav

@ujjawal-yadav ujjawal-yadav commented Sep 11, 2026

Copy link
Copy Markdown

WHY are these changes introduced?

Fixes #5890.

shopify theme dev ignores SHOPIFY_HTTP(S)_PROXY, so behind a corporate proxy every page render fails with the 502 / TypeError: fetch failed from the issue.

bootstrap.ts sets the proxy up with global-agent, which patches Node's http/https agents. The storefront path never reaches them: both branches of render() in storefront-renderer.ts and proxyStorefrontRequest() in proxy.ts call the built-in fetch, which is undici and ignores those agents. Each carries an eslint-disable-next-line no-restricted-globals, and the rule's own message is the diagnosis: "Built-in fetch does not support HTTP proxies." Confirmed against a local proxy: the built-in fetch goes direct, node-fetch goes through it.

WHAT is this pull request doing?

Routes those three call sites through fetch from @shopify/cli-kit/node/http, as storefront-session.ts in the same directory already does, and drops the suppressions.

Two details are why this isn't a one-line swap:

  • H3 rejects a node-fetch Response. Both functions are returned from H3 handlers, and returning the client's response as-is crashes the dev server (ERR_INVALID_ARG_TYPE ... Received an instance of Response). Both already re-wrapped with new Response(response.body, response), so a toWebResponse() helper converts at that same spot. render()'s signature and its consumers are unchanged.
  • node-fetch always exposes a body stream, even on 304. The built-in fetch reports null there, and new Response(stream, {status: 304}) throws, so every revalidated asset would answer 500. toWebResponse() also reads headers through raw(), since iterating node-fetch headers joins repeated set-cookie values and would merge the storefront's session cookies.

Requests use slow-request to keep the built-in fetch's semantics here: no automatic cancellation, so a slow theme still renders, and no retries, since this path carries cart and checkout writes. The request body goes through Readable.fromWeb(); a 4 MB POST arrives intact. Proxied assets now also pass through cli-kit's runWithTimer, which retains two performance.measure entries per request, as every other cli-kit request already does.

getProxyStorefrontHeaders() no longer forwards the browser's accept-encoding, so the storefront only picks a coding the client decodes. content-encoding is dropped only when the client actually decompressed the body, so anything it passes through, such as a chained br, gzip, still reaches the browser labelled correctly.

Not touched: notifier.ts, since --notify targets a user-chosen and usually local URL, and parseThemeFileContent() in cli-kit/node/themes/api.ts, which has the same bug for theme pull downloads but sits in another package. Happy to follow up there.

This also sits alongside #8287 rather than against it: the change routes theme through cli-kit's client instead of around it, which holds whichever implementation cli-kit uses. toWebResponse() gets simpler there, since h3 accepts an undici Response directly.

How to manually test your changes?

  1. Start a local HTTP proxy, for example mitmdump -p 8080.
  2. SHOPIFY_HTTP_PROXY=http://127.0.0.1:8080 SHOPIFY_HTTPS_PROXY=http://127.0.0.1:8080 shopify theme dev --store <store>
  3. Open the preview URL. On main it fails with 502. With this change the render and asset requests appear in the proxy log and the page loads. Reload once more to exercise the 304 path.

Checklist

  • I've considered possible cross-platform impacts (Mac, Linux, Windows)
  • I've considered possible documentation changes
  • I've considered analytics changes to measure impact
  • The change is user-facing — I've identified the correct bump type (patch for bug fixes · minor for new features · major for breaking changes) and added a changeset with pnpm changeset add

`createGlobalProxyAgent` patches Node's http/https agents, but the
storefront request path called the built-in fetch (undici), which does
not use them, so every render and proxied asset bypassed the proxy.

Route both through `fetch` from `@shopify/cli-kit/node/http`, keeping
the built-in `Response` at the H3 boundary, dropping the body on
null-body statuses (node-fetch always reports a stream there) and
keeping repeated `set-cookie` headers separate.

Fixes Shopify#5890
@ujjawal-yadav
ujjawal-yadav requested a review from a team as a code owner September 11, 2026 11:51
@ujjawal-yadav

Copy link
Copy Markdown
Author

"I have signed the CLA!"

The client in `@shopify/cli-kit/node/http` decompresses only exact
`gzip`, `x-gzip`, `deflate`, `x-deflate` and `br` codings. A chained
value such as `br, gzip` reaches us still compressed, and the
unconditional `content-encoding` delete then relabelled it as identity,
so the browser was handed compressed bytes it would not decode. The
built-in fetch decoded chained codings, so this was a narrowing
introduced by routing through the client.

Also drop `101` and `103` from the null-body statuses. The `Response`
constructor rejects any status outside 200-599, so neither can be built
here whatever the body is.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support SHOPIFY_HTTP(S)_PROXY usage in theme commands

1 participant