Impact (P0, security)
On a production VTEX storefront (@decocms/tanstack + @decocms/apps-vtex, 2026-07-29), customers reported — and we reproduced — seeing other customers' orders and personal data on the "my orders" (/myaccount → /account) area. Root cause: authenticated VTEX responses routed through the worker proxy are served with Cache-Control: public and cached at shared edge layers (Cloudflare/CloudFront), delivering one user's response to another.
Reproduction (production, anonymous requests)
# /api/sessions DIRECT to VTEX origin:
cache-control: no-store,no-cache
# /api/sessions THROUGH the worker proxy:
cache-control: public, s-maxage=60, stale-while-revalidate=3600, stale-if-error=86400
# /account through the worker:
cf-cache-status: EXPIRED age: 413 x-cache: Hit from cloudfront x-vtex-cache-status: EXPIRED
VTEX correctly marks /api/sessions as no-store; it is served as public after passing through our edge path. /account (personalized) is demonstrably served from shared caches.
Three contributing defects
1. Proxy does not normalize Cache-Control (packages/apps-vtex/src/utils/proxy.ts)
createVtexCheckoutProxy → filterHeadersStrict (~276-287) strips hop-by-hop / CF-internal / Set-Cookie but passes Cache-Control through untouched. The proxyHandler return in packages/tanstack/src/sdk/workerEntry.ts:1795-1797 is returned raw (no dressResponse). So authenticated proxy responses get no private/no-store protection.
Fix: default proxy responses to Cache-Control: private, no-store, no-cache, must-revalidate + CDN-Cache-Control: no-store, with an allow-list for genuinely public static assets (/files/, /arquivos/, /assets/vtex, /XMLData/). Consider a hardenAuthenticatedCache option (default true).
2. Login detection breaks for opaque token & account-suffixed cookie (packages/apps-vtex/src/utils/vtexId.ts)
The edge cache's only logged-in bypass depends on extractVtexContext().isLoggedIn. But extractVtexAuthCookie (24-27) only matches the base VtexIdclientAutCookie= (ignores VtexIdclientAutCookie_{account}), and parseVtexAuthToken (48-51) treats any non-JWT (opaque) token as logged-out. VTEX auth cookies are commonly opaque → isLoggedIn is false for genuinely logged-in users → the bypass never fires.
Fix: match the account-suffixed variant too (as parseCookie at 115-136 already does); treat presence of a non-expired auth cookie as logged-in even when opaque.
3. PRIVATE_PREFIX_RE misses /myaccount (packages/blocks/src/sdk/cacheHeaders.ts:265)
/^\/(cart|checkout|account|login|my-account)(\/|$)/ does not match myaccount (no hyphen), the real account path on this store, nor pt-BR routes (meus-pedidos, minha-conta, pedidos). These fall through to the cacheable listing default.
Fix: cover myaccount; optionally allow sites to register extra private prefixes.
Workarounds applied on the site (baggagio-tanstack)
hardenAuthenticatedProxyResponse() wraps proxy returns in worker-entry.ts, forcing private, no-store + CDN-Cache-Control: no-store (exempting static-asset prefixes).
cache-config.ts registers /myaccount*, /minha-conta*, /meus-pedidos*, /pedidos* as private.
- Cloudflare Cache Rule bypass on authenticated paths as immediate mitigation.
Impact (P0, security)
On a production VTEX storefront (
@decocms/tanstack+@decocms/apps-vtex, 2026-07-29), customers reported — and we reproduced — seeing other customers' orders and personal data on the "my orders" (/myaccount→/account) area. Root cause: authenticated VTEX responses routed through the worker proxy are served withCache-Control: publicand cached at shared edge layers (Cloudflare/CloudFront), delivering one user's response to another.Reproduction (production, anonymous requests)
VTEX correctly marks
/api/sessionsasno-store; it is served aspublicafter passing through our edge path./account(personalized) is demonstrably served from shared caches.Three contributing defects
1. Proxy does not normalize
Cache-Control(packages/apps-vtex/src/utils/proxy.ts)createVtexCheckoutProxy→filterHeadersStrict(~276-287) strips hop-by-hop / CF-internal / Set-Cookie but passesCache-Controlthrough untouched. TheproxyHandlerreturn inpackages/tanstack/src/sdk/workerEntry.ts:1795-1797is returned raw (nodressResponse). So authenticated proxy responses get noprivate/no-storeprotection.Fix: default proxy responses to
Cache-Control: private, no-store, no-cache, must-revalidate+CDN-Cache-Control: no-store, with an allow-list for genuinely public static assets (/files/,/arquivos/,/assets/vtex,/XMLData/). Consider ahardenAuthenticatedCacheoption (default true).2. Login detection breaks for opaque token & account-suffixed cookie (
packages/apps-vtex/src/utils/vtexId.ts)The edge cache's only logged-in bypass depends on
extractVtexContext().isLoggedIn. ButextractVtexAuthCookie(24-27) only matches the baseVtexIdclientAutCookie=(ignoresVtexIdclientAutCookie_{account}), andparseVtexAuthToken(48-51) treats any non-JWT (opaque) token as logged-out. VTEX auth cookies are commonly opaque →isLoggedInisfalsefor genuinely logged-in users → the bypass never fires.Fix: match the account-suffixed variant too (as
parseCookieat 115-136 already does); treat presence of a non-expired auth cookie as logged-in even when opaque.3.
PRIVATE_PREFIX_REmisses/myaccount(packages/blocks/src/sdk/cacheHeaders.ts:265)/^\/(cart|checkout|account|login|my-account)(\/|$)/does not matchmyaccount(no hyphen), the real account path on this store, nor pt-BR routes (meus-pedidos,minha-conta,pedidos). These fall through to the cacheablelistingdefault.Fix: cover
myaccount; optionally allow sites to register extra private prefixes.Workarounds applied on the site (baggagio-tanstack)
hardenAuthenticatedProxyResponse()wraps proxy returns inworker-entry.ts, forcingprivate, no-store+CDN-Cache-Control: no-store(exempting static-asset prefixes).cache-config.tsregisters/myaccount*,/minha-conta*,/meus-pedidos*,/pedidos*asprivate.