Skip to content

URL Reference

All URL patterns handled by the dispatch and theme workers. Use this as a single reference for routing behavior, debugging unexpected proxies, and understanding which endpoints are available.

These are the standard storefront URLs that the theme worker can render. Each maps to a base template type via resolveBaseTemplateName().

URL patternTemplateNotes
/indexHomepage
/products/{handle}productProduct detail page
/collections/{handle}/products/{handle}productProduct within collection context (SEO URL)
/collectionslist-collectionsCollection listing page
/collections/{handle}collectionCollection detail page
/cartcartCart page
/pages/{handle}pageStatic page
/blogs/{blog}/{article}articleBlog article (3+ path segments)
/blogs/{blog}blogBlog listing
/searchsearchSearch results

Any path that doesn’t match the above resolves to index. If the path is not / and resolves to index, the worker treats it as unknown and proxies to Shopify (catches typos, /gift_card/..., /challenge/..., etc.).

Shopify generates combined URLs for SEO and breadcrumb context. These look like nested resources but always render the “inner” resource.

/collections/{collection}/products/{product}

Renders the product page, not the collection. Shopify uses this pattern so that breadcrumbs can show Collection > Product. The collection handle is in the URL for context only — the product data is loaded from KV using the product handle (4th path segment).

/blogs/{blog}/{article}

Renders the article page, not the blog. Detected by path segment count: 3+ segments under /blogs/ = article, exactly 2 = blog listing. The blog handle identifies which blog the article belongs to.

/collections/{handle}/{tag} — Shopify renders this as the collection page filtered by tag. Our router classifies it as collection (same as /collections/{handle}) because the tag is just a filter, not a separate resource.

Not currently implemented — tagged collection URLs proxy to Shopify. If we add support, the tag will need to be extracted from pathParts[2] and passed as currentTags to the template context.

Gotcha: A tagged collection URL like /collections/sale/new-arrivals has the same path structure as /collections/{handle}/{subpage}. The router doesn’t distinguish — it’s always collection. This is correct because Shopify treats both the same way.

These don’t affect template resolution (only the pathname matters) but change rendering behavior:

ParameterEffect
?view={suffix}Overrides template suffix (e.g., ?view=modalproduct.modal template)
?variant={id}Selects product variant (pre-selects options, updates price)
?sort_by={key}Collection sort order
?filter.*={value}Collection filtering (Shopify storefront filtering API)
?page={n}Pagination
?q={query}Search query
?section_id={id}Section Rendering API — returns only the named section’s HTML
?sections={ids}Proxied to Shopify (multi-section rendering)

These paths are always forwarded to Shopify, never rendered by the theme worker. Classified by classifyRoute().

PrefixPurpose
/adminShopify admin
/checkout, /checkoutsCheckout flow
/accountCustomer account
/servicesShopify services
/appsShopify apps
/.well-known/shopifyShopify well-known endpoints
/web-pixelsShopify web pixels
/cdnShopify CDN
/passwordStore password page
/policiesPolicy pages (privacy, refund, etc.)
/recommendationsProduct recommendations API
/search/suggestSearch autocomplete API
PathPurpose
/cart.jsCart AJAX API
/cart/add.jsAdd to cart
/cart/update.jsUpdate cart
/cart/change.jsChange cart item
/cart/clear.jsClear cart
PatternPurpose
/cart/*.jsAny cart AJAX endpoint
*.json (any path)Shopify data API (e.g., /products/snowboard.json)
/products/*.js, /collections/*.jsShopify storefront JS endpoints
ConditionEffect
?sections={ids}Multi-section rendering — proxied to Shopify
?section_id={id}Single section rendering — rendered by theme worker (not proxied)
RouteMethodAuthPurpose
/api/cache/purgePOSTBearer CACHE_PURGE_TOKENPurge section cache, KV cache, D1 cache, embed scan state
/api/diagnosticsGETNoneReturns component registry count
/lk/sw.jsGETNoneService worker script
/lk/{filename}GETNoneClient assets from R2 (immutable, 1-year cache)
/localizationPOSTNoneCountry/currency switcher (sets cookie, redirects)
/__platform/app-block-sectionGETClient middlewareFetches Shopify app-block HTML, caches to KV
/__platform/app-block-promotePOSTBearer debug_tokenPromote/demote app blocks for inlining
/__dev/healthGETNoneHealth check ({ ok: true })
/__dev/seedPOSTDev onlySeeds KV + D1 with config/template data
/__dev/render-sectionsGETDev onlyDev section rendering API
/__dev/purge-eventsGETDebug tokenDebug bar purge event polling

All /__dev/* routes return 403 in production (gated by ENVIRONMENT !== "development").

RouteMethodAuthPurpose
/api/cache/purgePOSTBearer CACHE_PURGE_TOKENPurge dispatch CDN cache, forwards to theme worker
/cart/{action}.jsPOSTNoneCart interception for AB test tracking (when AB enabled)
/* (catch-all)ALLNoneCDN cache + AB routing + failover to Shopify
ParameterScopeEffect
?lk-debug=1All layersEnables debug mode (sets 1-day cookie), adds console output + window.lk API
?lk-debug=0All layersDisables debug mode (clears cookie)
?lk-fresh=1All layersBypasses every cache layer (requires debug mode active)

Browser 301 cache — If a route previously returned a 301 redirect (e.g., proxied to Shopify before the template was enabled), Chrome caches it permanently. Users must clear site data for the domain or use incognito.

Unknown paths render as proxy, not 404 — Paths like /asdfasdf or /gift_card/123 resolve to index in the template mapper, but because the path is not /, the worker detects this as “unknown” and proxies to Shopify rather than rendering a broken homepage.

.json suffix always proxies — Any path ending in .json is routed to Shopify, regardless of template type. This means /products/snowboard.json always returns Shopify’s JSON API, never the rendered product page.

Tagged collections vs nested products/collections/sale/new-arrivals (tagged collection) and /collections/sale/products/widget (nested product) look similar but route differently. The nested product pattern is detected by checking for a /products/ segment in the path. A tag that literally contains “products” (e.g., /collections/sale/products-on-clearance) does NOT trigger the nested product route because there’s no /products/ followed by another segment.

?section_id vs ?sections — Single section_id is rendered by the theme worker (renders the full page, extracts one section). Plural sections is proxied to Shopify.

D1 cache TTL — Template gating reads from D1 with a 60-second in-memory cache. After updating templates_json, changes propagate globally within 60 seconds without manual purge.

ConcernFile
URL → template mappingpackages/cf-client-site/src/lib/template-resolution.ts
Route classification (proxy vs render)packages/cf-client-site/src/middleware/routes.ts
Template gating + worker routingpackages/cf-client-site/src/worker.ts
Data loading + template renderingpackages/cf-client-site/src/render/handle-render.ts
Dispatch CDN + AB routingpackages/cf-client-dispatch/src/worker.ts
Testspackages/cf-client-site/src/lib/template-resolution.test.ts, packages/cf-client-site/src/middleware/routes.test.ts