liip/cache-control-bundle
A Symfony bundle for setting HTTP Cache-Control and Expires headers on controller actions via annotations and configuration, making it easy to mark responses public, private, or uncached and work with reverse proxies.
This bundle is archived and deprecated (last release 2013-12-19) and was superseded by FOSHttpCacheBundle. The README is effectively a migration document. Before installing, decide which path applies:
friendsofsymfony/http-cache-bundle with the friendsofsymfony/http-cache library.Minimal legacy setup:
AppKernel:composer require liip/cache-control-bundle
// app/AppKernel.php
new Liip\CacheControlBundle\LiipCacheControlBundle(),
liip_cache_control. The README examples are from the Symfony 2.0 era, so treat DependencyInjection/Configuration.php as the authoritative schema. The core idea is rules that map request paths to Cache-Control directives, plus a reverse-proxy/Varnish section:# app/config/config.yml — verify key names against Configuration.php
liip_cache_control:
rules:
- path: ^/$
controls:
public: true
max_age: 300
curl -sI http://localhost/ | grep -i cache-control
First use case: make an anonymous, read-only page (homepage, product listing, article detail) cacheable at the shared-cache level, then confirm Cache-Control: public, max-age=... and s-maxage are present. The bundle's value is applying those headers centrally — by path or controller/action annotation — rather than sprinkling $response->setPublic() through every controller.
Where to look first:
README.md — deprecation notice and the migration mapping to FOSHttpCacheBundle.DependencyInjection/Configuration.php — real config keys and defaults.Annotation/ — controller-level cache annotations.Resources/config/services.xml — service IDs for the cache manager and Varnish client.For a migration target, the equivalent FOSHttpCacheBundle configuration is:
fos_http_cache:
cache_control:
rules:
- match:
path: ^/$
methods: [GET, HEAD]
headers:
cache_control:
public: true
max_age: 300
proxy_client:
varnish:
servers: [localhost:6081]
1. Config rules first, annotations second. Use path-based rules for broad policy (static sections, listings, health checks) and annotations only for action-specific overrides. Centralizing policy avoids the classic problem of one controller forgetting setPublic() and poisoning a shared cache with a private response.
2. Controller annotations for per-action intent. In legacy code you typically see annotations on the action:
use Liip\CacheControlBundle\Annotation\Cache;
/**
* @Cache(public=true, maxAge=60)
*/
public function showAction($id) { /* ... */ }
Verify the exact annotation class/property names in the bundle's Annotation/ directory — the naming differed from FOSHttpCacheBundle's @Cache, so do not copy modern examples directly.
3. Purge on write, not just expire. Configure the shortest sane TTL, then explicitly invalidate when content changes. The bundle exposes a Varnish client/cache-manager service; locate the service ID in services.xml and inject it into the service that writes content, or better, into a kernel.terminate / domain event listener so the purge happens after the database transaction commits:
public function onContentUpdated(ContentUpdatedEvent $event)
{
$this->cacheManager->invalidate($event->getUrl());
}
A purge-before-commit is a common source of stale-then-repopulated cache entries.
4. Use s-maxage, not only max-age. With a shared proxy in front, max-age controls browsers and s-maxage controls Varnish. Symfony's Response::setSharedMaxAge() sets s-maxage and marks the response public — prefer it over manually assembling headers.
5. ESI for fragment-level caching. Cache the page shell aggressively and use ESI/Surrogate-Control for personalized fragments (cart count, user greeting). This is the safe way to cache pages for authenticated users without leaking one user's data to another.
6. Test the headers, not the bundle. Add functional tests asserting Cache-Control on key routes and integration tests that assert purge calls against a mocked HTTP client. Because the package is unmaintained, your test suite is the only regression net you have.
7. Migration pattern. When moving to FOSHttpCacheBundle, migrate in this order: (a) move config rules, (b) replace annotations, (c) swap the Varnish client for the FOSHttpCache library client, (d) enable tags/ban invalidation, (e) remove the Liip bundle. Both bundles cannot coexist cleanly — they overlap in services and annotations.
Configuration.php, before trusting any blog post.curl -I; don't assume annotations win over config.Cache-Control: private, no-cache (or omit public caching entirely) for logged-in pages, and set Vary: Cookie / Vary: Authorization where appropriate. A single misconfigured rule can serve one user's account page to another.PURGE/BAN, or a purge sent before commit all look like "caching works but content is stale." Check varnishlog and assert purge responses in tests.public does not mean "cache forever." Combine public with a short s-maxage and explicit purges; long TTLs plus missed invalidations produce the worst debugging sessions.HttpCache with ESI enabled), the <esi:include> tags reach the browser and do nothing.curl -sI, X-Cache/Age headers, Varnish varnishlog -g request, Symfony profiler's response panel, and a container dump (debug:container) to confirm which cache service is actually wired.kernel.response listeners are the practical hooks. Avoid subclassing bundle internals — they were not designed as a stable API and will not receive fixes.composer.json, schedule replacement with FOSHttpCacheBundle; the README's migration guide is the shortest path and the only supported direction.How can I help you explore Laravel packages today?