SW Shopware 6 Intermediate

DataFirefly Image Optimizer — Shopware 6 Documentation

Installation, WebP/AVIF configuration, CDN integration, Twig API and troubleshooting for the Image Optimizer plugin for Shopware 6.6 and 6.7.

Updated Module version 1.0.0

DataFirefly Image Optimizer automatically transforms every Shopware media image into WebP and AVIF variants, re-compresses original JPEGs and PNGs, and rewrites URLs to your CDN — without touching your theme. This documentation covers installation, full configuration, the Twig API exposed to themes, and troubleshooting.

Installation

The plugin ships as a ZIP. Two installation methods, functionally equivalent.

Via Shopware administration

  1. Settings → System → Extensions → Upload extension
  2. Select DfImageOptimizer-1.0.0.zip
  3. Click Install then Activate
  4. Clear cache: Settings → System → Cache & Indexes → Clear
cd /path/to/shopware
unzip DfImageOptimizer-1.0.0.zip -d custom/plugins/

sudo -u www-data setsid php bin/console plugin:refresh
sudo -u www-data setsid php bin/console plugin:install --activate DfImageOptimizer
sudo -u www-data setsid php bin/console cache:clear
sudo -u www-data setsid php bin/console theme:compile
Tip. theme:compile is mandatory after installation so the Twig override for the thumbnail component becomes active on the storefront. Without it, <picture> tags won’t be generated even if WebP and AVIF files are produced.

Post-install verification

Go to admin menu Catalogues → Image Optimizer. The dashboard should display with a Server capabilities card showing in real time:

  • Detected PHP version (8.2 minimum)
  • Imagick presence (recommended)
  • GD presence (required)
  • Effective WebP support — should be ✓
  • Effective AVIF support — may be ✗ depending on server, not blocking
  • Recommended engine: Imagick or GD

Architecture in two words

When an image is uploaded, the plugin listens to the entity media.written event, loads the image into a local temp file, then produces in parallel:

  • The re-compressed original (replaces the file if Compress original is checked)
  • A WebP sibling alongside with cumulative extension: foo.jpgfoo.jpg.webp
  • An AVIF sibling alongside: foo.jpgfoo.jpg.avif

Shopware thumbnails generated by the native ThumbnailService are processed the same way. On the storefront, the Twig override of storefront/component/image/thumbnail.html.twig wraps the <img> tag in a <picture> with AVIF, WebP and original-fallback sources — the browser automatically picks the lightest format it supports.

Configuration

Access: Settings → System → Extensions → DfImageOptimizer → Configure. Seven cards group the options.

“General” card

Option Default Effect
Auto-optimize on upload Enabled Triggers the pipeline immediately on every upload. Disable if you’d rather process everything in the background via cron.
Process thumbnails Enabled Generates WebP/AVIF for Shopware thumbnails too (typically 4 to 6 sizes per source image).

“WebP” card

Option Default Recommendation
Enable WebP Enabled Keep enabled except in very specific cases. WebP is supported by 96% of browsers.
WebP quality (1-100) 82 75-85 for good balance. 90+ for high-end photography, 70 for a heavy catalog.
Lossless for PNG Disabled Enable only if your PNGs contain text or sharp graphics (logos, icons). Otherwise lossy mode gives better gains.

“AVIF” card

Option Default Recommendation
Enable AVIF Disabled Enable if the dashboard indicates your server supports AVIF. Typical 50% gain vs JPEG, but encoding is slower than WebP.
AVIF quality (1-100) 55 45-65 for excellent rendering. AVIF tolerates lower quality than JPEG/WebP thanks to its modern codec.
Maximum width for AVIF (px) 2400 CPU safeguard. Images above this width are skipped for AVIF but keep their WebP. Increase if you have a powerful server and need AVIF on large images.
About AVIF. AVIF encoding requires either PHP 8.1+ with the IMG_AVIF flag compiled in, or Imagick with libheif. The Server capabilities dashboard tells you exactly what’s available. If AVIF is not supported, the option stays inoperative even when checked — no error, just no AVIF generation.

