DEV Community

Magevanta
Magevanta

Posted on • Originally published at magevanta.com

Magento 2 Image Optimization: WebP, Lazy Loading & CDN

Images are typically the single largest contributor to page weight in any e-commerce store. On a typical Magento 2 product page, unoptimized images can easily account for 60–80 % of total transferred bytes. Fixing that is one of the highest-ROI performance wins available — and it's entirely within your control. This guide walks through three complementary strategies: converting to WebP, enabling lazy loading, and offloading delivery to a CDN.

Why Images Hurt Magento Performance So Much

Magento handles a large number of image variants. Every product image is resized into multiple renditions (thumbnail, small_image, base_image, swatch) via the catalog/product/media pipeline. Without intervention, these are served as JPEGs or PNGs — even when the browser supports more efficient formats.

The knock-on effects:

  • LCP (Largest Contentful Paint) suffers when the hero product image is large and uncompressed.
  • CLS (Cumulative Layout Shift) spikes when image dimensions aren't declared in the HTML.
  • TTI (Time to Interactive) is delayed when off-screen images are loaded eagerly, competing with critical resources.

Let's tackle each piece.

1. Serving WebP Images in Magento 2

What WebP gives you

WebP delivers 25–35 % smaller files than JPEG at equivalent visual quality, and 50–80 % smaller than PNG for images with transparency. For a product catalog with hundreds of images, that's a significant bandwidth saving.

Native WebP support (Magento 2.4.4+)

Since Magento 2.4.4, the core image resizer supports WebP natively. The relevant setting is under Stores → Configuration → General → Web → Url Options → Catalog media URL format. Switch this to Image Optimization Parameters (the hash mode). This activates server-side image processing including WebP conversion.

Via CLI:

bin/magento config:set dev/image/default_adapter GD2
bin/magento config:set dev/image/use_webp_image 1
Enter fullscreen mode Exit fullscreen mode

After enabling, flush the image cache:

bin/magento catalog:images:resize
bin/magento cache:flush
Enter fullscreen mode Exit fullscreen mode

Magento will now serve WebP to browsers that support it, falling back to JPEG/PNG for others via the <picture> element.

Manual WebP conversion with cwebp

If you're on an older Magento version, convert images offline and serve them via an Nginx map block:

# Batch convert all product images
find pub/media/catalog/product -name "*.jpg" -exec sh -c \
  'cwebp -q 82 "$1" -o "${1%.jpg}.webp"' _ {} \;
Enter fullscreen mode Exit fullscreen mode

Then in Nginx:

map $http_accept $webp_suffix {
    default   "";
    "~*webp"  ".webp";
}

location ~* \.(png|jpg|jpeg)$ {
    add_header Vary Accept;
    try_files $uri$webp_suffix $uri =404;
}
Enter fullscreen mode Exit fullscreen mode

This zero-overhead approach serves WebP to supporting browsers and falls back gracefully — no PHP involved at request time.

2. Native Lazy Loading

Lazy loading defers off-screen images until the user scrolls near them. Since Chrome 77, Firefox 75, and Safari 15.4, this is supported natively via loading="lazy" — no JavaScript library needed.

Enabling it in Magento

The product image templates live in vendor/magento/module-catalog/view/frontend/templates/product/. The key file for the product page is image_with_borders.phtml.

Create a theme override at:

app/design/frontend/<Vendor>/<Theme>/Magento_Catalog/templates/product/image_with_borders.phtml
Enter fullscreen mode Exit fullscreen mode

Add loading="lazy" to all <img> tags that are not the LCP candidate:

<img
    src="<?= $block->escapeUrl($block->getImageUrl()) ?>"
    width="<?= $block->escapeHtmlAttr($block->getWidth()) ?>"
    height="<?= $block->escapeHtmlAttr($block->getHeight()) ?>"
    alt="<?= $block->escapeHtmlAttr($block->getLabel()) ?>"
    loading="lazy"
    class="<?= $block->escapeHtmlAttr($block->getClass()) ?>"
/>
Enter fullscreen mode Exit fullscreen mode

Important: Do not add loading="lazy" to the first visible product image (the LCP element). The browser needs to start downloading that immediately. Only apply it to gallery thumbnails, related products, and below-the-fold images.

