HTTP Caching and Reverse Proxy
HTTP Caching and Reverse Proxy
~15 Min. Lesezeit Zuletzt aktualisiert am August 8, 2026
Chapter 45 cached a COMPUTED value WITHIN PHP – now let's go a level HIGHER: caching the ENTIRE HTTP response, so some requests never even reach PHP.
The Cache-Control header
EVERY HTTP response CAN carry a Cache-Control header, telling browsers AND intermediate proxies WHETHER and HOW LONG the response may be cached:
use Symfony\Component\HttpFoundation\Response;
#[Route('/projects/{id}', name: 'project_show', requirements: ['id' => '\d+'])]
public function show(int $id, ProjectRepository $projectRepository): Response
{
$project = $projectRepository->find($id);
$response = $this->render('project/show.html.twig', ['project' => $project]);
$response->setPublic();
$response->setMaxAge(60);
return $response;
}setPublic() allows caching NOT ONLY in the browser, but also in SHARED caches (reverse proxies, CDNs) – the default is private (ONLY the individual browser may cache). setMaxAge(60) allows caching for 60 seconds.
Achtung: NEVER set setPublic() for pages with USER-SPECIFIC content (e.g. "Logged in as ..." from chapter 27) – a SHARED cache would otherwise serve ONE user's page to ALL other users. For our task manager, only genuinely PUBLIC content that's IDENTICAL for EVERYONE is a fit for setPublic().
Symfony's built-in HTTP cache
For local development/small deployments, Symfony ships a BUILT-IN reverse proxy in PHP – NO separate infrastructure component needed:
// Excerpt, ONLY relevant for chapter 46 - normally unchanged
use Symfony\Component\HttpKernel\HttpCache\HttpCache;
$kernel = new Kernel($_SERVER['APP_ENV'], (bool) $_SERVER['APP_DEBUG']);
if ('prod' === $_SERVER['APP_ENV']) {
$kernel = new HttpCache($kernel);
}
$response = $kernel->handle(Request::createFromGlobals());
$response->send();
$kernel->terminate($request, $response);HttpCache WRAPS the actual kernel – requests for which a VALID cached response exists get answered right HERE, WITHOUT the rest of the Symfony code (routing, controller, Doctrine) even running.
For real production: Varnish or Nginx
Symfony's PHP-based HTTP cache is handy for getting started, but REAL high-traffic production systems typically rely on a DEDICATED reverse proxy like Varnish or Nginx – placed IN FRONT of PHP, it answers cached requests WITHOUT even a PHP process needing to start (considerably faster than even the fastest PHP code).
ESI: caching partially dynamic pages
What if ONLY PART of a page should be cached (e.g. the static project description), but ANOTHER part must ALWAYS stay user-specific (e.g. "Logged in as ...")? Edge Side Includes (ESI) solve this:
{% render_esi controller('App\\Controller\\NavigationController::userBox') %}The EMBEDDED part gets treated as its OWN sub-request, with its OWN Cache-Control header – the rest of the page can be publicly cached, while EXACTLY this one building block NEVER gets cached. An advanced tool, whose full setup (an ESI-capable reverse proxy) exceeds this course's scope.
Tipp: Rule of thumb: HTTP caching pays off for pages that look IDENTICAL to MANY visitors (public marketing pages, unchanging content) – for our task manager, with content that's consistently user-specific and protected by security (block 5), application-level caching (chapter 45) plays the bigger role.