Introduction
A 100-millisecond delay in page load time correlates with a 1% drop in conversion rate, according to Deloitte’s 2020 retail performance study. For Shopify merchants serving thousands of concurrent sessions, every uncached request compounds that cost directly into revenue loss.
Shopify caching layers are not a single mechanism. They form a stack: Shopify’s own global CDN, Storefront API response caching, Hydrogen’s sub-request cache, and any application-level cache you control as a developer. Each layer has distinct behavior, TTLs, invalidation logic, and failure modes.
This post maps the complete Shopify cache architecture from edge to application tier, explains where most stores leak performance by misconfiguring or bypassing cache, and gives you the configuration patterns and Liquid/code-level controls to fix it.
What Are Shopify Caching Layers?
Shopify caching layers refer to the multiple discrete systems that store and serve pre-computed responses at different points in a request’s journey from browser to origin server.
Shopify’s infrastructure runs on a global CDN powered by Fastly. Every storefront request passes through this edge network before it ever reaches Shopify’s application servers. When a response is cached at the edge, subsequent requests for the same resource never touch the origin, reducing latency to single-digit milliseconds.
The full cache stack for a modern Shopify deployment looks like this:
| Layer | Technology | Controlled By | Default TTL |
| Browser cache | HTTP Cache-Control headers | Shopify (overridable) | Varies by asset type |
| CDN edge cache | Fastly / Shopify CDN | Shopify | 10 minutes (storefront pages) |
| Storefront API cache | GraphQL persisted queries | Developer | Per-query with cache-control |
| Hydrogen sub-request cache | CacheShort / CacheLong strategies | Developer | Configurable |
| App-level cache | Redis, KV, or external | Developer | Fully custom |
Understanding which layer handles which content determines how effectively your store scales under real traffic conditions. Developers working on high-traffic Shopify architecture need to treat each layer independently, not as a monolithic system.
How Shopify CDN Caching Works
Shopify’s CDN layer is the first and most impactful cache in the stack. Shopify automatically caches storefront HTML pages, static assets, and media files across Fastly’s global PoP network.
Cache-Control Headers on Storefront Pages
Shopify sets Cache-Control: public, max-age=0, s-maxage=600 on most storefront HTML responses. The s-maxage=600 directive tells Fastly to cache the full rendered HTML at the edge for 10 minutes. The max-age=0 tells browsers not to store the response locally, which prevents stale personalized content from appearing.
Static assets (CSS, JS, fonts, images served from cdn.shopify.com) receive much longer TTLs, typically max-age=31536000 (one year) with content-hash filenames for cache busting.
What Bypasses the CDN Cache
Certain conditions force Shopify to serve uncached responses from the origin:
- Requests with an active session cookie (logged-in customers)
- Cart-containing requests
- Preview bar and theme editor sessions
- Requests to /admin, /account, /cart, and /checkout
This means your product pages, collection pages, and static content are fully cacheable, while authenticated flows always bypass the CDN. Developers who frequently encounter Shopify Core Web Vitals regressions often trace the root cause to unexpected cache bypasses on pages that should be edge-cached.
Caching in Liquid-Rendered Storefronts
Shopify’s Liquid rendering engine does not expose explicit cache directives within templates. However, cache behavior is heavily influenced by Liquid object usage and app embed configuration.
How Liquid Affects Cache Eligibility
Certain Liquid objects and filters force a page into a non-cacheable state:
- {{ customer }} rendered outside of JavaScript triggers a session-gated response
- App blocks that inject customer-specific content via {% render %} can contaminate a page’s cacheability
- Section rendering API requests (?section_id=) are cached independently at the edge, allowing partial page updates without full cache invalidation
The safest pattern for personalization is to render a skeleton HTML response from the CDN-cached page and populate customer-specific content client-side via the Storefront API or AJAX Cart API. This keeps your HTML cacheable while delivering dynamic data.
Liquid Code Pattern: Cart-Safe Page Structure
{% comment %}
Avoid rendering {{ cart }} or {{ customer }} in the main page template.
Load dynamic data client-side to preserve CDN cacheability.
{% endcomment %}
<div
data-cart-count="{{ cart.item_count }}"
data-customer-logged-in="{{ customer != blank }}"
>
<!-- Static content served from CDN cache -->
</div>
<script>
// Hydrate cart badge and account state via fetch('/cart.js')
fetch('/cart.js')
.then(r => r.json())
.then(cart => {
document.querySelector('[data-cart-count]').textContent = cart.item_count;
});
</script>
This pattern lets Shopify’s CDN cache the full HTML response while JavaScript handles the personalized layer. Developers looking to complement this with Shopify Liquid optimization techniques should audit every Liquid object rendering on category and product pages first.
Storefront API Caching and Persisted Queries
The Shopify Storefront API supports HTTP caching natively. GET requests to the Storefront API with persisted query hashes are cacheable at the CDN layer because the full query is represented as a URL parameter rather than a POST body.
Persisted Queries and Cache Hits
When you use Shopify’s @shopify/hydrogen or direct API calls with persisted query IDs, Shopify can cache GraphQL responses at the edge using the same Fastly infrastructure that serves storefront HTML.
A persisted query request looks like:
GET https://{shop}.myshopify.com/api/2025-04/graphql.json
?operationName=ProductQuery
&persistedQuery={“version”:1,”sha256Hash”:”abc123…”}
This request is fully cacheable. A standard POST to the same endpoint is not cacheable because POST requests do not pass through Shopify’s edge cache layer.
The Shopify GraphQL API documentation confirms that only GET-formatted persisted queries trigger edge cache behavior. Developers building headless storefronts should always implement persisted queries as part of their initial architecture decisions.
Multi-Layer Caching in Shopify Hydrogen
Shopify Hydrogen (the React-based headless framework built on Remix) introduces a dedicated sub-request caching layer that operates independently from Shopify’s CDN caching.
CacheShort, CacheLong, and CacheNone Strategies
Hydrogen exposes three built-in cache strategies via the @shopify/hydrogen package:
import {
CacheShort,
CacheLong,
CacheNone,
createStorefrontClient,
} from '@shopify/hydrogen';
// In your loader function:
const product = await storefront.query(PRODUCT_QUERY, {
variables: { handle: params.productHandle },
cache: CacheLong(), // TTL: 1 hour, stale-while-revalidate: 23 hours
});
const cart = await storefront.query(CART_QUERY, {
variables: { cartId },
cache: CacheNone(), // Never cache cart data
});
CacheLong() sets max-age=3600, stale-while-revalidate=82800. CacheShort() sets max-age=1, stale-while-revalidate=9. CacheNone() adds Cache-Control: no-store.
Hydrogen’s Cache Storage Layer
Hydrogen stores sub-request cache entries in the Workers KV storage when deployed on Oxygen (Shopify’s hosting platform). On self-hosted deployments, you configure a custom cache handler. This means serverless functions in Shopify Hydrogen projects need an explicit cache strategy for every data-fetching loader, or sub-requests bypass the cache entirely and hit the Storefront API on every page view.
The practical rule: use CacheLong() for product and collection data, CacheShort() for inventory-sensitive content, and CacheNone() for anything customer-specific.
Cache Invalidation Strategies in Shopify
Cache invalidation in the Shopify ecosystem operates through two primary mechanisms: time-based expiry (TTL rollover) and event-driven purge via webhooks.
TTL-Based Invalidation
Shopify’s CDN cache invalidates automatically when the TTL expires. For storefront pages with s-maxage=600, stale content at the edge can persist for up to 10 minutes after a product or collection update. This is acceptable for most stores, but problematic during flash sales or inventory threshold events.
Webhook-Driven Cache Purge
For near-real-time invalidation, you can trigger a Shopify CDN cache purge via the POST /admin/api/2025-04/themes/{theme_id}/assets.json endpoint after writing asset changes, or use Shopify Webhooks to react to product and inventory events and invalidate your own application-level cache.
A products/update webhook payload, for example, should trigger purging of the affected product page URL from any upstream cache (Redis, Varnish, or your CDN proxy). The queue-based Shopify webhook processing pattern is the correct architectural choice here, as it prevents cache stampedes when a single product update fires hundreds of concurrent invalidation requests.
Understanding Shopify webhooks at the infrastructure level is essential before building any event-driven invalidation system.
Common Shopify Caching Mistakes to Avoid
Most performance regressions on Shopify storefronts trace back to a small set of caching configuration errors that are straightforward to audit and fix.
Rendering Dynamic Objects in Cacheable Templates
Placing {{ customer.first_name }} or {{ cart.item_count }} directly in Liquid templates that Shopify serves as edge-cached HTML causes two problems: first, the page becomes ineligible for CDN caching; second, cached versions may serve another user’s personalized data. Always move customer and cart state to client-side JavaScript hydration.
Missing Cache Strategies in Hydrogen Loaders
Every Hydrogen loader that calls storefront.query() without a cache option defaults to CacheShort(). For product data that changes infrequently, this means sub-request cache misses on nearly every page load. Auditing your loaders and applying CacheLong() to stable data can reduce Storefront API call volume by 60-80% under normal traffic conditions.
These issues frequently surface alongside the broader Shopify technical mistakes that degrade storefront performance over time. Running a comprehensive speed optimization checklist for your Shopify store should always include a cache layer audit as a dedicated step.
Caching and Shopify Plus: Expanded Control
Shopify Plus merchants gain access to additional caching controls through the checkout extensibility platform and the ability to use custom checkout pages built with Checkout UI Extensions.
The checkout itself is never cached at the CDN layer. However, Shopify Plus merchants building on headless architectures with Hydrogen and Oxygen can configure custom cache policies per route using Remix’s headers export. This lets you set aggressive CDN TTLs on marketing landing pages while bypassing cache entirely on transactional routes.
Understanding Shopify vs Shopify Plus infrastructure differences matters when planning a multi-layer caching strategy, since Plus unlocks custom storefront domains and Oxygen deployment options that affect which caching mechanisms are even available to you.
Conclusion
Shopify caching layers operate as an interconnected stack, and optimizing one layer without understanding the others produces incomplete results. The three most critical takeaways are:
- Shopify’s CDN (Fastly) automatically caches storefront HTML with a 10-minute TTL. Any dynamic Liquid object rendered server-side invalidates that cache eligibility and forces an origin request on every page view.
- Hydrogen’s sub-request cache with CacheLong() is the single highest-impact optimization available in headless deployments. Apply it aggressively to product and collection data.
- Webhook-driven invalidation replaces polling and TTL-guessing with precise, event-triggered cache purges. Build this into your architecture from the start, not as a retrofit.
Audit your current cache configuration before your next traffic spike. If you need expert guidance on implementing a production-grade Shopify cache architecture, explore the fault-tolerant Shopify integration patterns that complement every caching strategy.
FAQs
What are Shopify caching layers? Shopify caching layers are the multiple distinct caching systems that store and serve pre-computed responses at different points in a request’s path. They include Shopify’s Fastly-powered CDN edge cache, browser-level HTTP caching, Storefront API response caching via persisted queries, and Hydrogen’s sub-request cache backed by Workers KV on Oxygen.
How do I prevent a Shopify page from being cached by the CDN? To prevent a Shopify storefront page from being cached at the CDN edge, ensure that the page response includes a session cookie or that a customer-authenticated state is active. Alternatively, set a Cache-Control: no-store directive in a Hydrogen route’s headers() export. Shopify automatically bypasses the CDN for all /cart, /account, and /checkout routes.
What is the difference between CacheShort and CacheLong in Shopify Hydrogen? CacheShort() sets max-age=1, stale-while-revalidate=9, making it appropriate for frequently changing data like inventory counts. CacheLong() sets max-age=3600, stale-while-revalidate=82800, making it suitable for stable data like product descriptions and collection metadata. Both are built-in cache strategies from the @shopify/hydrogen package and control the sub-request cache in Oxygen.
Why is my Shopify store serving stale content after a product update? Stale content after a product update is typically caused by Shopify’s CDN TTL (up to 10 minutes) or an unconfigured application-level cache that is not responding to products/update webhook events. The fix is to implement webhook-driven cache invalidation: subscribe to the products/update topic, and on receipt, purge the affected product page from any upstream cache layer your application manages.
Does Shopify Hydrogen cache GraphQL API responses automatically? No, Hydrogen does not cache Storefront API responses automatically. Every storefront.query() call in a Hydrogen loader requires an explicit cache option. Without one, Hydrogen defaults to CacheShort(), which is a very short TTL and results in near-constant API requests under real traffic. Assign CacheLong() to product and collection queries, and CacheNone() to cart and customer queries as a baseline configuration.
{ "@context": "https://schema.org", "@type": "FAQPage", "mainEntity": [ { "@type": "Question", "name": "What are Shopify caching layers?", "acceptedAnswer": { "@type": "Answer", "text": "Shopify caching layers are the multiple distinct caching systems that store and serve pre-computed responses at different points in a request's path. They include Shopify's Fastly-powered CDN edge cache, browser-level HTTP caching, Storefront API response caching via persisted queries, and Hydrogen's sub-request cache backed by Workers KV on Oxygen." } }, { "@type": "Question", "name": "How do I prevent a Shopify page from being cached by the CDN?", "acceptedAnswer": { "@type": "Answer", "text": "To prevent a Shopify storefront page from being cached at the CDN edge, ensure that the page response includes a session cookie or that a customer-authenticated state is active. Alternatively, set a Cache-Control: no-store directive in a Hydrogen route's headers() export. Shopify automatically bypasses the CDN for all /cart, /account, and /checkout routes." } }, { "@type": "Question", "name": "What is the difference between CacheShort and CacheLong in Shopify Hydrogen?", "acceptedAnswer": { "@type": "Answer", "text": "CacheShort() sets max-age=1, stale-while-revalidate=9, making it appropriate for frequently changing data like inventory counts. CacheLong() sets max-age=3600, stale-while-revalidate=82800, making it suitable for stable data like product descriptions and collection metadata. Both are built-in cache strategies from the @shopify/hydrogen package and control the sub-request cache in Oxygen." } }, { "@type": "Question", "name": "Why is my Shopify store serving stale content after a product update?", "acceptedAnswer": { "@type": "Answer", "text": "Stale content after a product update is typically caused by Shopify's CDN TTL (up to 10 minutes) or an unconfigured application-level cache that is not responding to products/update webhook events. The fix is to implement webhook-driven cache invalidation: subscribe to the products/update topic, and on receipt, purge the affected product page from any upstream cache layer your application manages." } }, { "@type": "Question", "name": "Does Shopify Hydrogen cache GraphQL API responses automatically?", "acceptedAnswer": { "@type": "Answer", "text": "No, Hydrogen does not cache Storefront API responses automatically. Every storefront.query() call in a Hydrogen loader requires an explicit cache option. Without one, Hydrogen defaults to CacheShort(), which is a very short TTL and results in near-constant API requests under real traffic. Assign CacheLong() to product and collection queries, and CacheNone() to cart and customer queries as a baseline configuration." } } ] }
