Repository navigation
Conversation
The cachebreaker appended time() to every image preview URL, so the URL changed on each page load and the browser downloaded every preview again. preview() now passes the file modification time, which keeps the URL stable until the file changes. fileIcon() accepts an int version; true still falls back to time() for Twig fileIcon() callers. The file route now answers revalidation requests (If-Modified-Since) with 304 instead of resending the file. Last-Modified was already set by BinaryFileResponse.
czachor
force-pushed
the
perf/image-cache
branch
from
October 20, 2026 12:14
5f9a16c to
352ad2f
Compare
This was referenced Oct 20, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Refs #75
Context
Opening the manager on a directory with many images is slow mainly because of the browser: with
cachebreakerenabled (the default), every image preview is downloaded again on every page load. This PR lets the browser cache previews and revalidate them cheaply. Server-side listing work is handled in #166.Why a separate PR
#166 reduces the work the server does to build the listing and does not change anything the browser receives. This PR changes HTTP behavior: preview URLs and responses of the file route. The two are independent: they touch different code paths, each can be reviewed, merged or reverted on its own, and they can be merged in any order.
Changes
1. Cachebreaker uses the file modification time
Cause:
FileTypeService::fileIcon()appended?time=<time()>to every image preview URL. The URL was different on every page load, so the browser never reused a cached preview and downloaded every image again.Change:
preview()passes the file modification time as the cachebreaker. The URL stays the same until the file changes, so the browser can reuse its cached copy; a replaced file still gets a new URL, which is what the cachebreaker is for.fileIcon()now acceptsbool|int|null: an int is used as the version,truestill falls back totime(), so the TwigfileIcon()function behaves as before.2. File route answers revalidation with 304
Cause:
binaryFileResponseAction(thefile_manager_fileroute, used for previews when the directory is not public) always sent the whole file, even when the browser already had it and asked withIf-Modified-Since.Change:
$response->isNotModified($request)turns the response into304 Not Modifiedwhen the file has not changed.BinaryFileResponsealready setsLast-Modified, so no new header is computed. ETag is not added on purpose:setAutoEtag()hashes the whole file on every request.Backward compatibility
No configuration or template changes.
fileIcon()keeps its signature for existing callers (true/false/null). The cache visibility of the file route is unchanged (Symfony default).Tests
vendor/bin/simple-phpunit: 11 tests, 30 assertions, OK (Symfony 7.4).CachebreakerTestwith its own environment (config_cachebreaker.yml,cachebreaker: true) and fixture directoryTests/Fixtures/web/images, so existing tests and fixtures are untouched:time=<file mtime>(the fixture is touched to a fixed past date, so atime()value cannot match);If-Modified-Sincereturns 304.Both new tests fail without the code changes. The branch merges cleanly with #166 (19 tests OK after the merge).