---
title: "Backend routing"
manual: "TYPO3 Explained"
version: "main"
permalink: "https://docs.typo3.org/permalink/t3coreapi:backend-routing@main"
source: "ApiOverview/Backend/BackendRouting.rst"
modified: "2026-09-16T13:05:25+00:00"
---

# Backend routing

Each request to the backend is eventually executed by a controller.
A list of routes is defined which maps a given request to a controller
and an action.

Routes are defined inside extensions, in the files

-   [Configuration/Backend/Routes.php](https://docs.typo3.org/permalink/t3coreapi:extension-configuration-backend-routes@main)
    for general requests
-   [Configuration/Backend/AjaxRoutes.php](https://docs.typo3.org/permalink/t3coreapi:extension-configuration-backend-ajaxroutes@main)
    for Ajax calls

Here is an extract of [EXT:backend/Configuration/Backend/Routes.php (GitHub)](https://github.com/typo3/typo3/blob/main/typo3/sysext/backend/Configuration/Backend/Routes.php):

**EXT:backend/Configuration/Backend/Routes.php (excerpt)**

```php
<?php

use TYPO3\CMS\Backend\Controller;

return [
  // Login screen of the TYPO3 Backend
  'login' => [
    'path' => '/login',
    'access' => 'public',
    'target' => Controller\LoginController::class . '::formAction',
  ],

  // Main backend rendering setup (previously called backend.php) for the TYPO3 Backend
  'main' => [
    'path' => '/main',
    'referrer' => 'required,refresh-always',
    'target' => Controller\BackendController::class . '::mainAction',
  ],

  // ...
];

```

So, a route file essentially returns an array containing route mappings. A route
is defined by a key, a path, a referrer and a target. The "public" `access`
property indicates that no authentication is required for that action.

> [!NOTE]
> The current route object is available as a [route attribute](https://docs.typo3.org/permalink/t3coreapi:typo3-request-attribute-route@main) in the PSR-7 request object of every
> backend request. It is added through the PSR-15 middleware stack and can be
> retrieved using `$request->getAttribute('route')`.

## Backend routing and cross-site scripting

Public backend routes (those having option `'access' => 'public'`) do not
require any session token, but can be used to redirect to a route that requires
a session token internally. For this context, the backend user logged in must
have a valid session.

This scenario can lead to situations where an existing cross-site scripting
vulnerability (XSS) bypasses the mentioned session token, which can be
considered cross-site request forgery (CSRF). The difference in terminology is
that this scenario occurs on same-site requests and not cross-site - however,
potential security implications are still the same.

Backend routes can enforce the existence of an HTTP referrer header by adding a
`referrer` to routes to mitigate the described scenario.

**EXT:my_extension/Configuration/Backend/Routes.php**

```php
<?php

use MyVendor\MyExtension\Controller\MyRouteController;

return [
  'my_route' => [
    'path' => '/my-route/{identifier}',
    'referrer' => 'required,refresh-empty',
    'target' => MyRouteController::class . '::handle',
  ],
];

```

Values for `referrer` are declared as a comma-separated list:

-   `required` enforces existence of HTTP `Referer` header that has to match
    the currently used backend URL (for example, `https://example.org/typo3/`),
    the request will be denied otherwise.
-   `refresh-empty` triggers an HTML-based refresh in case HTTP `Referer`
    header is not given or empty - this attempt uses an HTML refresh, since
    regular HTTP `Location` redirect still would not set a referrer. It implies
    this technique should only be used on plain HTML responses and will not have
    any impact, for example, on JSON or XML response types.

This technique should be used on all public routes (without session token) that
internally redirect to a restricted route (having a session token). The goal is
to protect and keep information about the current session token internal.

The request sequence in the TYPO3 Core looks like this:

1.  HTTP request to `https://example.org/typo3/` having a valid user
    session
1.  Internally **public** backend route `/login` is processed
1.  Internally redirects to **restricted** backend route `/main` since an
    existing and valid backend user session was found
    \+ HTTP redirect to `https://example.org/typo3/main?token=...`
    \+ exposing the token is mitigated with `referrer` route option mentioned
    above

> [!WARNING]
> **Attention**
>
> Please keep in mind these steps are part of a mitigation strategy, which
> requires to be aware of mentioned implications when implementing custom web
> applications.

## Dynamic URL parts in backend URLs

Backend routes can be registered with path segments that contain dynamic parts,
which are then resolved into a PSR-7 request attribute called
[routing](https://docs.typo3.org/permalink/t3coreapi:typo3-request-attribute-routing-backend@main).

These routes are defined within the route path as named placeholders:

**EXT:my_extension/Configuration/Backend/Routes.php**

```php
<?php

use MyVendor\MyExtension\Controller\MyRouteController;

return [
  'my_route' => [
    'path' => '/my-route/{identifier}',
    'target' => MyRouteController::class . '::handle',
  ],
];

```

Within a controller (we use here a non-Extbase controller as example):

**EXT:my_extension/Classes/Controller/MyRouteController.php**

```php
<?php

declare(strict_types=1);

namespace MyVendor\MyExtension\Controller;

use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;

final class MyRouteController
{
  public function handle(ServerRequestInterface $request): ResponseInterface
  {
    $routing = $request->getAttribute('routing');
    $myIdentifier = $routing['identifier'];
    $route = $routing->getRoute();
    // ...
  }
}

```

## Generating backend URLs

Using the UriBuilder API, you can generate any kind of URL for the backend, may
it be a module, a typical route or an Ajax call. Therefore use either
`buildUriFromRoute()` or `buildUriFromRoutePath()`. The
`UriBuilder` then returns a PSR-7 conform `Uri` object that can be
cast to a string when needed. Furthermore, the `UriBuilder` automatically
generates and applies the mentioned session token.

To generate a backend URL via the `UriBuilder` you'd usually use the route
identifier and optional `parameters`.

In case of Extbase controllers you can append the controller action to the route
identifier to directly target those actions. See also module configuration: [controllerActions](https://docs.typo3.org/permalink/t3coreapi:confval-backend-module-controlleractions@main).

### Via Fluid ViewHelper

To generate a backend URL in Fluid you can simply use html:`<f:be.link>` (which
is using `UriBuilder` internally).

**EXT:my_extension/Resources/Private/Templates/SomeTemplate.fluid.html**

```html
<f:be.link route="web_layout" parameters="{id:42}">go to page 42</f:be.link>
<f:be.link route="web_ExtkeyExample">go to custom BE module</f:be.link>
<f:be.link route="web_ExtkeyExample.MyModuleController_list">
    go to custom BE module but specific controller action
</f:be.link>
```

### Via PHP

Example within a controller (we use here a non-Extbase controller):

**EXT:my_extension/Classes/Controller/MyRouteController.php**

```php
<?php

declare(strict_types=1);

namespace MyVendor\MyExtension\Controller;

use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use TYPO3\CMS\Backend\Routing\UriBuilder;

final class MyRouteController
{
  public function __construct(
    private readonly UriBuilder $uriBuilder,
  ) {}

  public function handle(ServerRequestInterface $request): ResponseInterface
  {
    // ... do some stuff

    // Using a route identifier
    $uri = $this->uriBuilder->buildUriFromRoute(
      'web_layout',
      ['id' => 42],
    );

    // Using a route path
    $uri = $this->uriBuilder->buildUriFromRoutePath(
      '/record/edit',
      [
        'edit' => [
          'pages' => [
            123 => 'edit',
          ],
        ],
      ],
    );

    // Using a PSR-7 request object
    // This is typically useful when linking to the current route or module
    // in the TYPO3 backend to avoid internals with any PSR-7 request attribute.
    $uri = $this->uriBuilder->buildUriFromRequest($request, ['id' => 42]);

    // ... do some other stuff
  }
}

```

## Sudo mode

The sudo mode, as known from the install tool,
can be request for arbitrary backend modules.

You can configure the sudo mode in your backend routing like this:

**EXT:my_extension/Configuration/Backend/Routes.php**

```php
<?php

use MyVendor\MyExtension\Handlers\MyHandler;
use TYPO3\CMS\Backend\Security\SudoMode\Access\AccessLifetime;

return [
  'my-route' => [
    'path' => '/my/route',
    'target' => MyHandler::class . '::process',
    'sudoMode' => [
      'group' => 'mySudoModeGroup',
      'lifetime' => AccessLifetime::S,
    ],
  ],
];

```

See also [Custom backend modules requiring the sudo mode](https://docs.typo3.org/permalink/t3coreapi:backend-module-sudo-modules@main).

## More information

Please refer to the following resources and look at how the TYPO3 source code
handles backend routing in your TYPO3 version.

-   [TYPO3 request object](https://docs.typo3.org/permalink/t3coreapi:typo3-request@main)
-   TYPO3 Core: [EXT:backend/Configuration/Backend/AjaxRoutes.php (GitHub)](https://github.com/typo3/typo3/blob/main/typo3/sysext/backend/Configuration/Backend/AjaxRoutes.php)
-   TYPO3 Core: [EXT:backend/Configuration/Backend/Routes.php (GitHub)](https://github.com/typo3/typo3/blob/main/typo3/sysext/backend/Configuration/Backend/Routes.php)
