---
title: "Middlewares (request handling)"
manual: "TYPO3 Explained"
version: "main"
permalink: "https://docs.typo3.org/permalink/t3coreapi:request-handling@main"
source: "ApiOverview/RequestLifeCycle/Middlewares.rst"
modified: "2026-09-14T11:10:56+00:00"
---

# Middlewares (request handling)

TYPO3 has implemented [PSR-15](https://www.php-fig.org/psr/psr-15/) for handling incoming HTTP requests. The
implementation within TYPO3 is often called "Middlewares", as PSR-15 consists of
two interfaces where one is called `Middleware`.

-   [Basic concept](https://docs.typo3.org/permalink/t3coreapi:basic-concept@main)
-   [TYPO3 implementation](https://docs.typo3.org/permalink/t3coreapi:typo3-implementation@main)
-   [Middlewares](https://docs.typo3.org/permalink/t3coreapi:middlewares@main)
-   [Middleware examples](https://docs.typo3.org/permalink/t3coreapi:middleware-examples@main)
-   [Configuring middlewares](https://docs.typo3.org/permalink/t3coreapi:configuring-middlewares@main)
-   [Override ordering of middlewares](https://docs.typo3.org/permalink/t3coreapi:override-ordering-of-middlewares@main)
-   [Creating new request / response objects](https://docs.typo3.org/permalink/t3coreapi:creating-new-request-response-objects@main)
-   [Executing HTTP requests in middlewares](https://docs.typo3.org/permalink/t3coreapi:executing-http-requests-in-middlewares@main)
-   [Debugging](https://docs.typo3.org/permalink/t3coreapi:debugging@main)

## Basic concept

The most important information is available at
[https://www.php-fig.org/psr/psr-15/](https://www.php-fig.org/psr/psr-15/) and [https://www.php-fig.org/psr/psr-15/meta/](https://www.php-fig.org/psr/psr-15/meta/)
where the standard itself is explained.

The idea is to use [PSR-7](https://www.php-fig.org/psr/psr-7/) `Request` and `Response` as a base, and
wrap the execution with middlewares which implement [PSR-15](https://www.php-fig.org/psr/psr-15/). PSR-15 will receive
the incoming request and return the created response. Within PSR-15 multiple
request handlers and middlewares can be executed. Each of them can adjust the
request and response.

## TYPO3 implementation

TYPO3 has implemented the PSR-15 approach in the following way:

```plantuml
skinparam dpi 150
hide empty members

skinparam sequence {
    ArrowColor  #e18d00
    DividerBackgroundColor  #f7a831
    GroupBackgroundColor    #f7a831
    LifeLineBackgroundColor white
    LifeLineBorderColor #e18d00
    ParticipantBackgroundColor  #f7a831
    ParticipantBorderColor  #e18d00
    BoxLineColor    #e18d00
    BoxBackgroundColor  #DDDDDD
}

participant Application
participant ServerRequestFactory
participant MiddlewareStackResolver

participant "MiddlewareDispatcher \n(RequestHandlerInterface)" as MiddlewareDispatcher

box "Middleware A"
participant "Anonymous\nRequestHandler" as RequestHandlerA << Generated >>
participant "MiddlewareA" as Middleware
end box

box "Middleware B"
participant "Anonymous\nRequestHandler" as RequestHandlerB << Generated >>
participant "MiddlewareB" as MiddlewareB
end box

participant "(Frontend|Backend)\nRequestHandler" as RequestHandler

note over Middleware
Every Middleware
 is wrapped in
  an anonymous
 RequestHandler
end note

note over RequestHandler
Always the last
RequestHandler
in the stack
end note

autonumber 1
Application -> ServerRequestFactory: fromGlobals()
autonumber 1
Application <-- ServerRequestFactory: Request

autonumber 2
Application -> MiddlewareStackResolver: resolve()
autonumber 3
Application <-- MiddlewareStackResolver: Stack

autonumber 4
Application -> MiddlewareDispatcher: handle(Request)
autonumber 4
MiddlewareDispatcher -> RequestHandlerA: handle(Request)

autonumber 5
RequestHandlerA -> Middleware: process(Request, \nnext RequestHandler)
autonumber 5
Middleware -> RequestHandlerB : handle(Request)
autonumber 5
RequestHandlerB -> MiddlewareB: process(Request, \nnext RequestHandler)

autonumber 6
MiddlewareB -> RequestHandler : handle(Request)
autonumber 6
RequestHandler --> MiddlewareB: Response

autonumber 7
MiddlewareB --> RequestHandlerB: Response

autonumber 7
RequestHandlerB --> Middleware: Response

autonumber 7
Middleware --> RequestHandlerA: Response

autonumber 8
RequestHandlerA --> MiddlewareDispatcher: Response
autonumber 8
MiddlewareDispatcher --> Application: Response
```

Figure 1-1: Application flow

1.  TYPO3 will create a [TYPO3 request object](https://docs.typo3.org/permalink/t3coreapi:typo3-request@main).
1.  TYPO3 will collect and sort all configured PSR-15 middlewares.
1.  TYPO3 will convert all middlewares to PSR-15 request handlers.
1.  TYPO3 will call the first middleware with request and the next middleware.
1.  Each middleware can modify the request if needed, see [Middlewares](https://docs.typo3.org/permalink/t3coreapi:request-handling-middlewares@main).
1.  Final Request is passed to the last RequestHandler (`TYPO3CMSFrontendHttpRequestHandler`
    or `TYPO3CMSBackendHttpRequestHandler`) which generates PSR-7 response and passes
    it back to the last middleware.
1.  Each middleware gets back a PSR-7 response from middleware later in the stack and passes it up the stack to the previous middleware.
    Each middleware can modify the response before passing it back.
1.  This response is passed back to the execution flow.

> [!NOTE]
> **Changed in version 14.0**
>
> Middlewares must not rely on the PHP superglobals `$_GET` / `$_POST`
> (and legacy `$HTTP_GET_VARS` / `$HTTP_POST_VARS`) being synchronized
> with the PSR-7 request. TYPO3 no longer resets these globals from request data.
>
> Also note that `$GLOBALS['TYPO3_REQUEST']` is a compatibility layer and
> may not be available in all middleware positions (it is set later in the
> application flow). Always use the PSR-7 request
> (`\Psr\Http\Message\ServerRequestInterface`) passed into
> `process()` and pass it explicitly to services that need it.

## Middlewares

Each middleware has to implement the PSR-15
`\Psr\Http\Server\MiddlewareInterface`:

-   **interface MiddlewareInterface**

    -   *Fully qualified name:* `\Psr\Http\Server\MiddlewareInterface`

    Participant in processing a server request and response.

    An HTTP middleware component participates in processing an HTTP message:
    by acting on the request, generating the response, or forwarding the
    request to a subsequent middleware and possibly acting on its response.

    -   **process(\\Psr\\Http\\Message\\ServerRequestInterface $request, \\Psr\\Http\\Server\\RequestHandlerInterface $handler)**

        Process an incoming server request.

        Processes an incoming server request in order to produce a response.
        If unable to produce the response itself, it may delegate to the provided
        request handler to do so.

        -   *param $request:* the request
        -   *param $handler:* the handler

        *Returns:* `PsrHttpMessageResponseInterface`

By doing so, the middleware can do one or multiple of the following:

-   Adjust the incoming request, e.g. add further information.
-   Create and return a PSR-7 response.
-   Call next request handler (which again can be a middleware).
-   Adjust response received from the next request handler.

### Using Extbase

One note about using [Extbase](https://docs.typo3.org/permalink/t3coreapi:extbase-extension-framework@main) in middlewares: do not! Extbase
relies on [frontend TypoScript](https://docs.typo3.org/m/typo3/reference-typoscript/main/en-us/Index.html#start) being present; otherwise
the configuration is not applied. This is usually no problem - Extbase plugins
are typically either included as [USER content object](https://docs.typo3.org/m/typo3/reference-typoscript/main/en-us/ContentObjects/UserAndUserInt/Index.html#cobj-user)
(its content is cached and returned together with other content elements in
fully-cached page context), or the Extbase plugin is registered as USER_INT. In
this case, the TYPO3 Core takes care of calculating TypoScript before
the plugin is rendered, while other USER content objects are fetched from page
cache.

With TYPO3 v11, the "calling Extbase in a context where TypoScript has not been
calculated" scenario did not fail, but simply returned an empty array for
TypoScript, crippling the configuration of the plugin in question. This
mitigation hack has been removed in TYPO3 v13, though. Extension developers
that already use Extbase in a middleware have the following options:

-   Consider not using Extbase for the use case: Extbase is quite expensive.
    Executing it from within middlewares can increase the parse time in
    fully-cached page context significantly and should be avoided especially for
    "simple" things. In many cases, directly manipulating the response object
    and skipping the Extbase overhead in a middleware should be enough.
-   Move away from the middleware and register the Extbase instance as a casual
    [USER_INT](https://docs.typo3.org/m/typo3/reference-typoscript/main/en-us/ContentObjects/UserAndUserInt/Index.html#cobj-user-int) object via TypoScript: Extbase is
    designed to be executed like this, the bootstrap will take care of
    properly calculating TypoScript, and Extbase will run as expected.

    Note that with TYPO3 v12, the overhead of USER_INT content objects has been
    reduced significantly, since TypoScript can be fetched from improved cache
    layers more quickly. This is also more resilient towards core changes since
    extension developers do not need to go through the fiddly process of
    bootstrapping Extbase on their own.

## Middleware examples

The following list shows typical use cases for middlewares.

-   [A middleware to return a JSON response with localized
    strings](https://docs.typo3.org/permalink/t3coreapi:example-localization-middleware@main).

### Returning a custom response

This middleware checks whether `foo/bar` was called and will return
an unavailable response in that case. Otherwise the next middleware will be
called, and its response is returned instead.

**EXT:my_extension/Classes/Middleware/NotAvailableMiddleware.php**

```php
<?php

declare(strict_types=1);

namespace MyVendor\MyExtension\Middleware;

use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Frontend\Controller\ErrorController;

class NotAvailableMiddleware implements MiddlewareInterface
{
  public function process(
    ServerRequestInterface $request,
    RequestHandlerInterface $handler,
  ): ResponseInterface {
    if ($request->getRequestTarget() === 'foo/bar') {
      return GeneralUtility::makeInstance(ErrorController::class)
          ->unavailableAction(
            $request,
            'This page is temporarily unavailable.',
          );
    }

    return $handler->handle($request);
  }
}

```

### Enriching the request

The current request can be extended with further information, e.g. the current
resolved site and language could be attached to the request.

In order to do so, a new request is built with additional attributes, before
calling the next request handler with the enhanced request.

**EXT:my_extension/Classes/Middleware/RequestEnrichingMiddleware.php**

```php
<?php

declare(strict_types=1);

namespace MyVendor\MyExtension\Middleware;

use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;
use TYPO3\CMS\Core\Routing\SiteMatcher;

class RequestEnrichingMiddleware implements MiddlewareInterface
{
  public function __construct(
    private readonly SiteMatcher $matcher,
  ) {}

  public function process(
    ServerRequestInterface $request,
    RequestHandlerInterface $handler,
  ): ResponseInterface {
    $routeResult = $this->matcher->matchRequest($request);

    $request = $request->withAttribute('site', $routeResult->getSite());
    $request = $request->withAttribute('language', $routeResult->getLanguage());

    return $handler->handle($request);
  }
}

```

### Enriching the response

This middleware will check the length of generated output, and add a header
with this information to the response.

In order to do so, the next request handler is called. It will return the generated
response, which can be enriched before it gets returned.

If you want to modify the response coming from certain middleware,
your middleware has to be configured to be `before` it.
Order of processing middlewares when enriching response is opposite
to when middlewares are modifying the request.

**EXT:my_extension/Classes/Middleware/ResponseEnrichingMiddleware.php**

```php
<?php

declare(strict_types=1);

namespace MyVendor\MyExtension\Middleware;

use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;

class RequestEnrichingMiddleware implements MiddlewareInterface
{
  public function process(
    ServerRequestInterface $request,
    RequestHandlerInterface $handler,
  ): ResponseInterface {
    $response = $handler->handle($request);

    if ($request->getRequestTarget() === 'foo/bar') {
      $response = $response->withHeader(
        'Content-Length',
        (string)$response->getBody()->getSize(),
      );
    }

    return $response;
  }
}

```

## Configuring middlewares

In order to implement a custom middleware, this middleware has to be configured.
TYPO3 already provides some middlewares out of the box. Beside adding your own
middlewares, it's also possible to remove existing middlewares from
the configuration.

The configuration is provided within
[`Configuration/RequestMiddlewares.php`](../../ExtensionArchitecture/FileStructure/Configuration/RequestMiddlewaresPhp.md#file-extension-configuration-requestmiddlewares-php) of an extension:

**EXT:some_extension/Configuration/RequestMiddlewares.php**

```php
<?php

return [
  'frontend' => [
    'middleware-identifier' => [
      'target' => \Vendor\SomeExtension\Middleware\ConcreteClass::class,
      'before' => [
        'another-middleware-identifier',
      ],
      'after' => [
        'yet-another-middleware-identifier',
      ],
    ],
  ],
  'backend' => [
    'middleware-identifier' => [
      'target' => \Vendor\SomeExtension\Middleware\AnotherConcreteClass::class,
      'before' => [
        'another-middleware-identifier',
      ],
      'after' => [
        'yet-another-middleware-identifier',
      ],
    ],
  ],
];

```

TYPO3 has multiple stacks where one middleware might only be necessary in one
of them. Therefore the configuration defines the context on its first level to define the
context. Within each context the middleware is registered as new subsection with
an unique identifier as key.

The default stacks are: `frontend` and `backend`.

Each middleware consists of the following options:

-   **target**

    PHP string

    FQCN (=Fully Qualified Class Name) to use as middleware.

-   **before**

    PHP Array

    List of middleware identifiers. The middleware itself is
    executed before any other middleware within this array.

-   **after**

    PHP Array

    List of middleware identifiers. The middleware itself is
    executed after any other middleware within this array.

-   **disabled**

    PHP boolean

    Allows to disable specific middlewares.

The `before` and `after` configuration is used to sort middlewares in form of a stack.
You can check the calculated order in the configuration module in TYPO3 Backend.

Middleware which is configured `before` another middleware (higher in the stack) wraps execution of following middlewares.
Code written before `$handler->handle($request);` in the `process` method can modify
the request before it's passed to the next middlewares. Code written after `$handler->handle($request);`
can modify the response provided by next middlewares.

Middleware which is configured `after` another (e.g. `MiddlewareB` from the diagram above),
will see changes to the request made by previous middleware (`MiddlewareA`),
but will not see changes made to the response from `MiddlewareA`.

## Override ordering of middlewares

To change the ordering of middlewares shipped by the Core an extension can override the registration in
[`Configuration/RequestMiddlewares.php`](../../ExtensionArchitecture/FileStructure/Configuration/RequestMiddlewaresPhp.md#file-extension-configuration-requestmiddlewares-php):

**EXT:some_extension/Configuration/RequestMiddlewares.php**

```php
<?php

return [
  'frontend' => [
    'middleware-identifier' => [
      'after' => [
        'another-middleware-identifier',
      ],
      'before' => [
        '3rd-middleware-identifier',
      ],
    ],
  ],
];

```

However, this could lead to circular ordering depending on the ordering constraints of other
middlewares. Alternatively an existing middleware can be disabled and reregistered again with a new
identifier. This will circumvent the risk of circularity:

**EXT:some_extension/Configuration/RequestMiddlewares.php**

```php
<?php

return [
  'frontend' => [
    'middleware-identifier' => [
      'disabled' => true,
    ],
    'overwrite-middleware-identifier' => [
      'target' => \MyVendor\SomeExtension\Middleware\MyMiddleware::class,
      'after' => [
        'another-middleware-identifier',
      ],
      'before' => [
        '3rd-middleware-identifier',
      ],
    ],
  ],
];

```

**EXT:some_extension/Configuration/RequestMiddlewares.php**

```php
<?php

return [
  'frontend' => [
    'middleware-identifier' => [
      'after' => [
        'another-middleware-identifier',
      ],
      'before' => [
        '3rd-middleware-identifier',
      ],
    ],
  ],
];

```

> [!WARNING]
> **Attention**
>
> Always check the integrity of the middleware stack after changing the default ordering.
> This can be done in the configuration module that comes with EXT:lowlevel.

## Creating new request / response objects

[PSR-17](https://www.php-fig.org/psr/psr-17/) HTTP Factory interfaces are provided by `psr/http-factory` and should be used as
dependencies for [PSR-15](https://www.php-fig.org/psr/psr-15/) request handlers or services that need to create [PSR-7](https://www.php-fig.org/psr/psr-7/) message objects.

It is discouraged to explicitly create [PSR-7](https://www.php-fig.org/psr/psr-7/) instances of classes from the `\TYPO3\CMS\Core\Http`
namespace (they are not public APIs). Instead, use type declarations against [PSR-17](https://www.php-fig.org/psr/psr-17/) HTTP Message Factory
interfaces and dependency injection.

### Example

A middleware that needs to send a JSON response when a certain condition is met, uses the
[PSR-17](https://www.php-fig.org/psr/psr-17/) response factory interface (the concrete TYPO3 implementation is injected as a constructor
dependency) to create a new [PSR-7](https://www.php-fig.org/psr/psr-7/) response object:

**EXT:some_extension/Classes/Middleware/StatusCheckMiddleware.php**

```php
<?php

declare(strict_types=1);

namespace MyVendor\MyExtension\Middleware;

use Psr\Http\Message\ResponseFactoryInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;

class StatusCheckMiddleware implements MiddlewareInterface
{
  /** @var ResponseFactoryInterface */
  private $responseFactory;

  public function __construct(ResponseFactoryInterface $responseFactory)
  {
    $this->responseFactory = $responseFactory;
  }

  public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
  {
    if ($request->getRequestTarget() === '/check') {
      $data = ['status' => 'ok'];
      $response = $this->responseFactory->createResponse()
          ->withHeader('Content-Type', 'application/json; charset=utf-8');
      $response->getBody()->write(json_encode($data));
      return $response;
    }
    return $handler->handle($request);
  }
}

```

## Executing HTTP requests in middlewares

The [PSR-18](https://www.php-fig.org/psr/psr-18/) HTTP Client is intended to be used by [PSR-15](https://www.php-fig.org/psr/psr-15/) request handlers in order to perform HTTP
requests based on [PSR-7](https://www.php-fig.org/psr/psr-7/) message objects without relying on a specific HTTP client implementation.

[PSR-18](https://www.php-fig.org/psr/psr-18/) consists of a client interface and three exception interfaces:

-   `\Psr\Http\Client\ClientInterface`
-   `\Psr\Http\Client\ClientExceptionInterface`
-   `\Psr\Http\Client\NetworkExceptionInterface`
-   `\Psr\Http\Client\RequestExceptionInterface`

Request handlers use dependency injection to retrieve the concrete implementation
of the [PSR-18](https://www.php-fig.org/psr/psr-18/) HTTP client interface `\Psr\Http\Client\ClientInterface`.

The [PSR-18](https://www.php-fig.org/psr/psr-18/) HTTP Client interface is provided by `psr/http-client` and may be used as
dependency for services in order to perform HTTP requests using [PSR-7](https://www.php-fig.org/psr/psr-7/) request objects.
[PSR-7](https://www.php-fig.org/psr/psr-7/) request objects can be created with the [PSR-17 Request Factory interface](https://docs.typo3.org/permalink/t3coreapi:request-handling-psr-17@main).

> [!NOTE]
> This does not replace the currently available Guzzle wrapper
> `\TYPO3\CMS\Core\Http\RequestFactory->request()`, but is available as a more generic,
> framework-agnostic alternative. The PSR-18 interface does not allow you to pass
> request-specific guzzle options. But global options defined in `$GLOBALS['TYPO3_CONF_VARS']['HTTP']`
> are taken into account because GuzzleHTTP is used as the backend for this PSR-18 implementation.
> The concrete implementation is internal and will be replaced by a native guzzle PSR-18
> implementation once it is available.

### Example usage

A middleware might need to request an external service in order to transform the response
into a new response. The PSR-18 HTTP client interface is used to perform the external
HTTP request. The PSR-17 Request Factory Interface is used to create the HTTP request that
the PSR-18 HTTP Client expects. The PSR-7 Response Factory is then used to create a new
response to be returned to the user. All of these interface implementations are injected
as constructor dependencies:

**EXT:some_extension/Classes/Middleware/ExampleMiddleware.php**

```php
<?php

declare(strict_types=1);

namespace MyVendor\MyExtension\Middleware;

use Psr\Http\Client\ClientInterface;
use Psr\Http\Message\RequestFactoryInterface;
use Psr\Http\Message\ResponseFactoryInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;

final readonly class ExampleMiddleware implements MiddlewareInterface
{
  public function __construct(
    private ResponseFactoryInterface $responseFactory,
    private RequestFactoryInterface $requestFactory,
    private ClientInterface $client,
  ) {}

  public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
  {
    if ($request->getRequestTarget() === '/example') {
      $req = $this->requestFactory->createRequest('GET', 'https://api.external.app/endpoint.json');
      // Perform HTTP request
      $res = $this->client->sendRequest($req);
      // Process data
      $data = [
        'content' => json_decode((string)$res->getBody()),
      ];
      $response = $this->responseFactory->createResponse()
          ->withHeader('Content-Type', 'application/json; charset=utf-8');
      $response->getBody()->write(json_encode($data));
      return $response;
    }
    return $handler->handle($request);
  }
}

```

## Debugging

In order to see which middlewares are configured and to see the order of
execution, TYPO3 offers a the menu entry **HTTP Middlewares (PSR-15)**
within the "Configuration" module:

![](../../Images/ManualScreenshots/RequestHandling/ConfigurationMiddleware.png)