“Compression” card

Option Default Recommendation
Compress original JPG/PNG Enabled Replaces the original with its re-compressed version. Irreversible — disable if you want to keep raw sources for future editing.
JPEG quality (1-100) 85 85 is the web photo standard. Drop to 80 for extra gain if quality stays visually acceptable.
PNG compression level (0-9) 7 9 = maximum compression but 3-4× slower. 7 is the standard balance.
Strip EXIF/ICC metadata Enabled Typical gain 5 to 30 KB per camera-output photo. Keep if you manage content needing precise color profiles.

“CDN” card

Option Default Explanation
Enable CDN URL rewriting Disabled If disabled, URLs point to your origin. Enable after configuring your CDN.
CDN base URL Format: https://cdn.example.com with no trailing slash. E.g. https://shop-cdn.b-cdn.net for BunnyCDN.
Rewrite scope Media only See details below.
Preserve query strings Enabled Keeps cache-busting parameters (?v=1234) when rewriting.

The three scopes in detail:

  • Media only — rewrites only URLs starting with /media/. Safest, covers 95% of typical use cases.
  • Media + thumbnails — adds /thumbnail/. Useful if your storefront serves many dynamically generated thumbnails.
  • All static assets — adds /theme/, /bundles/ and /assets/. Only choose this if your CDN is correctly configured to pull-cache all assets and you’ve tested in staging.

“Frontend rendering” card

Option Default Effect
Output as <picture> tag Enabled Wraps storefront <img> tags in a <picture> with AVIF/WebP sources.
Add loading="lazy" Enabled Native browser lazy-loading. Keep unless you have your own solution.
Add decoding="async" Enabled Lets the browser decode in parallel with HTML parsing.
Force width/height Enabled Anti-CLS (Cumulative Layout Shift). Browser reserves space before image loads.

“Batch processing” card

Option Default Recommendation
Batch size for cron task 50 50 is a good balance. Increase to 100-200 if you need to catch up a large catalog quickly and your server can handle it.
Cron interval (minutes) 15 Information only — actual interval is defined by OptimizeImagesTask::getDefaultInterval(). To actually change, modify the value in the scheduled_task table or reinstall the plugin after change.

Configure a CDN — concrete examples

  1. Create a Pull Zone on bunny.net with your origin URL, e.g. https://shop.example.com
  2. BunnyCDN gives you a hostname like shop-cdn.b-cdn.net
  3. In the plugin config, set: https://shop-cdn.b-cdn.net
  4. Choose Media only scope to start
  5. Enable CDN rewriting

The plugin automatically injects <link rel="dns-prefetch" href="https://shop-cdn.b-cdn.net"> and <link rel="preconnect" href="https://shop-cdn.b-cdn.net" crossorigin> in the storefront <head> — saving 50 to 200 ms on the first CDN request.

Cloudflare

Cloudflare in standard DNS proxy mode doesn’t need CDN rewriting — Cloudflare automatically caches on your main hostname. But if you use a dedicated Cloudflare Custom Hostname for assets (e.g. cdn.example.com), configure it here. Also enable Cloudflare Cache Reserve or Polish to get Cloudflare optimization on top of yours.

KeyCDN

Same configuration as BunnyCDN: create a Pull Zone, get an URL like shop-12345.kxcdn.com, configure it in the plugin with the https:// prefix.

AWS CloudFront

Create a CloudFront distribution with your Shopware server as origin. Distribution URL is like https://d1234abc.cloudfront.net — or your custom domain if you’ve configured an alias. Set minimum TTL to 1 day to fully benefit from caching.

Detailed optimization pipeline

