Store speed is usually an architecture problem
Compressing images is where most speed work stops. What actually decides a product page is the scripts, sections and queries somebody chose to add.
AnalysisJune 24, 20268 min read
Compressing images is where speed work stops for most stores. It should be where it starts, and it should take an afternoon. Everything expensive about a slow Shopify store is a decision someone made about architecture: how many apps inject a script, how many sections the homepage renders, what the collection template asks Liquid to loop over, and which third parties get to block the first paint. None of that is fixed by a better image format.
This is an argument, so let me state it plainly. Image weight is the cheapest problem you have, and it gets all the attention because it is the only one a tool will name for you. The costs that decide whether a product page feels fast are not files. They are choices, and somebody has to be willing to reverse them.
What compressing images actually buys you
Real bytes, and it is worth doing. Serve responsive sizes, let Shopify's image CDN do the resizing, use modern formats, set explicit width and height so the layout does not shift, and lazy-load everything below the fold while eagerly loading the one image that is the largest element on screen. That last part is where most stores get it backwards: lazy-loading the hero image is a way to make Largest Contentful Paint worse while feeling like you optimized something.
Do all of that and you have removed weight from the network. What you have not touched is time — the interval between the browser holding your HTML and the page being usable. That interval is mostly script execution and blocking requests, and it does not care how small your JPEGs are.
The costs that are actually on the page
Scripts you did not write
Every app that touches the storefront adds something: a script tag, a theme app extension, a pixel, or an embedded block that pulls its own bundle. Individually each is small, and each vendor will tell you it is asynchronous. Collectively they are the largest chunk of main-thread work on most stores, and asynchronous does not mean free — it means the parser is not blocked while the file downloads. It executes anyway, and it competes with everything else for the same single thread.
The number worth writing down is not milliseconds. It is the count. How many distinct third-party origins does a product page contact? Count them on your own store; the number is almost always higher than the team guesses. The exercise that produces the most speed per hour of work is not tuning; it is opening the app list and asking, for each entry, what it does and whether anyone would notice if it were gone. Uninstalling an app that stopped being used two seasons ago is worth more than a week of micro-optimization, and it is often the only intervention that moves the field data at all.
Be careful with the uninstall. Removing an app does not always remove its script tag or its theme edits, and a storefront can carry the ghost of three apps nobody has installed since last year. Search for the vendor's domain after every removal.
The render-blocking few
A handful of things genuinely block the first paint rather than merely competing for the thread: a synchronous script in the head, a stylesheet from an external origin, a webfont with no fallback strategy, a consent banner that gates rendering, and an A/B testing snippet installed the recommended way — synchronously, at the top, so the page does not flash the control before the variant applies.
That last one is a deliberate trade rather than a mistake: you are paying first-paint time to avoid a flicker. It is the right call while an experiment runs and the wrong one as a permanent fixture on a store testing nothing. If you do experimentation seriously, the snippet earns its cost during a test and is worth reconsidering between them.
The homepage that renders everything
Sections are the best thing about modern Shopify themes and the easiest thing to abuse. A merchant who can add a section will add sections, and a homepage that started with five ends up with eighteen: three carousels, two video embeds, a reviews widget, a social feed, a countdown timer and a slider of nine collections.
Every one of those is markup rendered server-side, assets requested client-side, and often a JavaScript component initializing on load whether or not anyone scrolls to it. The homepage is also the page most measured and least likely to be where a purchase happens, which is why it accumulates weight nobody defends.
The fix is structural rather than clever. Sections below the fold should not initialize their JavaScript until they are near the viewport. Carousels should not exist in triplicate. An embedded social feed is a third-party iframe with its own framework inside it. This is ordinary theme work, not a specialist discipline, and it is where the largest wins live.
Liquid that loops the whole catalog
This one does not show up in a browser at all, because it happens before the response is sent. A product loop that walks every variant and every option inside it, a metafield read performed once per iteration, a mega-menu that resolves every child of every link on every request — each of these is server time, server time is time to first byte, and time to first byte is the floor under every other metric on the page.
A slow template usually renders successfully, so the failure mode is not an error — it is a template that costs several times its sibling on every request, and nobody notices because the browser waterfall looks identical apart from one long first bar. Measure time to first byte per template — home, collection, product, cart — and treat a slow template as a query problem, not a front-end one.
Scroll the figure sideways to see all of it
A fast lab score is not a fast product page
Lab tools run one URL, on one simulated device, from one location, with no cookies and no cart. Your visitors arrive on a product page, on real hardware with twenty tabs open, often with a populated cart, frequently from an ad whose tracking parameters trigger a different code path.
A homepage that scores well while product pages score badly is what you would expect, because the product page carries the reviews widget, the variant picker, the upsell block and the pixel that fires on view. Lab tools also cannot see interaction latency for a real person tapping a variant swatch. That shows up in field data as Interaction to Next Paint, and it is the metric that most often disagrees with a good lab score.
Use lab tools as a debugger, never as a scoreboard. They tell you what happened in one run, and they are excellent at that. They do not tell you whether your store is fast.
Scroll the figure sideways to see all of it
What to actually measure
Four things, in this order.
- Field Core Web Vitals, segmented by template. One number for the whole site hides the fact that the product page is the slow one. Split home, collection, product and cart, and split mobile from desktop.
- Third-party request count and origin count, per template. Counts are stable, easy to compare over time, and they map directly onto a decision a person can make: remove the app, or keep it and say why.
- Time to first byte per template. The Liquid problem is invisible in every other metric, and it moves the floor for all of them.
- Interaction to Next Paint on the product page. Specifically on the variant selector and the add-to-cart button, because those are the interactions that carry the money.
Notice what is not on that list: a score. A score compresses several unrelated things into one number that moves for reasons you cannot act on. We do this as performance optimization, and the deliverable is always a list of removals and rewrites, not a screenshot of a green circle.
Segment all four by traffic source before you attribute any movement to your work. Paid social arrives on lower-end devices than organic search, so shifting budget between channels moves the field metrics on its own.
The uncomfortable conclusion
Most stores could get faster by deleting things. Not by refactoring, not by adopting a framework, not by moving to a headless build — by removing four apps, cutting the homepage in half, and fixing one collection template that loops the catalog. That work is unglamorous, it produces no artefact anyone can put in a deck, and it is almost always the correct answer.
And no, going headless is not a speed strategy. It is a different architecture with a different set of costs, and a custom storefront carrying the same third-party scripts is the same slow page with a better build pipeline. The scripts were the problem. The build was never the problem.
The stores that are genuinely fast are not the ones with the best-compressed images. They are the ones where somebody said no often enough that there was not much left to load.