Symfony HttpCache reverse proxy and distributed cache consistency
0 reputation · 05 Mar 2025, 12:23 UTC
0 reputation · 05 Mar 2025, 12:23 UTC
The Symfony HttpCache class provides a built-in reverse proxy implementation that intercepts requests to serve cached responses based on HTTP headers. While the filesystem-based store is standard for single-server setups, deploying this architecture across multiple application nodes introduces challenges regarding state synchronization.
When using the default HttpCache implementation, cache invalidation requests—such as PURGE or BAN methods—are processed by the specific node receiving the request. This creates a scenario where one node may serve updated content while others continue to serve stale responses from their local storage.
Given the current design of the HttpCache class and the Symfony Kernel lifecycle, what are the architectural options for ensuring consistent cache invalidation across a distributed cluster? Is there a documented mechanism to broadcast invalidation events to all active reverse proxy instances?
34775 reputation · 05 Mar 2025, 19:10 UTC
Symfony's HttpCache has no built-in mechanism to broadcast invalidation events across nodes. A PURGE or BAN request is handled only by the node that receives it, because the default FileCacheStore writes to that node's local filesystem. To get cluster-wide consistency you must choose one of four architectural patterns:
FileCacheStore with a network store (Redis or Memcached) that every node reads and writes. A purge on any node deletes the key for all nodes.purge() on its local cache instance.StoreInterface backed by Redis Pub/Sub or similar, so one purge message triggers eviction on every subscriber.BAN requests to it; the proxy's own clustering handles distribution. Symfony only generates the ban, it does not distribute it.Confirmed: HttpCache delegates storage to a StoreInterface implementation, and the bundled store is filesystem-based and node-local. There is no documented broadcast API in the component itself.
Likely, but verify against your version: the exact service wiring for swapping the store differs between the classic HttpCache kernel wrapper and FrameworkBundle's framework.http_cache integration. On recent Symfony versions (5.4/6.x), enabling http_cache: true uses the built-in store; to override it you decorate or replace the store service. Check your installed version's FrameworkExtension wiring before copying config from a blog post.
# config/services.yaml
services:
app.http_cache.store:
class: Symfony\Component\HttpKernel\HttpCache\Store
# or a custom StoreInterface implementation wrapping Redis
Symfony\Component\HttpKernel\HttpCache\HttpCache:
arguments:
$store: '@app.http_cache.store'
Note: the stock Store class is filesystem-only, so a Redis-backed setup requires a custom StoreInterface implementation — this is the part most tutorials gloss over.
PURGE on one node, then request the same URL from a second node and confirm the response is a miss (check the X-Symfony-Cache debug header in dev, or your store's key listing).redis-cli --scan --pattern '*http_cache*' | wc -l before and after the purge should drop identically regardless of which node received the request.If you keep local stores for latency reasons, dispatch an event and consume it on every node:
$bus->dispatch(new PurgeUrlMessage($url));
// handler on each node
public function __invoke(PurgeUrlMessage $msg): void
{
$this->httpCache->invalidate(Request::create($msg->url));
}
Make the handler idempotent — message ordering is not guaranteed, and a purge arriving before a late write can re-stale the entry.
FileCacheStore over NFS causes file-locking contention under load — avoid it.Which store is currently configured for your HttpCache? If you are already on Redis, you only need to confirm the key prefix matches across nodes; if you are on the filesystem default, the decision is between swapping the store and adding Messenger-based propagation.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.