Symfony event subscribers let application code react when the framework dispatches request, response, exception, console, or custom events. They fit behavior that should run around controllers and services, such as adding response headers, checking request state, or centralizing exception handling.
A subscriber implements EventSubscriberInterface and declares its event map in getSubscribedEvents(). In a default Symfony Flex project, classes under src/ are loaded as services and autoconfiguration tags subscriber classes for the event dispatcher without a manual services.yaml entry.
Start from a project root that contains bin/console and at least one route that can be requested locally. The subscriber listens to kernel.response, adds an X-Catalog-Subscriber header, and can be proven through both debug:event-dispatcher and an HTTP response.
Related: How to create a Symfony project
Related: How to debug the Symfony service container
Related: How to create a Symfony controller
$ mkdir -p src/EventSubscriber
$ vi src/EventSubscriber/CatalogResponseSubscriber.php
<?php namespace App\EventSubscriber; use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\HttpKernel\Event\ResponseEvent; use Symfony\Component\HttpKernel\KernelEvents; final class CatalogResponseSubscriber implements EventSubscriberInterface { public function onKernelResponse(ResponseEvent $event): void { if (!$event->isMainRequest()) { return; } $event->getResponse()->headers->set('X-Catalog-Subscriber', 'active'); } public static function getSubscribedEvents(): array { return [ KernelEvents::RESPONSE => 'onKernelResponse', ]; } }
KernelEvents::RESPONSE maps to the kernel.response event. Use KernelEvents::REQUEST, KernelEvents::EXCEPTION, or a custom event class when the subscriber belongs to a different dispatch point.
$ php bin/console cache:clear // Clearing the cache for the dev environment with debug true [OK] Cache for the "dev" environment (debug=true) was successfully cleared.
Default development projects usually rebuild automatically, but clearing the cache removes stale compiled container metadata before inspecting the dispatcher.
Related: How to clear Symfony cache
$ php bin/console debug:event-dispatcher kernel.response Registered Listeners for "kernel.response" Event ================================================ ------- -------------------------------------------------------------------------------------------- ---------- Order Callable Priority ------- -------------------------------------------------------------------------------------------- ---------- ##### snipped ##### #5 App\EventSubscriber\CatalogResponseSubscriber::onKernelResponse() 0 ##### snipped ##### ------- -------------------------------------------------------------------------------------------- ----------
To run before lower-priority response listeners, return KernelEvents::RESPONSE ⇒ ['onKernelResponse', 20] from getSubscribedEvents().
$ symfony server:start --no-tls --port=8000 -d
[OK] Web server listening
http://127.0.0.1:8000
Use another free port when 8000 already belongs to a local project.
Related: How to run a Symfony project locally
$ curl -I -sS http://127.0.0.1:8000/catalog HTTP/1.1 200 OK Cache-Control: no-cache, private Content-Type: text/html; charset=UTF-8 X-Catalog-Subscriber: active X-Robots-Tag: noindex
Replace /catalog with a route your application already serves. The X-Catalog-Subscriber header confirms that onKernelResponse() changed the response.
$ symfony server:stop [OK] Stopped 2 process(es) successfully