Storefront Performance
Core Web Vitals on a Magento page: what Luma's JavaScript costs, how critical CSS helps, and which page to measure.
bin/magento config:set dev/css/use_css_critical_path 1Core Web Vitals are three metrics measured from real visits at the 75th percentile. LCP, largest contentful paint, is good at 2.5 seconds or less. INP, interaction to next paint, is good at 200 milliseconds or less; it replaced FID in March 2024. CLS, cumulative layout shift, is good at 0.1 or less.
What Luma costs
Luma's JavaScript is many small files behind RequireJS, and a UI component cannot initialise until its dependencies have all loaded. That shows up as INP on mid-range phones. Private data — cart count, customer name — is not in the cached HTML; it arrives in a separate uncached request to /customer/section/load after first render, which is correct behaviour and also a source of layout shift if the markup has no reserved space.
The levers
Production mode and deployed static content first, then: critical CSS, enabled with dev/css/use_css_critical_path, which inlines the minified contents of the theme's web/css/critical.css and loads the rest asynchronously; correct image dimensions in etc/view.xml, applied with bin/magento catalog:images:resize; Varnish as the full page cache; JavaScript minification. Each config change needs bin/magento cache:flush, and a CSS or view.xml change needs bin/magento setup:static-content:deploy.
Measure a real page
Measure a product page and a category page. The homepage is often hand-built, lighter than everything else, and the least representative page on the site. Test logged out, twice, and read the second run — the first request after a flush is a cache miss. Compare lab runs with field data before you act on either.
Example
# Stores > Configuration > Advanced > Developer is hidden in production mode,
# so these settings are applied from the CLI.
bin/magento config:set dev/css/use_css_critical_path 1
# Value was saved.
bin/magento config:set dev/js/minify_files 1
# Value was saved.
bin/magento setup:static-content:deploy -f en_US
# Deploy using quick strategy
# ... (several minutes)
bin/magento cache:flush
# Flushed cache types:
# config
# layout
# block_html
# ...When to use it
- Explain why a storefront passes Lighthouse but fails Core Web Vitals in the field.
- Cut LCP on a category page without changing the theme's stack.
- Turn on critical CSS on a production store where the Developer config section is hidden.
- Confirm Varnish is serving product pages from cache after a deploy.
More examples
The critical CSS the theme inlines
With dev/css/use_css_critical_path set to 1, this file is minified and inlined into the head, and the rest of the stylesheets load asynchronously, which improves first render. Generate it with a tool such as Penthouse rather than by hand. If your theme has no critical.css but Luma does, Luma's file is used instead. The change needs setup:static-content:deploy and cache:flush. Reserving the image's aspect ratio here is what keeps CLS down.
/* app/design/frontend/Acme/storefront/web/css/critical.css */
/* Above-the-fold rules only: header, hero, first product image. */
body {
margin: 0;
font-family: system-ui, sans-serif;
}
.page-header {
min-height: 96px;
}
.product.media img {
aspect-ratio: 4 / 5;
width: 100%;
height: auto;
}Image sizes, where LCP bytes come from
The LCP element on a category page is almost always a product image, and Magento serves whatever size this file asks for. After editing view.xml run bin/magento catalog:images:resize to generate the new cached files and bin/magento setup:static-content:deploy, then bin/magento cache:flush. Without the resize the storefront keeps serving the old dimensions and nothing appears to change.
<!-- app/design/frontend/Acme/storefront/etc/view.xml -->
<view xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="urn:magento:framework:Config/etc/view.xsd">
<media>
<images module="Magento_Catalog">
<image id="category_page_grid" type="small_image">
<width>300</width>
<height>375</height>
</image>
</images>
</media>
</view>Checking a page is actually being cached
X-Magento-Cache-Debug comes from Varnish and only appears for clients inside the debug ACL in the generated VCL. Always send a Host header when testing against 127.0.0.1: Magento builds URLs from the request host, so a hostless request can poison cached markup with 127.0.0.1 links. A page that is always a MISS is usually sending a session or a Vary header it should not.
curl -s -o /dev/null -D - \
-H 'Host: www.example.com' \
http://127.0.0.1/joust-duffle-bag.html
# HTTP/1.1 200 OK
# X-Magento-Cache-Debug: MISS
# Content-Type: text/html; charset=UTF-8
Discussion