Declaring image dimensions

Always include explicit width and height attributes. Without them, the browser can't reserve space before the image loads, causing layout shift (CLS). Magento's $block->getWidth() and $block->getHeight() already return the configured resize dimensions — use them.

Category listing pages

The category grid template (list.phtml / grid.phtml) often renders 20–40 product thumbnails. Lazy-load all except the first row:

<?php $isFirstRow = ($iterator % $columnCount === 1 && $iterator <= $columnCount); ?>
<img
    ...
    loading="<?= $isFirstRow ? 'eager' : 'lazy' ?>"
/>
Enter fullscreen mode Exit fullscreen mode

3. CDN Configuration for Magento 2

A CDN moves your static assets — images, CSS, JS — to edge nodes close to your visitors. Even with perfectly optimized images, a server in Frankfurt serving customers in Singapore adds 200+ ms of latency per asset. A CDN collapses that to ~20 ms.

Choosing a CDN

For Magento, popular options include Cloudflare (free tier, automatic WebP via Polish), BunnyCDN (~$1/TB, excellent price/performance for self-hosted), Fastly (used by Adobe Commerce Cloud), and AWS CloudFront (1 TB/month free, most flexible). For a self-hosted store, Cloudflare or BunnyCDN are the pragmatic choices.

Configuring Magento to use a CDN URL

In Admin: Stores → Configuration → General → Web → Base URLs (Secure)

Set the media base URL to your CDN subdomain:

https://cdn.yourstore.com/
Enter fullscreen mode Exit fullscreen mode

Or via CLI:

bin/magento config:set web/unsecure/base_media_url https://cdn.yourstore.com/
bin/magento config:set web/secure/base_media_url https://cdn.yourstore.com/
bin/magento cache:flush
Enter fullscreen mode Exit fullscreen mode

Magento will now generate all pub/media URLs pointing at the CDN. Your origin server only serves the assets once; subsequent requests are cached at the edge.

CDN cache headers

Make sure your origin sends proper cache headers so the CDN can cache aggressively:

location ~* \.(jpg|jpeg|png|webp|gif|svg|ico|css|js|woff2)$ {
    expires 1y;
    add_header Cache-Control "public, immutable";
}
Enter fullscreen mode Exit fullscreen mode

Set a long TTL for versioned assets. Magento's static content deployment appends version hashes to CSS/JS, so cache-busting is handled automatically.

Cloudflare Polish (automatic WebP)

If you use Cloudflare, enable Polish (Images → Polish: Lossless or Lossy). It automatically converts and serves WebP to supporting browsers — no server-side changes needed. Combine with Mirage for mobile optimization.

Putting It Together: Expected Gains

Here's what a typical Magento 2 store can expect from implementing all three optimizations:

Metric Before After Improvement
Total image weight (homepage) 2.4 MB 680 KB −72%
LCP 4.2 s 1.8 s −57%
CLS score 0.18 0.02 −89%
Edge latency (media assets) 200+ ms ~20 ms −90%

Quick-Start Checklist

  • [ ] Enable WebP in Magento 2.4.4+ via dev/image/use_webp_image
  • [ ] Run bin/magento catalog:images:resize to regenerate image cache
  • [ ] Add loading="lazy" to all non-LCP product images
  • [ ] Always include explicit width and height on <img> tags
  • [ ] Set CDN base URL in Magento config
  • [ ] Configure long Cache-Control: immutable headers on media
  • [ ] Enable Cloudflare Polish or BunnyCDN Image Optimizer if using those CDNs

Conclusion

Image optimization in Magento 2 is a three-layer problem: format (WebP), loading strategy (lazy), and delivery (CDN). None of these require expensive extensions — they're configuration and template changes any developer can make in an afternoon. Combined, they can cut image-related page weight by 70 %+ and shave seconds off your LCP. In an environment where every 100 ms of load time costs conversion rate, that's not just a technical win — it's a business one.

Start with WebP (lowest effort, highest gain), then tackle lazy loading (quick template edit), and finally wire up a CDN for global edge delivery. Your Lighthouse score — and your customers — will thank you.

Top comments (0)