For each image (original or thumbnail) to process, the plugin runs these steps in order:

  1. Download source image from Shopware public filesystem to local temp file (/tmp/dfimgopt_xxx.jpg)
  2. If Compress original enabled: in-place re-compression with configured quality, metadata stripping if enabled. If the re-compressed version is smaller than the original, it replaces the source file on the filesystem.
  3. If WebP enabled: conversion to WebP, write sibling foo.jpg.webp to public filesystem
  4. If AVIF enabled and width ≤ max width: conversion to AVIF, write sibling foo.jpg.avif
  5. Record in df_image_optimizer table with counters and bytes saved
  6. Cleanup local temp file via finally block (even on error)

Imagick is used by priority when available (superior quality and the only AVIF engine via libheif on many servers). GD takes over otherwise — it has supported WebP for a long time and AVIF since PHP 8.1.

Original JPEG compression — irreversible. When the Compress original option is checked, the compressed version replaces the original on the filesystem. If you need to retrieve raw sources for other uses (printing, editing), disable this option — you’ll still get gains via WebP and AVIF.

Scheduled task — catching up existing images

Enabling the plugin on a shop already containing thousands of images doesn’t trigger retroactive optimization. This is intentional: converting 50,000 images to AVIF at once would saturate your server. Instead, the scheduled task df_image_optimizer.optimize_pending runs every 15 minutes by default:

  1. SQL query via LEFT JOIN on df_image_optimizer to identify not-yet-optimized media
  2. Process a batch of 50 images (configurable batch size)
  3. Finish and free the worker for the next task

On a shop with 10,000 images, expect about 50 hours to fully catch up in the background. To speed up:

  • Increase batch size in config (try 100 or 200)
  • Use the Run batch button on the dashboard several times in a row
  • Run the task in a loop manually via CLI:
    for i in {1..100}; do sudo -u www-data setsid php bin/console scheduled-task:run-single df_image_optimizer.optimize_pending; done

Twig API exposed to themes

Two Twig helpers are registered and usable in any theme or plugin template.

Filter |df_cdn

Rewrites a URL to the CDN if enabled, otherwise returns the URL unchanged. Useful for assets you include manually.

<img src="{{ media.url|df_cdn }}" alt="...">
<link rel="preload" as="image" href="{{ heroImage.url|df_cdn }}">
<style>
    .hero { background-image: url("{{ bgImage.url|df_cdn }}"); }
</style>

Function df_picture()

Renders a full <picture> tag with AVIF, WebP and original-fallback sources, plus all configured attributes (lazy, async, width/height).

{{ df_picture(
    media,
    alt='Accessible description',
    classes='product-image card-img',
    sizes='(max-width: 768px) 100vw, 50vw'
) }}

Generates:

<picture>
    <source type="image/avif"
            srcset="https://cdn.example.com/media/foo.jpg.avif"
            sizes="(max-width: 768px) 100vw, 50vw">
    <source type="image/webp"
            srcset="https://cdn.example.com/media/foo.jpg.webp"
            sizes="(max-width: 768px) 100vw, 50vw">
    <img src="https://cdn.example.com/media/foo.jpg"
         alt="Accessible description"
         class="product-image card-img"
         sizes="(max-width: 768px) 100vw, 50vw"
         loading="lazy"
         decoding="async"
         width="1200"
         height="800">
</picture>

Admin API endpoints

Three REST endpoints are available, authenticated via the standard admin Bearer token.

Method Route Description
GET /api/_action/df-image-optimizer/stats Overview + 30-day activity + server capabilities
POST /api/_action/df-image-optimizer/run-batch Runs a batch. Optional POST parameter batchSize (default 50, max 500)
GET /api/_action/df-image-optimizer/capabilities Server detection (Imagick / GD / WebP / AVIF)

curl example:

TOKEN=$(curl -s -X POST https://shop.example.com/api/oauth/token 
    -H "Content-Type: application/json" 
    -d '{"grant_type":"password","client_id":"administration","scope":"write","username":"admin","password":"shopware"}' 
    | jq -r .access_token)

