Hero Image
Request–Response Flow in Drupal

This article explains how Drupal turns an incoming HTTP request into the HTML (or JSON) response you see in the browser. It covers:

  • A high-level beginner-friendly overview
  • A deeper walkthrough of the Symfony and Drupal core code involved
  • References to actual core files and methods
  • A diagram-friendly flow representation
  • Real-world developer use cases

This knowledge is essential for debugging, performance, and understanding how modules, controllers, themes, and APIs interact inside Drupal.


1. High-Level Flow (Beginner Friendly)

When a user visits /node/5, Drupal follows a predictable pipeline:

Browser → index.php → Kernel Boot → Routing → Controller → Render Array → Twig → HTML → Browser

Summary of steps

  1. Browser sends an HTTP request.
  2. index.php receives it.
  3. DrupalKernel boots the system.
  4. Routing determines which controller should handle the request.
  5. Controller returns a render array or response object.
  6. The render pipeline and Twig generate HTML.
  7. A Response object is sent back to the browser.

2. index.php - Drupal’s Front Controller

File: web/index.php

This is the single entry point for all Drupal requests.

Key code

$autoloader = require_once __DIR__ . '/../vendor/autoload.php';

$request = Request::createFromGlobals();

$kernel = new DrupalKernel('prod', $autoloader);

$response = $kernel->handle($request);
$response->send();

$kernel->terminate($request, $response);

What happens here

  • Composer autoloader loads classes.
  • Symfony Request object is created.
  • DrupalKernel initializes the Drupal environment.
  • The request moves into the Symfony HttpKernel pipeline.
  • The final Response is sent to the browser.

3. DrupalKernel - Booting Drupal

File: core/lib/Drupal/Core/DrupalKernel.php

The core entry method:

public function handle(Request $request, $type = self::MASTER_REQUEST, $catch = true) {
  $this->boot();
  return $this->container->get('http_kernel')->handle($request, $type, $catch);
}

What boot() does

protected function boot() {
  $this->initializeSettings();
  $this->initializeContainer();
}

During boot, Drupal loads:

  • Module service providers
  • Event subscribers
  • Routing providers
  • CMI configuration
  • Cache backend
  • Active theme information
  • Dependency injection container

This is where Drupal becomes fully operational.


4. Symfony HttpKernel - Routing and Pipeline

After booting, processing is handed to Symfony’s HttpKernel.

File: vendor/symfony/http-kernel/HttpKernel.php

Routing occurs through Drupal’s router service:

$route_info = $this->container->get('router')->matchRequest($request);

Route matching logic is implemented in:

  • core/lib/Drupal/Core/Routing/Router.php
  • core/lib/Drupal/Core/Routing/Matcher/DumperMatcher.php

Routing determines:

  • Which controller to call
  • Parameters extracted from the path
  • Required permissions and access checks

Failure results in:

  • 404 Not Found
  • 403 Access Denied

5. Controller Resolver - Executing Your Controller

File: vendor/symfony/http-kernel/Controller/ControllerResolver.php

Execution flow:

$controller = $this->resolver->resolve($request);
$arguments = $this->argumentResolver->getArguments($request, $controller);

$response = call_user_func_array($controller, $arguments);

Example controller:

public function content(NodeInterface $node) {
  return [
    '#theme' => 'node_display',
    '#node' => $node,
  ];
}

Controllers return structured data:

  • Render arrays
  • JSON responses
  • Redirect responses
  • Binary/file responses

They do not output HTML directly.


6. Render Arrays → Render Pipeline → Twig

If a controller returns a render array, Drupal turns it into HTML using the render system.

Renderer: core/lib/Drupal/Core/Render/Renderer.php

public function render($elements) {
  return $this->doRender($elements);
}

Theme Manager: core/lib/Drupal/Core/Theme/ThemeManager.php

$theme_hook = $this->themeRegistry->get($hook_name);

Twig template execution occurs within:
vendor/twig/twig/src/Template.php

Render processing includes:

  • Preprocess functions
  • Theme hook resolution
  • Library attachments
  • Lazy builders
  • Cache metadata (tags, contexts, max-age)

Render caching explains why UI changes sometimes appear delayed.


7. The Response Object

After rendering, Drupal wraps the output in a Symfony Response.

return $response;

Sending the response:

$response->send();

Browser receives:

  • HTML or JSON body
  • Status code
  • Headers
  • Cookies

8. Full Execution Flow

web/index.php
  → DrupalKernel::handle()
    → boot()
      → load settings
      → load modules
      → build container
      → build router
    → HttpKernel::handle()
      → matchRequest()
      → resolve controller
      → execute controller
      → render array → Renderer → ThemeManager → Twig
    → Response::send()

This represents the full pipeline every request follows.


Why This Architecture Matters

A clear understanding of the request lifecycle helps developers:

  • Debug routing and access issues
  • Fix caching or stale render data problems
  • Write efficient custom controllers
  • Build middleware and event subscribers
  • Diagnose multisite or config split issues
  • Implement reliable API integrations such as Salesforce or Elasticsearch

Quick Summary

  • Requests enter through index.php.
  • DrupalKernel boots the system.
  • Symfony handles routing and controller execution.
  • Controllers return render arrays or response objects.
  • Render arrays pass through Twig and theming.
  • A final Symfony Response is returned to the browser.

Real-World Use Cases

  • Creating custom route/controller pages
  • Returning JSON for React or Vue frontends
  • Adding middleware for security or logging
  • Debugging missing or stale blocks
  • Tracing Webform to API submission flows
  • Working with cache invalidation, Redis, and dynamic page cache
  • Troubleshooting performance bottlenecks in kernel or routing