Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
81 changes: 68 additions & 13 deletions guides/plugins/plugins/content/media/remote-thumbnail-generation.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
# Remote Thumbnail Generation

::: info
This feature is available starting with Shopware version 6.6.4.0
This feature is available starting with Shopware version 6.6.4.0. The `{mediaUpdatedAt}` pattern variable was added in 6.6.5.0, and the `ResolveRemoteThumbnailUrlExtension` hook was added in 6.7.1.0.
:::

In certain scenarios, you might want to disable the filesystem thumbnail generation in Shopware and use an external CDN service to handle the thumbnails.
Expand All @@ -27,19 +27,24 @@
pattern: '{mediaUrl}/{mediaPath}?width={width}&ts={mediaUpdatedAt}'
```

1. `shopware.media.remote_thumbnails.enable`: Set this parameter to `true` to enable remote thumbnails.

2. `shopware.media.remote_thumbnails.pattern`: This parameter defines the URL pattern for your remote thumbnails. Replace it with your actual URL pattern.
| Key | Type | Default | Description |
|--------------------------------------------|--------|------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `shopware.media.remote_thumbnails.enable` | bool | `false` | Master switch. When `true`, Shopware stops creating, persisting, and deleting thumbnail files and database records and instead synthesizes thumbnail URLs from the configured pattern at request time. |
| `shopware.media.remote_thumbnails.pattern` | string | `{mediaUrl}/{mediaPath}?width={width}&ts={mediaUpdatedAt}` | Template used to build thumbnail URLs. Variables are listed below. |

The pattern supports the following variables:

* `mediaUrl`: The base URL of the media file.
* `mediaPath`: The media file path relative to the `mediaUrl`.
* `width`: The width of the thumbnail.
* `height`: The height of the thumbnail.
* `mediaUpdatedAt`: The timestamp of the last media change.
* `mediaUrl`: The base URL of the public filesystem (`shopware.filesystem.public`). For media whose `path` is already absolute (e.g., uploaded by URL), `{mediaUrl}` is replaced with an empty string and the leading slash is trimmed automatically.
* `mediaPath`: The media file path relative to `mediaUrl` (for example `media/ab/cd/file.jpg`).
* `width`: The width from the assigned `media_thumbnail_size` of the media folder.
* `height`: The height from the assigned `media_thumbnail_size` of the media folder.
* `mediaUpdatedAt`: Unix timestamp of `media.updatedAt`, falling back to `media.createdAt`. Empty string when both are null. Useful as a cache-buster (Shopware versions older than 6.6.5.0 do not support this variable).

For example, with the default pattern `{mediaUrl}/{mediaPath}?width={width}&ts={mediaUpdatedAt}`, the thumbnail URL is generated as `https://yourshop.example/abc/123/456.jpg?width=80&ts=1718954838`.

For example, by default, the pattern was set as `{mediaUrl}/{mediaPath}?width={width}&ts={mediaUpdatedAt}`, the thumbnail URL would be generated as `https://yourshop.example/abc/123/456.jpg?width=80&ts=1718954838`.
::: warning
Private media is intentionally excluded from remote URL generation: it keeps using its signed local URL even while the feature is enabled. Plan accordingly if your shop relies on private media.
:::

## Usage

Expand All @@ -48,18 +53,68 @@

Please note that the external service needs to be able to handle the URL pattern and generate the appropriate thumbnails based on the provided parameters.

## Behavior when enabled

Enabling `shopware.media.remote_thumbnails.enable` short-circuits multiple subsystems. Plugins that interact with thumbnails directly should be aware of the changes:

* **`ThumbnailService`** — `generate()`, `updateThumbnails()`, and `deleteThumbnails()` throw `MediaException::thumbnailGenerationDisabled()` (HTTP 400, error code `MEDIA_THUMBNAIL_GENERATION_DISABLED`). Guard custom code that calls these methods.
* **Message handler** — `GenerateThumbnailsHandler` and `UpdateThumbnailsHandler` consume but no longer process `GenerateThumbnailsMessage` / `UpdateThumbnailsMessage`.
* **Media indexer** — `MediaIndexer::iterate`, `update`, and `handle` early-return, so no `MediaIndexingMessage` is enqueued and `media.thumbnails_ro` is not maintained (since 6.7.1.0). Running `bin/console dal:refresh:index media.indexer` is a no-op.

Check warning on line 62 in guides/plugins/plugins/content/media/remote-thumbnail-generation.md

View workflow job for this annotation

GitHub Actions / LanguageTool

[LanguageTool] guides/plugins/plugins/content/media/remote-thumbnail-generation.md#L62

If a new sentence starts here, add a space and start with an uppercase letter. (LC_AFTER_PERIOD[1]) Suggestions: ` Indexer`, ` indexer` Rule: https://community.languagetool.org/rule/show/LC_AFTER_PERIOD?lang=en-US&subId=1 Category: CASING
Raw output
guides/plugins/plugins/content/media/remote-thumbnail-generation.md:62:227: If a new sentence starts here, add a space and start with an uppercase letter. (LC_AFTER_PERIOD[1])
 Suggestions: ` Indexer`, ` indexer`
 Rule: https://community.languagetool.org/rule/show/LC_AFTER_PERIOD?lang=en-US&subId=1
 Category: CASING
