Oxygen runtime
Oxygen runs your storefront as a Web Worker on Cloudflare's open-source workerd runtime. Workers use web-standard APIs, such as fetch, Request, Response, Streams, URL, and Web Crypto, instead of Node APIs. For details about the standard APIs, refer to MDN and Cloudflare's Workers runtime APIs.
This page covers what's specific to Oxygen.
Anchor to Local parityLocal parity
MiniOxygen runs your Worker locally on workerd with sample Oxygen request headers. Test runtime APIs on a preview deployment.
At build time, MiniOxygen sets the Worker's compatibility date from your Hydrogen version by default. Hydrogen 2026-10 uses 2026-10-01. The React Router template sets 2026-04-01. The compatibility date controls which runtime behavior changes apply to your Worker.
Anchor to Request headersRequest headers
Oxygen adds the following headers to each incoming request. A header is missing when Oxygen doesn't have its value.
| Header | Value |
|---|---|
oxygen-buyer-ip | The buyer's IP address. Pass it as buyerIp to createShopifyRequestContext(). |
oxygen-buyer-country | The buyer's country. |
oxygen-buyer-region | The buyer's region. |
oxygen-buyer-region-code | The buyer's region code. |
oxygen-buyer-city | The buyer's city. |
oxygen-buyer-postal-code | The buyer's postal code. |
oxygen-buyer-continent | The buyer's continent. |
oxygen-buyer-latitude | The buyer's latitude. |
oxygen-buyer-longitude | The buyer's longitude. |
oxygen-buyer-timezone | The buyer's time zone. |
oxygen-buyer-metro-code | The buyer's metro code (Designated Market Area). |
oxygen-buyer-is-eu-country | Any non-empty value means that the buyer's country is in the EU. |
Location headers are approximate. Use them for defaults, such as suggesting a market, but let buyers change the result. For more information, refer to Markets.
Anchor to CacheCache
Open a cache with caches.open() and pass it to the Storefront API client, as described in Caching. Oxygen doesn't support caches.default, and each data center has its own cache.
If you use the Cache API directly, instead of through the Storefront API client, then note the following:
- Cache keys must use
https:URLs. Requests withhttp:URLs aren't cached. - The cache reads
Cache-Control,Expires,ETag, andLast-Modifiedfrom the response that you pass tocache.put(). Browser caches ignore these headers, but Oxygen's cache doesn't. - Responses with a
Set-Cookieheader aren't cached. Remove the header, or addCache-Control: private=Set-Cookie, before you callcache.put().
Anchor to Differences from NodeDifferences from Node
Oxygen doesn't include Node APIs. Replace the following in your server code:
| Node API | Use instead |
|---|---|
Buffer | Uint8Array, TextEncoder, TextDecoder, atob(), and btoa(). TextEncoder and TextDecoder support only UTF-8. |
node:crypto | Web Crypto, through crypto.subtle. For supported algorithms, refer to Cloudflare's Web Crypto docs. |
| Node streams | Web Streams, such as ReadableStream and TransformStream. |
fs and other node:* modules | Bundle static data into your build, or fetch it. |
process.env | The Worker's env argument. Refer to Read variables in your app. |
Anchor to Other limitsOther limits
- Request scope: Call
fetch(),setTimeout(), andsetInterval()inside the request handler, not at module scope. - Dynamic code:
eval()andnew Function()aren't available. - Time:
Date.now()returns the time of the last I/O operation, not the exact current time. Don't use it to measure code that doesn't do I/O. - Headers: To read multiple
Set-Cookievalues, useheaders.getSetCookie().