Skip to content

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:

  1. Start dispatching request (kernel.request)
  2. Controller found to handle request (kernel.controller)
  3. Controller arguments have been resolved (kernel.controller_arguments)
  4. Controller does not return a Response instance (kernel.view) — only when the controller returns a render array rather than a full Response object
  5. Response created (kernel.response)
  6. Response for request created (kernel.finish_request)
  7. Response was sent (kernel.terminate)

Two additional events fire only on the error path:

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 even kernel.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:

  1. Use the Page Cache Kill Switch action in an earlier model to disable page caching for specific routes.
  2. 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.request fires 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 during kernel.request may not take effect because the cached response overwrites it.

  • kernel.controller fires 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.response fires 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

  1. Event: Response created
  2. Condition: Route match — compare to the route name, e.g. entity.taxonomy_term.canonical
  3. Action: Redirect to URL

This pattern works reliably with all caches enabled, for both anonymous and authenticated users.

Redirect and display a status message

  1. Event: Response created
  2. Condition: Route match
  3. Action: Display a message (e.g. using the Drupal messenger)
  4. 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

  1. Event: Response created
  2. 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

  1. Event: Controller found to handle request
  2. Condition: Route match
  3. 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.