Once you have handled the fundamentals - images, JavaScript, fonts, apps - the next performance gains come from resource prioritization, CDN behavior, TTFB reduction, and browser hint implementation. These advanced techniques are what separate a store scoring 70 from one scoring 90. They require understanding how browsers fetch resources, how Shopify's CDN caches content, and how to give browsers precise instructions about what to load first and when.
Who This Guide Is For
For stores already at 65 to 75 on mobile, these techniques are what push scores into the 80s and reduce real user LCP below 2 seconds.
Advanced Shopify Speed Optimization Techniques Overview
Preloading Critical Shopify Resources
Preloading instructs the browser to fetch a resource at high priority before it discovers that resource in the normal parsing flow. The browser starts the download immediately - before parsing your CSS, before executing JavaScript, before anything else. Preloading the wrong resources wastes bandwidth and can hurt performance. Preloading the right resources is one of the highest-impact single-line changes available.
Your LCP image is the primary preload target. On a product page this is the main product image; on the homepage it is the hero. The imagesrcset and imagesizes attributes on the preload hint must match your <img> tag's srcset and sizes exactly so the browser preloads the correct size for the current viewport.
Self-hosted fonts used above the fold should be preloaded with the crossorigin attribute. This is not optional - without crossorigin, the browser fetches the font twice, doubling the download cost for your primary typeface.
Preloading competes for bandwidth. Too many preloads create contention that slows everything down. Preload only the resources on the critical path: the LCP image, the primary font, and optionally the main theme script. Do not preload resources already loaded early through normal parsing.
{% assign hero_img = section.settings.hero_image %}
<link
rel="preload"
as="image"
href="{{ hero_img | image_url: width: 1400, format: 'webp' }}"
imagesrcset="
{{ hero_img | image_url: width: 600, format: 'webp' }} 600w,
{{ hero_img | image_url: width: 900, format: 'webp' }} 900w,
{{ hero_img | image_url: width: 1400, format: 'webp' }} 1400w
"
imagesizes="100vw"
>
<!-- Self-hosted font preload - crossorigin is required -->
<link
rel="preload"
as="font"
href="{{ 'PrimaryFont-Regular.woff2' | asset_url }}"
type="font/woff2"
crossorigin
>
Prefetch and Preconnect in Shopify
Preconnect establishes the DNS lookup, TCP connection, and TLS handshake with a domain before any resource from that domain is requested. Limit to 3 to 4 domains - each preconnect keeps a connection open, consuming memory and CPU. Use for Shopify CDN, Google Fonts, and your primary analytics domain.
Prefetch downloads resources the browser will likely need for the next navigation. It runs at low priority during browser idle time, so it does not compete with current page resources. For Shopify stores with predictable navigation patterns, prefetching the next likely page reduces perceived load time dramatically.
For third-party domains that load later in the page (analytics, social widgets), DNS prefetch resolves the domain name in advance without establishing a full connection. Lighter than preconnect and appropriate for resources needed in the second half of page loading.
<head>
<link rel="preconnect" href="https://cdn.shopify.com" crossorigin>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="dns-prefetch" href="https://connect.facebook.net">
<link rel="dns-prefetch" href="https://static.klaviyo.com">
</head>
{% if template == 'index' %}
<link rel="prefetch" href="/collections/all">
{% endif %}
{% if template == 'product' %}
<link rel="prefetch" href="/cart">
{% endif %}
let prefetchedUrls = new Set();
document.querySelectorAll('a[href^="/"]').forEach(link => {
link.addEventListener('mouseenter', function() {
const url = this.href;
if (prefetchedUrls.has(url)) return;
prefetchedUrls.add(url);
const prefetchLink = document.createElement('link');
prefetchLink.rel = 'prefetch';
prefetchLink.href = url;
document.head.appendChild(prefetchLink);
}, { passive: true });
});
Resource Prioritization in Shopify
The browser assigns priority to every resource it discovers. Understanding and influencing these priorities is an advanced optimization that most guides overlook. Your goal is ensuring your LCP image gets Highest or High priority and nothing unnecessary occupies Highest priority alongside it.
<!-- LCP image: explicitly highest priority -->
<img
src="{{ hero_image | image_url: width: 1400, format: 'webp' }}"
fetchpriority="high"
loading="eager"
width="{{ hero_image.width }}"
height="{{ hero_image.height }}"
alt="{{ hero_image.alt | escape }}"
>
<!-- Second image in viewport: reduce priority to avoid LCP competition -->
<img
src="{{ secondary_image | image_url: width: 800, format: 'webp' }}"
fetchpriority="low"
loading="eager"
alt="{{ secondary_image.alt | escape }}"
>
<!-- Below-fold images: low priority and lazy loaded -->
<img
src="{{ product_image | image_url: width: 400, format: 'webp' }}"
fetchpriority="auto"
loading="lazy"
alt="{{ product_image.alt | escape }}"
>
fetchpriority="low" to images that are technically in the viewport but not the LCP element prevents them from competing with the LCP image for bandwidth. This alone can improve LCP by 200 to 400ms on pages with multiple above-fold images. Audit your resource priorities in Chrome DevTools Network tab by adding the Priority column - your LCP image should show "Highest."Shopify CDN Optimization
Shopify's CDN (powered by Fastly) automatically handles geographic distribution of assets. Understanding how to work with the CDN - rather than accidentally working against it - produces meaningful performance gains.
Always reference theme assets with Shopify's asset filters: {{ 'file.js' | asset_url }}. Direct path references work but bypass some CDN optimizations. The filter generates the full CDN URL with cache-busting parameters automatically - you do not need to manage cache invalidation manually.
Some apps append unique parameters to page URLs. These create unique cache keys, meaning each unique URL combination generates a cache miss. Shopify handles variant parameters specially and does not create separate cache entries for them. UTM parameters and custom query strings do create separate entries - strip non-essential query parameters from internal links.
Serve a cached page to all visitors, then personalize specific elements via JavaScript after load. Cart count, wishlist state, and loyalty points should be hydrated client-side after the cached HTML loads. This is how high-traffic Shopify stores serve millions of requests efficiently.
// Load visitor-specific content client-side after cached page loads
fetch('/cart.js')
.then(res => res.json())
.then(cart => {
document.querySelector('.cart-count').textContent = cart.item_count;
});
Reducing TTFB in Shopify
Time to First Byte is the gap between a browser requesting a page and receiving the first byte of the response. A well-performing Shopify store has TTFB under 200ms for cached pages and under 600ms for uncached pages. If your TTFB consistently exceeds 600ms, Liquid template complexity is the primary investigation target.
A collection page loop inside a navigation loop increases TTFB proportionally to collection size. Replace with flattened logic. This is the most common cause of high TTFB on stores with substantial product catalogs.
Each metafield access in a loop is a separate data lookup. Assigning to a variable at loop entry reduces lookups on repeated access. A snippet rendering in 200ms that appears on every page is your highest TTFB optimization target.
Unpaginated loops over large collections are the most common cause of high TTFB on Shopify stores with substantial product catalogs. Use the Shopify Theme Inspector Chrome extension to identify per-template and per-snippet render times - identifying a slow snippet takes 2 minutes and fixing it can reduce TTFB by 150 to 180ms.
Advanced Debugging Techniques
Long Animation Frames are JavaScript executions that block the main thread for more than 50ms. Unlike long tasks measured during page load, LoAF measurements capture blocking during user interactions - the cause of high INP scores. Use the Performance panel in Chrome DevTools: record while interacting with your page, then look for red marks in the timeline to identify which JavaScript function caused each block.
Open Chrome DevTools, press Ctrl+Shift+P, and search "Show Rendering." Paint Flashing highlights areas being repainted excessively. Layout Shift Regions highlights elements causing CLS in real time - more precise than PageSpeed's CLS score. Frame Rendering Stats shows frames per second during interaction; below 60fps indicates janky scroll or animation performance.
When you cannot identify the cause of a performance problem through auditing: duplicate your theme, remove all apps and third-party scripts on the duplicate, test its performance, then add back app scripts one at a time until performance drops. The app that causes the drop is the primary culprit. This binary search approach isolates problems in complex stores faster than auditing individual scripts.
// LoAF API - collect real-user blocking data
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
if (entry.duration > 50) {
console.log('Long Animation Frame:', {
duration: entry.duration,
scripts: entry.scripts.map(s => ({
name: s.sourceURL,
duration: s.duration
}))
});
}
}
});
observer.observe({ type: 'long-animation-frame', buffered: true });
// Web Vitals with attribution - send to your analytics platform
import { onLCP, onINP, onCLS } from 'web-vitals/attribution';
onLCP(({ value, attribution }) => {
console.log('LCP:', value, 'ms');
console.log('LCP element:', attribution.lcpEntry?.element);
});
onINP(({ value, attribution }) => {
console.log('INP:', value, 'ms');
console.log('Input delay:', attribution.inputDelay);
console.log('Processing time:', attribution.processingDuration);
});
Automation Tools for Shopify Performance
Use the PageSpeed Insights API to monitor your key pages automatically on a schedule. Alert when any page drops more than 5 points from the previous day's baseline. Schedule via a GitHub Action or cron job to catch regressions before they affect real users.
Lighthouse CI integrates performance testing into your theme development workflow. Before publishing a theme update, Lighthouse CI runs a full performance audit and fails the deployment if scores drop below your configured thresholds. This prevents performance regressions from reaching your live store without manual testing.
GA4 can collect Core Web Vitals data from real users with minimal setup. Create a custom exploration with Core Web Vitals metrics segmented by page path, device type, and country. This provides visibility into which specific pages and user segments are experiencing performance problems that lab tests would not capture.
# lighthouserc.yml - fail deployment if scores drop
ci:
collect:
url:
- 'https://yourstore.com'
- 'https://yourstore.com/products/test-product'
assert:
assertions:
'categories:performance': ['warn', {minScore: 0.7}]
'largest-contentful-paint': ['error', {maxNumericValue: 2500}]
'cumulative-layout-shift': ['error', {maxNumericValue: 0.1}]
Shopify Performance Hacks for Advanced Stores
Instead of full page reloads when filter or sort selections change, use Shopify's Section Rendering API to fetch only the updated HTML for specific sections. This replaces a full page navigation with a single API call that returns only the changed section's HTML - the browser does not re-process everything it already has.
async function updateCollection(url) {
const response = await fetch(`${url}§ions=collection-products`);
const data = await response.json();
document.querySelector('#collection-products').innerHTML =
data['collection-products'];
}
Extract the CSS needed for above-fold rendering and inline it in your
<head>. Load the full stylesheet asynchronously. This is the most impactful single CSS optimization for FCP - typically moving FCP by 300 to 600ms and LCP by a similar margin.<head>
<style>
/* Critical CSS: only what is needed for above-fold render (~8-15KB) */
body { margin: 0; font-family: var(--font-body); color: #1a1a1a; }
.site-header { position: sticky; top: 0; background: #fff; z-index: 100; }
.hero-section { min-height: 500px; }
</style>
<link
rel="preload"
href="{{ 'theme.css' | asset_url }}"
as="style"
onload="this.onload=null;this.rel='stylesheet'"
>
<noscript><link rel="stylesheet" href="{{ 'theme.css' | asset_url }}"></noscript>
</head>
requestIdleCallback for Non-Critical InitializationTasks that do not affect the visible page experience can be deferred to browser idle periods. The
timeout: 3000 ensures the callback runs within 3 seconds even if the browser never reaches an idle state, providing a reasonable fallback for busy pages.function initNonCritical() {
initAnalytics();
attachPrefetchListeners();
initFocusManagement();
}
if ('requestIdleCallback' in window) {
requestIdleCallback(initNonCritical, { timeout: 3000 });
} else {
setTimeout(initNonCritical, 1);
}
The Speculation Rules API prerenders entire pages in a background tab before the visitor navigates to them. For predictable navigation like product-to-cart flow, prerendering the cart page makes it appear to load instantly. Add as a progressive enhancement - browsers without support ignore the script tag entirely.
<script type="speculationrules">
{
"prerender": [
{
"where": { "href_matches": "/cart" },
"eagerness": "moderate"
}
]
}
</script>
Shopify's CDN supports AVIF format through the
image_url filter. AVIF produces 20 to 30 percent smaller files than WebP at equivalent quality. Browser support is now above 90 percent globally. Serve AVIF alongside a WebP fallback using the <picture> element for complete coverage.<picture>
<source
srcset="{{ product.featured_image | image_url: width: 800, format: 'avif' }}"
type="image/avif"
>
<source
srcset="{{ product.featured_image | image_url: width: 800, format: 'webp' }}"
type="image/webp"
>
<img
src="{{ product.featured_image | image_url: width: 800 }}"
width="{{ product.featured_image.width }}"
height="{{ product.featured_image.height }}"
loading="lazy"
alt="{{ product.featured_image.alt | escape }}"
>
</picture>
Pro Tips From Real Shopify Performance Work
TTFB Spikes After App Installations Are Diagnostic
When TTFB increases noticeably after installing an app, the app is running server-side code through Shopify's webhook or script injection system. This is distinct from client-side script loading. If an app's server-side processing is adding to TTFB, contact the app developer - this is often a fixable configuration issue on their end.
CrUX Data Lags Your Improvements by 28 Days
When your field data in Search Console does not match your recent PageSpeed lab improvements, the lag is expected. CrUX data is a 28-day rolling window. Improvements made today will not fully appear in CrUX data for four weeks. Test in lab data to confirm improvements are real. Wait for field data to catch up before evaluating SEO ranking impact.
The Most Overlooked Mobile INP Fix
Stores scoring 80 on mobile PageSpeed but failing INP typically have non-passive scroll and touch event listeners. Adding { passive: true } to every scroll, touchstart, touchmove, and wheel event listener in your theme JavaScript is a 10-minute fix that can drop INP from Poor to Good on stores where JavaScript is otherwise well-optimized.
Performance Budgets Beat Monthly Audits
A performance budget embedded in your deployment workflow catches regressions immediately. A monthly audit catches them after they have affected real users for up to 30 days. If you implement one thing from this guide beyond the technical optimizations, make it a Lighthouse CI check before every theme publish.
Summary
Advanced Shopify performance optimization is precise work. Each technique targets a specific part of the browser loading process - preloading the LCP image gives the browser an early start on the most important resource, preconnecting eliminates connection overhead for critical third parties, critical CSS inlining eliminates render-blocking stylesheet loading, the Speculation Rules API prerenders the next page before the visitor navigates to it, and the LoAF API identifies the exact JavaScript causing INP problems in real user sessions.
For teams managing ongoing performance optimization across multiple pages and optimization layers, Ecom: Page Speed Expert provides Shopify-native tooling that handles the systematic optimization work across images, scripts, and performance monitoring - freeing developer time for the advanced techniques that require deeper intervention.
Start with the fundamental guides in this series if you have not already. Return here when the basics are done. The advanced gains are waiting, but they build on everything that came before.