* **CLI commands** — `bin/console media:generate-thumbnails` aborts with `Command::FAILURE` and prints `Remote thumbnails are enabled. Skipping thumbnail generation.`
* **`FileSaver`** — uploads no longer dispatch a `GenerateThumbnailsMessage`, and renames skip the thumbnail-rename step (which would otherwise fail because no local thumbnail files exist).
* **`MediaDeletionSubscriber`** — original files are still deleted, but the thumbnail-file enumeration and the `media_thumbnail` row deletion are skipped.
* **`MediaUrlLoader`** — listens to `media.loaded` / `media.partial_loaded` and delegates to `RemoteThumbnailLoader`, which synthesizes a `MediaThumbnailCollection` in memory based on the media folder's configured `media_thumbnail_size` entries.

::: warning
The synthesized `MediaThumbnailEntity` instances are assigned a fresh random UUID on every request and are not persisted. Do not rely on `media_thumbnail.id` being stable, and do not join other tables against it while the feature is on.
:::

## Cleaning up local thumbnails

If you switch from local to remote thumbnails on an existing shop, the previously generated `media_thumbnail` records and files remain in storage. Shopware ships a CLI command (added in 6.6.6.0) that removes them:

```bash
bin/console media:delete-local-thumbnails
```

The command only runs when remote thumbnails are enabled. It deletes every row from `media_thumbnail`, removes the corresponding files via the configured filesystem adapter, and clears the serialized `media.thumbnails_ro` column so that the storefront immediately stops referencing stale entries.

## Extending the URL generation

`RemoteThumbnailLoader` dispatches the URL through the `ResolveRemoteThumbnailUrlExtension` (extension name `remote_thumbnail_url.resolve`, available since 6.7.1.0). Apps and plugins can subscribe to it to:

* rewrite the URL (for example, sign it, route it through an image-resizing proxy, swap the host per sales-channel),
* return `null` to skip a thumbnail entirely (since 6.7.3.0),
* read the full `MediaEntity` to make decisions based on `mimeType`, custom fields, or folder configuration.

Subscribe to the extension via the standard extension event mechanism. Do not modify `MediaUrlLoader` or `RemoteThumbnailLoader` directly; both are marked `@final` and may change.

## Invalidating Thumbnails with Fastly

If you are using Fastly as your CDN, you can let Shopware invalidate the cached thumbnails when the media is updated.
To do this, you need to configure your Fastly API key in your `config/packages/shopware.yaml`:
If you are using Fastly as your CDN, you can let Shopware invalidate the cached thumbnails when media is updated.
To do this, configure your Fastly API key in your `config/packages/shopware.yaml`:

```yaml
shopware:
cdn:
fastly:
api_key: YOUR_FASTLY_API_KEY
api_key: '%env(FASTLY_API_KEY)%'
soft_purge: false
max_parallel_invalidations: 2
```

| Key | Type | Default | Description |
|--------------------------------------------------|-------------|---------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `shopware.cdn.fastly.api_key` | string | `''` | Personal Fastly token. **Setting this to a non-empty value is what activates media invalidation** — there is no separate `enabled` flag. With an empty key, the listener silently no-ops. |
| `shopware.cdn.fastly.soft_purge` | bool/string | `false` | Sent verbatim as the `fastly-soft-purge` request header. Set to `true` (or `'1'`) to keep serving stale content while purges propagate. |
| `shopware.cdn.fastly.max_parallel_invalidations` | int | `2` | Guzzle pool concurrency for purge requests. Bounds how many `POST https://api.fastly.com/purge/{url}` calls are in flight simultaneously. |

When media changes (path change, file update, or deletion), the `BanMediaUrl` listener resolves all affected URLs and dispatches them through `FastlyMediaReverseProxy`, which sends one purge request per URL. Failures are logged at `critical` level and do not block the write process.

::: info
This is the **media-cache** Fastly configuration node. It is independent from `shopware.http_cache.reverse_proxy.fastly`, which configures the storefront's HTTP-cache invalidation gateway and exposes additional options such as `service_id`, `instance_tag`, and `tag_prefix`. See [Reverse HTTP cache](../../../../hosting/infrastructure/reverse-http-cache.md#configure-fastly) for the storefront side.

Check warning on line 115 in guides/plugins/plugins/content/media/remote-thumbnail-generation.md

View workflow job for this annotation

GitHub Actions / LanguageTool

[LanguageTool] guides/plugins/plugins/content/media/remote-thumbnail-generation.md#L115

The usual collocation for “independent” is “of”, not “from”. Did you mean “independent of”? (INDEPENDENTLY_FROM_OF[120]) Suggestions: `independent of` Rule: https://community.languagetool.org/rule/show/INDEPENDENTLY_FROM_OF?lang=en-US&subId=120 Category: COLLOCATIONS
Raw output
guides/plugins/plugins/content/media/remote-thumbnail-generation.md:115:45: The usual collocation for “independent” is “of”, not “from”. Did you mean “independent of”? (INDEPENDENTLY_FROM_OF[120])
 Suggestions: `independent of`
 Rule: https://community.languagetool.org/rule/show/INDEPENDENTLY_FROM_OF?lang=en-US&subId=120
 Category: COLLOCATIONS
:::

## Conclusion

By using remote thumbnails, you can offload the task of thumbnail generation to an external service, potentially improving the performance and scalability of your Shopware installation.
Loading