Magento 2 Experten — Hyvä Theme, Tailwind CSS & SEO aus einer Hand ›

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:

public/index.php
// 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.