Choosing the right request and response events¶
When building ECA models that react to incoming HTTP requests — for example to perform redirects, enforce access rules, or modify response headers — it is essential to pick the correct kernel event. The Symfony HTTP kernel dispatches several events during the lifecycle of every request, and each one fires at a different stage. Using the wrong event is the most common source of problems such as redirects working only once, status messages disappearing after a redirect, or models that never fire on cached pages.
This guide walks through the available kernel events in the order they are dispatched, explains when to use each one, and highlights the caching pitfalls that trip up many users.
The HTTP request lifecycle¶
Drupal uses Symfony's HTTP kernel, which dispatches events in the following order for a normal, successful request:
- Start dispatching request (
kernel.request) - Controller found to handle request (
kernel.controller) - Controller arguments have been resolved (
kernel.controller_arguments) - Controller does not return a Response instance (
kernel.view) — only when the controller returns a render array rather than a full Response object - Response created (
kernel.response) - Response for request created (
kernel.finish_request) - Response was sent (
kernel.terminate)
Two additional events fire only on the error path:
- Uncaught exception (
kernel.exception) - Exception status code — fires on routing exceptions that produce 4xx or 5xx status codes (available since ECA 2.1.0)
Choosing the right event¶
The table below summarises typical use cases and the recommended event for each.
| Use case | Recommended event | Why |
|---|---|---|
| Redirect a page request | Response created | Works reliably with all caches enabled, for both anonymous and authenticated users. |
| Display a message and redirect | Response created | Messages stored in the session survive the redirect when this event is used. |
| Check the current route and act on it | Controller found to handle request | Fires even when pages are served from the dynamic page cache. |
| Set or modify response headers | Response created | The response object is available at this stage. |
| Redirect 403 pages to login | Response created | Inspect the response status code and redirect accordingly. See the library model Redirect 403 to Login Page. |
| Redirect unpublished content | Start dispatching request | Intercept the request before the controller runs. See the library model Redirect unpublished. |
| Perform expensive background work | Response was sent | Runs after the response has been delivered to the client, so it does not delay the page load. |
| Custom error handling | Uncaught exception | Allows you to log, redirect, or display a custom error page. |
Caching and kernel events¶
Drupal ships with two page-level cache layers that can prevent kernel events from firing at all:
Page Cache- The Internal Page Cache module (
page_cache) caches full responses for anonymous users. When a cached response exists, the kernel short-circuits the entire request and none of the kernel events fire — not evenkernel.request. Dynamic Page Cache- The Internal Dynamic Page Cache module (
dynamic_page_cache) caches rendered responses but still dispatches early kernel events. However, once a response is cached, later events in the chain may receive a cached response rather than a freshly built one.
Which events fire on cached pages?¶
| Event | Fires with Page Cache (anonymous)? | Fires with Dynamic Page Cache? |
|---|---|---|
kernel.request |
No | Yes |
kernel.controller |
No | Yes |
kernel.controller_arguments |
No | Yes |
kernel.view |
No | Sometimes (only if the controller runs) |
kernel.response |
No | Yes |
kernel.finish_request |
No | Yes |
kernel.terminate |
No | Yes |
Page Cache bypasses everything
When the Internal Page Cache module serves a cached page for an anonymous user, no ECA model will fire. If your model must react to every anonymous request, you have two options:
- Use the Page Cache Kill Switch action in an earlier model to disable page caching for specific routes.
- Uninstall the Internal Page Cache module (not recommended for production sites with high traffic).
The Dynamic Page Cache pitfall¶
Even with the Dynamic Page Cache enabled, not all events behave the same way. A common pitfall:
-
kernel.requestfires before the controller is resolved. This makes it tempting for redirects, but the core redirect action (system.action.redirect) sets the redirect on the response event internally. When a cached response is served, the redirect set duringkernel.requestmay not take effect because the cached response overwrites it. -
kernel.controllerfires after the controller is found, even when the Dynamic Page Cache eventually serves a cached response. This makes it a reliable choice for inspecting the current route. -
kernel.responsefires every time a response is being prepared, whether from cache or freshly built. This makes it the most reliable event for redirects and response modifications.
Rule of thumb
For redirects, prefer the Response created event (kernel.response).
For route-based logic that does not modify the response, the Controller
found event (kernel.controller) is a safe choice.
Common patterns¶
Redirect based on a route¶
- Event: Response created
- Condition: Route match — compare to the route name, e.g.
entity.taxonomy_term.canonical - Action: Redirect to URL
This pattern works reliably with all caches enabled, for both anonymous and authenticated users.
Redirect and display a status message¶
- Event: Response created
- Condition: Route match
- Action: Display a message (e.g. using the Drupal messenger)
- Action: Redirect to URL
Note
Status messages are stored in the user's session. For anonymous users without a session, messages set before a redirect will be lost. This is standard Drupal behaviour and not specific to ECA.
Inspect or modify the response¶
- Event: Response created
- Action: Set headers — add or modify response headers such as
Cache-Control,X-Robots-Tag, or custom headers.
React to a specific route without modifying the response¶
- Event: Controller found to handle request
- Condition: Route match
- Action: any action that does not depend on the response object (e.g. logging, setting tokens, triggering custom events)
See the library model Route test for a working example.
Why "View Content Entity" is not the right event for redirects¶
The View Content Entity event might seem like the natural choice when you want to react to a user visiting a node or taxonomy term page. However, this event fires whenever the entity is rendered, which can happen multiple times per request (e.g. in views, blocks, or embedded references) and is not limited to the canonical page of that entity.
Additionally, because this event fires during the rendering phase, any redirect action triggered by it is processed too late — the response may already be cached by the Dynamic Page Cache, causing the redirect to work only on the first uncached request.
Use the Response created or Controller found events instead, combined with a Route match condition to target the correct page.
Related resources¶
- Response created — event reference
- Controller found to handle request — event reference
- Start dispatching request — event reference
- Route match — condition reference
- Page Cache Kill Switch — action reference
- Redirect 403 to Login Page — library example
- Redirect unpublished — library example
- Route test — library example