The
netresearch/nr-image-optimize
extension provides
on-demand image optimization for TYPO3. Images are processed
lazily via middleware when first requested, with support for
modern formats (WebP, AVIF), responsive srcset generation,
and automatic format negotiation.
Introduction
Learn what the extension does, its features, and
system requirements.
Installation
Install via Composer or the Extension Manager.
Configuration
Configure the SourceSetViewHelper, render modes,
responsive srcset, and lazy loading.
Usage
Integrate responsive images into your Fluid
templates with practical examples.
Maintenance
Manage processed images and check system
requirements in the backend module.
Developer reference
Architecture overview and PHP API reference.
Introduction
What does it do?
The |extension_name| extension (|extension_key|) optimizes images
in TYPO3 on demand. Instead of processing every image at upload
time, images are converted and resized lazily when first
requested through the /processed/ URL path.
This approach reduces server load during content editing and
ensures that only images actually viewed by visitors are
processed.
Features
Lazy image processing. Images are optimized only when
a visitor first requests them.
Modern format support. Automatic WebP and AVIF
conversion with fallback to original formats.
Responsive images. Built-in SourceSetViewHelper for
srcset and sizes generation.
Render modes. Choose between cover and fit
resize strategies.
Width-based srcset. Optional responsive srcset with
configurable width variants and sizes attribute.
Fetch priority. Native fetchpriority attribute
support for Core Web Vitals optimization.
The extension works out of the box after installation. No
additional configuration is required. Images accessed through
the /processed/ path are automatically optimized by the
frontend middleware.
Tip
For best results, ensure that your server has the
Imagick or GD PHP extension installed. The backend
maintenance module can verify all prerequisites for you
(see System requirements check).
Configuration
The extension works out of the box with sensible defaults.
Images are automatically optimized when accessed via the
/processed/ path. All configuration happens through
ViewHelper attributes in your Fluid templates.
SourceSetViewHelper
The SourceSetViewHelper generates responsive <img> tags
with srcset attributes.
Image file resource (FAL file reference). Either file
or path must be provided.
path
path
Type
string
URI string path to the image, typically generated via
f:uri.image(). Use path instead of file when
passing a pre-resolved image URI. Either file or
path must be provided.
width
width
Type
integer
Target width in pixels.
height
height
Type
integer
Target height in pixels.
sizes
sizes
Type
string
Responsive sizes attribute for the generated
<img> tag.
Render mode. cover resizes images to fully cover
the given dimensions. fit resizes images to fit
within the given dimensions.
responsiveSrcset
responsiveSrcset
Type
boolean
Default
false
Enable width-based responsive srcset instead of
density-based 2x srcset.
widthVariants
widthVariants
Type
string|array
Default
480, 576, 640, 768, 992, 1200, 1800
Width variants for responsive srcset
(comma-separated string or array).
fetchpriority
fetchpriority
Type
string
Native HTML fetchpriority attribute. Allowed
values: high, low, auto. Omitted when
empty.
Encoding quality
The encoding quality is not configurable per ViewHelper call --
there is no quality argument. Generated URLs always carry the
default quality of 75 (for example
/processed/fileadmin/image.w1200h800m0q75.jpg), and variant
requests that carry no q value are processed with the same
default.
Note
As of this version the default variant quality is 75. Earlier
versions encoded WebP and AVIF variants at quality 100, so
those variants are now generated with stronger compression and
accordingly lower fidelity. The quality value is part of the
generated file name, so all previously generated variants
become stale and are recreated on demand under their new name.
Use Clear processed images to remove
the obsolete files.
Source set configuration
Define source sets per media breakpoint via the set
attribute:
Both modes support lazy loading via the native
loading="lazy" attribute. When using JS-based lazy
loading (class="lazyload"), the data-srcset
attribute is added automatically.
Backward compatibility
By default responsiveSrcset
is false, preserving the existing 2x density-based
srcset behavior. All existing templates continue to work
without modifications.
Trusted storage symlinks
New in version 1.2.0
The additionalTrustedStorageSymlinks extension configuration
setting.
The processor validates that both the source image and the
target variant resolve (via realpath()) to a location
inside an allowed root -- the public webroot, or a Local FAL
storage's own base path -- before reading or writing anything.
This rejects requests for images reached through a symlink that
escapes those roots (for example a symlink accidentally or
maliciously pointing at /etc).
Some deployments relocate TYPO3 core's own
_processed_ image cache -- a subdirectory inside a FAL
storage's own directory, e.g. fileadmin/_processed_ --
onto local/ephemeral storage, to keep frequently-rewritten
derivative images off shared/NFS storage. That leaves only a
symlink behind inside the storage, which the FAL-storage
basePath lookup above does not see (it only resolves the
storage's own base path, never looks inside it), so variant
requests for such images are rejected even though the original
file is legitimately part of the deployed application.
The additionalTrustedStorageSymlinks extension configuration
setting closes this gap on an explicit, per-instance, opt-in
basis: a comma-separated list of directory names that, when
found as a symlink directly inside a Local FAL storage's base
path, are resolved and added to the allow-list too.
The setting can also be edited via the backend:
Admin Tools > Settings > Extension Configuration >
nr_image_optimize.
Attention
This widens the set of filesystem locations the processor
will read from and publicly serve variants of. Only add
well-known, infrastructure-managed names here -- never a
name an untrusted party (e.g. an FTP-only content account)
could create on their own. The default is empty, which
keeps today's behavior unchanged for every installation that
does not configure it.
Additional trusted roots
New in version 1.3.0
The additionalTrustedRoots extension configuration setting.
TYPO3's var/ directory is now trusted automatically.
Some deployments need to serve variants of images that live under
an absolute filesystem path that is neither the public webroot,
a Local FAL storage's own base path, nor one of the hardcoded
TYPO3-internal locations (var/, symlinked
processed/uploads, or extension-published
_assets/<hash> directories) -- for example a custom mount
managed outside of FAL.
The additionalTrustedRoots extension configuration setting
closes this gap on an explicit, per-instance, opt-in basis: a
comma-separated list of absolute filesystem paths that are
realpath-resolved and added to the allow-list directly.
The setting can also be edited via the backend:
Admin Tools > Settings > Extension Configuration >
nr_image_optimize.
Attention
This widens the set of filesystem locations the processor
will read from and publicly serve variants of. Only add paths
you fully trust. Relative paths are rejected outright (never
resolved against the PHP process's working directory). The
default is empty, which keeps today's behavior unchanged for
every installation that does not configure it.
WebP/AVIF output quality
New in version 1.3.0
The qualityWebp and qualityAvif extension configuration
settings.
The primary variant's quality is controlled per-request via the
q<n> URL segment. The .webp and .avif sidecars previously
reused that same numeric quality, but AVIF's quality scale is steeper
than WebP's or JPEG's -- at matching numbers an AVIF file comes out
larger than the WebP sidecar, defeating the point of serving AVIF at
all.
Two extension configuration settings control sidecar quality
independently of the primary variant:
qualityWebp (default 75)
Output quality for the generated WebP variant.
qualityAvif (default 60)
Output quality for the generated AVIF variant. The lower default
keeps AVIF variants genuinely smaller than WebP while staying
visually comparable.
The settings can also be edited via the backend:
Admin Tools > Settings > Extension Configuration >
nr_image_optimize.
Attention
Per-format quality is not part of the processed-variant cache
filename. Changing either setting only affects newly generated
variants -- clear already-processed images (see
Clear processed images) to apply the new quality to existing
ones.
Usage
This chapter shows practical examples for integrating
responsive images into your Fluid templates.
Register the namespace
Add the ViewHelper namespace at the top of your Fluid
template or register it globally:
Public images only: absolute URLs are passed through
New in version 1.1.3
Absolute URLs, data: URIs, and URLs with a query string are
passed through unchanged and rendered as a plain <img> tag.
The /processed/ endpoint is designed for public files only.
It resolves the given path below the public web root and writes the
generated variants as static files into public/processed/,
where the web server delivers them directly — without any access
check.
Files in non-public FAL storages (is_public = 0) can therefore
not be processed. Extensions such as
fal_securedownload
resolve such files to tokenized eID URLs
(/index.php?eID=dumpFile&...) whose delivery runs through TYPO3
and performs a permission check on every request.
The ViewHelper detects absolute URLs (http://, https://,
//), data: URIs, and URLs containing a query string and
passes them through unchanged, rendering a plain <img> tag with
the URL as src:
No srcset/sizes attributes and no per-breakpoint
<source> elements are generated — the browser always
loads the image in its original dimensions.
No WebP/AVIF variants and no quality optimization are
applied.
In return, the access control of the generating extension
(e.g. fal_securedownload) stays fully intact, because the
URL — including its access token — is emitted unchanged.
If you need optimized variants of images in protected storages,
generate them with TYPO3's own image processing (for example
f:image or the ImageService). Processed files are then
created inside the protected storage's processing folder and are
delivered through the same secure-download mechanism, keeping the
permission check intact.
Fetch priority for Core Web Vitals
Use the fetchpriority attribute to hint the browser
about resource prioritization, improving Largest Contentful
Paint (LCP) scores:
Frontend middleware registered before
typo3/cms-frontend/site. Intercepts requests whose
path starts with /processed/ and delegates image
processing to the Processor class.
Processor
classProcessor
Fully qualified name
\Netresearch\NrImageOptimize\Processor
Core image processing engine. Parses the requested URL
to extract dimensions, quality, mode, and format
parameters. Uses the Intervention Image library for
actual image manipulation. Processed images are cached
on disk to avoid repeated processing.
Fluid ViewHelper that generates <img> tags with
srcset attributes for responsive image delivery.
Supports both density-based (2x) and width-based
responsive srcset modes.
Added: additionalTrustedRoots extension configuration --
per-instance, opt-in, comma-separated list of absolute
filesystem paths, outside FAL and TYPO3-internal locations,
that are realpath-resolved and added to the path-validation
allow-list. TYPO3's var/ directory is now trusted
automatically. Port of the fix on main (2.4.0). See
Additional trusted roots.
Added: qualityWebp (default 75) and qualityAvif
(default 60) extension configuration settings, so the
WebP and AVIF sidecar variants can be tuned independently of
the primary variant's quality. AVIF's steeper quality scale
previously meant AVIF variants came out larger than WebP at
the same numeric quality. Port of the fix on main (2.4.0).
See WebP/AVIF output quality.
Fixed: the Maintenance module's "clear processed images"
action failed whenever processed was a symlink to a
shared volume -- a common Deployer/CI deployment layout. It
validated the target with realpath()-equality, which
resolves the symlink and never matches, so clearing failed on
every symlinked deployment. The directory is now emptied in
place instead of recreated, so the symlink survives. Port of
the fix on main (2.4.0).
1.2.0
Added: additionalTrustedStorageSymlinks extension
configuration -- per-instance, opt-in, comma-separated list of
directory names that, when found as a symlink directly inside a
Local FAL storage's own base path (e.g.
fileadmin/_processed_), are resolved and added to the
path-validation allow-list. Closes the gap where deployments
relocate TYPO3 core's own _processed_ image cache onto
local/ephemeral storage to keep it off shared/NFS storage,
leaving a symlink behind that the FAL-storage basePath lookup
cannot see. Default empty; keeps today's behaviour for every
installation that doesn't opt in. See Trusted storage symlinks.
Fixed: images published via public/_assets/<hash>
symlinks (extension Resources/Public/ assets) were
rejected with HTTP 400. TYPO3 core publishes each extension's
Resources/Public/ directory by symlinking
public/_assets/<hash>/ to a location outside the public
webroot. getAllowedRoots() did not resolve these symlinks, so
variant requests for e.g. an extension's default/fallback image
failed even though the file is a legitimate part of the deployed
application. Every immediate child of _assets is now
resolved individually.
1.1.3
Fixed: the sourceSet ViewHelper passes absolute URLs
(http://, https://, //), data: URIs, and URLs
carrying a query string through unchanged and renders them as a
plain <img> tag. Previously such paths — e.g. the tokenized
eID=dumpFile URLs fal_securedownload
generates for files in non-public storages — were mangled into
broken /processed/... variant paths. The access control
of the generating extension stays intact; see
Public images only: absolute URLs are passed through for the trade-off. Port of the
fix on main (2.2.4).
1.1.2
Fixed: silent HTTP 400 responses now log their rejection reason
via error_log() (URL-pattern mismatch and
path-outside-allowed-roots branches).
Fixed: a transient StorageRepository failure during early
TYPO3 bootstrap no longer poisons the per-process allowed-roots
cache; the degraded fallback is kept only for the current
request.
Fixed: getAllowedRoots() is memoized per request, avoiding
redundant lookups and repeated log lines.
Fixed: a filesystem-root public path (/) no longer rejects
every valid path.
1.1.1
Fixed: processed image requests no longer return
HTTP 400 when fileadmin (or any other Local
FAL storage) is a symlink to an external location
such as an NFS/EFS mount. isPathWithinAllowedRoots
now accepts any realpath-resolved path that lies
within the TYPO3 public root or the realpath of any
configured Local storage's basePath. Symlinks
placed inside a storage that escape every allowed
root -- e.g. fileadmin/evil -> /etc
-- continue to be rejected. Backport of the fix on
main, reported in
issue #70.
Hardened: paths containing NUL bytes are rejected
outright, closing a minor realpath-bypass via the
not-yet-existing-path parent-walk branch.
Changed (BC for subclasses and manual instantiators):
Netresearch\\NrImageOptimize\\Processor gains a
new required StorageRepository constructor
parameter. Consumers that autowire the service (the
default in TYPO3 12+) are unaffected; any code that
extends the class or constructs it by hand must
forward the new dependency.
Changed (BC): dropped PHP 8.1 support. The TYPO3_12
maintenance branch now requires PHP 8.2 or newer
(TYPO3 v12 itself still supports PHP 8.1, but this
extension aligns with the netresearch/typo3-ci-workflows
tooling which requires PHP 8.2+).
1.1.0
New in version 1.1.0
Comprehensive quality review: security hardening, performance
improvements, backend maintenance module, responsive srcset,
and expanded test coverage.
Added backend maintenance module with directory statistics,
system requirements check, and clear processed images action.
Added responsive width-based srcset generation as
opt-in feature.
Added widthVariants parameter for custom breakpoints.
Added sizes parameter for responsive image sizing.
Added fetchpriority attribute for resource hints.
Added path traversal hardening and XSS prevention.
Added DoS prevention via dimension and quality clamping.