Skip to main content

Serve assets from Shopify CDN

Serve all theme assets from the Shopify CDN instead of external domains to eliminate connection overhead for each domain.


Shopify serves theme assets, such as CSS, JavaScript, fonts, and images, through its CDN. Asset URLs are moving from the shared cdn.shopify.com domain to your store's own domain under a /cdn path, for example /cdn/shop/files/hero.jpg. When an asset is served from the store domain, it's same-origin with the HTML document, so the browser reuses the connection it already has: no extra DNS lookup, TCP connection, or TLS handshake.

The migration is still rolling out, so some requests still use cdn.shopify.com. Theme editor previews, some Shopify platform assets, and some regions continue to use the shared domain, and Shopify's font library is served from fonts.shopifycdn.com when store-domain URLs aren't in use. You don't control which URL is emitted: the platform picks it at render time. What you control is whether the asset goes through Shopify at all.

Connection overhead per external domain includes the DNS lookup, TCP connection, and TLS handshake, all before the first byte of the asset downloads. Multiple domains compound this problem.


Move assets to the theme's assets/ directory and reference them with the asset_url Liquid filter:

<link rel="stylesheet" href="https://external-domain.com/style.css" />
<script src="https://external-domain.com/script.js"></script>
<img src="https://external-domain.com/image.jpg" alt="..." />

<link rel="stylesheet" href="{{ 'style.css' | asset_url }}">
<script src="{{ 'script.js' | asset_url }}"></script>
<img src="{{ 'image.jpg' | asset_url }}" alt="...">

Anchor to Automatic asset versioningAutomatic asset versioning

The asset_url filter appends version numbers to URLs. When you update a file, the version number changes, forcing browsers to download the new version.

{{ 'style.css' | asset_url }}
{%- comment -%} Outputs: /cdn/.../style.css?v=1384022871 {%- endcomment -%}

Without versioning, browsers might cache outdated assets for days.

Anchor to CSS background imagesCSS background images

For images referenced in CSS, use asset_url in a .css.liquid file:

assets/style.css.liquid

.hero {
background: url({{ 'bg.jpg' | asset_url }});
}

The source file must have a .css.liquid extension for Shopify to process the Liquid code inside it. Reference it as style.css, without the .liquid extension: Shopify serves the compiled asset under that name.


Target: minimize external domains. Aim for a maximum of one external domain beyond your store domain. Theme assets served through asset_url add little or no connection overhead, because they're served either from the same origin as your HTML or from a Shopify domain the browser is already connected to.

When you can't use the Shopify CDN, such as when third-party services don't allow copying files or require dynamic updates, check the service's documentation. If you must use an external domain, then add a preconnect hint to warm up the connection early.


  • Chrome DevTools Network panel: count unique domains and measure connection overhead.
  • Network tab: verify that assets load from your store domain or a Shopify domain rather than from third-party domains.
  • Compare before and after: total connection time with and without external domains.


Was this page helpful?