curl -X POST https://shop.example.com/api/_action/df-image-optimizer/run-batch 
    -H "Authorization: Bearer $TOKEN" 
    -H "Content-Type: application/json" 
    -d '{"batchSize":200}'

Tables created

df_image_optimizer

One row per optimized media. Unique key on media_id — re-optimization of the same media overwrites the row.

id                BINARY(16)   UUID
media_id          BINARY(16)   FK media.id ON DELETE CASCADE, UNIQUE
has_webp          TINYINT(1)
has_avif          TINYINT(1)
compressed        TINYINT(1)
original_size     BIGINT       Original weight in bytes
bytes_saved       BIGINT       Cumulative savings (compression + WebP/AVIF delta)
sales_channel_id  BINARY(16)   FK sales_channel.id ON DELETE SET NULL
optimized_at      DATETIME(3)
created_at        DATETIME(3)

df_image_optimizer_log

Optional error journal. Read-only for debugging — no automatic purge.

Troubleshooting

“Dashboard shows AVIF: Not available”

Your server doesn’t have the required AVIF stack. Options:

  • GD-only: check php -m | grep gd and php -i | grep AVIF. You need PHP 8.1+ and GD compiled with --with-avif. On recent Debian/Ubuntu, it’s default.
  • Imagick available: check php -r "print_r(Imagick::queryFormats('AVIF'));". Empty? Your Imagick is not compiled with libheif — recompilation needed or switch to GD.
  • Acceptable fallback: leave AVIF disabled and focus on WebP. WebP-only gain is already huge compared to native Shopware JPEG.

.webp files are generated but storefront still shows JPEG”

The theme compiler hasn’t picked up the Twig override. Fix:

sudo -u www-data setsid php bin/console theme:compile
sudo -u www-data setsid php bin/console cache:clear

Then verify with browser DevTools (Chrome or Firefox): open the Network tab, reload a product page, and look at MIME types of loaded images. You should see image/avif or image/webp instead of image/jpeg.

“Media upload has become slow”

AVIF conversion in particular is CPU-intensive — expect 1 to 3 seconds per image. If that’s annoying, disable auto-optimization on upload (General card) and let only the scheduled task process in background. Uploads become instant again and images are optimized within 15 minutes max.

“Thumbnail .webp files are not generated”

Verify Process thumbnails is checked in the General card. Then manually regenerate thumbnails so they go through the pipeline:

sudo -u www-data setsid php bin/console media:generate-thumbnails

“How do I clear all generated WebP/AVIF files?”

The plugin doesn’t delete them automatically, even on uninstall (to preserve your backups). To clean manually:

cd /path/to/shopware
find public/media -name "*.webp" -delete
find public/media -name "*.avif" -delete

“CDN URLs are not applied everywhere”

Check the configured scope. If you see origin URLs for theme assets (/theme/.../style.css), that’s normal with the default Media only scope. Switch to All static assets if your CDN is configured to serve all assets.

Note also that rewritten URLs concern server-side Twig rendering. If your frontend calls the Store-API and rebuilds URLs client-side in JS, you’ll need to apply rewriting on the client separately.

Uninstallation

sudo -u www-data setsid php bin/console plugin:uninstall DfImageOptimizer
sudo -u www-data setsid php bin/console plugin:remove DfImageOptimizer

On uninstall, Shopware asks whether to keep user data:

  • Keep (default): the df_image_optimizer and df_image_optimizer_log tables stay in the database. Reinstalling the plugin resumes the history.
  • Don’t keep: both tables are dropped (DROP TABLE).

In both cases, .webp and .avif files on the filesystem remain — use the find commands above to clean them up if needed.

Going further

  • Watch your Core Web Vitals score in Google Search Console — LCP should drop within 2 to 4 weeks after activation
  • Test with PageSpeed Insights before/after — typical gain 20 to 40 points on mobile
  • Also enable HTTP/2 or HTTP/3 server-side to multiply the CDN benefit
  • Combine with a Shopware full-page cache for static response times
Was this page helpful?

Still stuck? Contact support