---
title: "The Extbase plugin route enhancer"
manual: "TYPO3 Explained"
version: "main"
permalink: "https://docs.typo3.org/permalink/t3coreapi:extbase-routing-enhancer@main"
source: "ExtensionArchitecture/Extbase/Routing/Enhancer.rst"
rendered: "2026-09-21T11:24:09+00:00"
---

# The Extbase plugin route enhancer {#extbase-routing-enhancer}

TYPO3 provides three built-in route enhancer types. For Extbase plugins, always
use `type: Extbase`. It differs from the generic `Plugin` enhancer in one
important way: it generates one route variant *per controller/action combination*
and handles the plugin argument namespace automatically.

> [!NOTE]
> **See also**
>
> -   [Routing Enhancers](https://docs.typo3.org/permalink/t3coreapi:routing-advanced-routing-configuration-enhancers@main) —
>     overview of all enhancer types and how enhancers and aspects work together.
> -   [Extbase plugin enhancer reference](https://docs.typo3.org/permalink/t3coreapi:routing-extbase-plugin-enhancer@main) —
>     the full reference entry in the Core routing chapter.

## How the plugin namespace is derived {#extbase-routing-enhancer-namespace}

Every Extbase plugin has an auto-generated namespace used to prefix all its
URL parameters. The namespace is derived from the `extension` and `plugin`
keys in the enhancer configuration:

```none
tx_<lowercased_extension>_<lowercased_plugin>
```

For `extension: MyExtension` and `plugin: Conferences` the namespace is
`tx_myextension_conferences`. You never write this by hand — the enhancer
derives it from those two keys.

Alternatively, set `namespace` directly if you need an exact string (for
example, when the auto-derived name would be wrong for a multi-word extension
key):

**EXT:my_extension/Configuration/Sets/MyExtension/route-enhancers.yaml (excerpt)**

```yaml
routeEnhancers:
  ConferencesPlugin:
    type: Extbase
    namespace: tx_myextension_conferences
    # … routes …

```

The key directly under `routeEnhancers` — `ConferencesPlugin` here — is
an arbitrary identifier you choose. It only has to be unique across all enhancers on
the site; it is not tied to the extension or plugin name. Pick something descriptive.

> [!NOTE]
> **See also**
>
> [Extbase plugin enhancer with explicit namespace](https://docs.typo3.org/permalink/t3coreapi:routing-extbase-plugin-enhancer@main) —
> the `namespace` property as an alternative to `extension` \+ `plugin`.

## Enhancer configuration {#extbase-routing-enhancer-config}

A complete enhancer for a plugin with a list and a detail action, showing the
recommended baseline. Only `type`, `extension`, `plugin` and
`routes` are strictly required; `limitToPages` is included here
because you should always scope an enhancer to its pages (see below), and
`defaultController` is omitted as it is optional:

**EXT:my_extension/Configuration/Sets/MyExtension/route-enhancers.yaml**

```yaml
routeEnhancers:
  ConferencesPlugin:
    type: Extbase
    limitToPages: [42]
    extension: MyExtension
    plugin: Conferences
    routes:
      - routePath: '/'
        _controller: 'Conference::list'
      - routePath: '/{conference_slug}'
        _controller: 'Conference::show'
        _arguments:
          conference_slug: conference
    aspects:
      conference_slug:
        type: PersistedAliasMapper
        tableName: tx_myextension_domain_model_conference
        routeFieldName: slug

```

The key properties:

-   **`type: Extbase`**

    Selects the Extbase plugin enhancer.

-   **`limitToPages`**

    Restricts the enhancer to specific pages. Always set this — without it TYPO3
    evaluates the enhancer for every page, which slows down route generation
    across the whole site.

    Each entry is OR-combined. Integer values match against
    the page UID. String values are
    [Symfony expression language](https://docs.typo3.org/permalink/t3coreapi:symfony-expression-language@main)
    expressions with access to `page` (the full page record array),
    `site`, and `siteLanguage`.

    <!-- TODO: no Markdown rendering for "versionadded" -->

    Expression language support in limitToPages was added.Match by backend layout — useful when layout reliably identifies plugin pages:

    ```yaml
    limitToPages:
      - 'page["backend_layout"] == "pagets__conferences"'
    ```

    A robust, UID-free approach is to register a custom value for the
    **Contains Plugin** page property (the `module` field) in
    [`EXT:my_extension/Configuration/TCA/Overrides/pages.php`](../../FileStructure/Configuration/TCA/Index.md#file-extension-configuration-tca-overridessomefile-php):

    **EXT:my_extension/Configuration/TCA/Overrides/pages.php**

    ```php
    <?php

    use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;

    ExtensionManagementUtility::addTcaSelectItem(
      'pages',
      'module',
      [
        'label' => 'Conference plugin',
        'value' => 'conferences',
      ],
    );

    ```

    Editors set this field on the plugin page in the backend, then the enhancer
    targets those pages without any hardcoded UIDs:

    ```yaml
    limitToPages:
      - 'page["module"] == "conferences"'
    ```

    Plain page UIDs:

    ```yaml
    limitToPages:
      - 42
      - 99
    ```

    All approaches can be mixed in one array — entries are OR-combined. Use
    `&&` inside a single string for AND logic.

-   **`extension`**

    The extension name in UpperCamelCase, without vendor prefix and without
    underscores (for example `MyExtension`, not `my_extension`).

-   **`plugin`**

    The plugin name as registered in `\TYPO3\CMS\Extbase\Utility\ExtensionUtility::configurePlugin()`
    (for example `Conferences`).

-   **`defaultController` (optional)**

    The controller/action pair to assume when an incoming URL carries no explicit
    controller or action. Written as `ControllerName::actionName` (no
    `Action` suffix, no namespace). It is only a fallback: generated URLs
    (via [uriFor()](https://docs.typo3.org/permalink/t3coreapi:extbase-routing-uri-builder@main)) always supply the
    controller and action, so a minimal enhancer can omit this key.

-   **`routes`**

    One entry per controller/action combination that should produce a readable
    URL. For example, a single route for the detail action:

    ```yaml
    routes:
      - routePath: '/{conference_slug}'
        _controller: 'Conference::show'
    ```

    See [Defining routes for Extbase plugins](https://docs.typo3.org/permalink/t3coreapi:extbase-routing-routes@main) for the full route syntax.

-   **`aspects`**

    Maps placeholder names to mappers that translate between internal values
    (UIDs) and URL segments (slugs). For example, mapping the
    `conference_slug` placeholder above to a database slug field:

    **EXT:my_extension/Configuration/Sets/MyExtension/route-enhancers.yaml**

    ```yaml
    aspects:
      conference_slug:
        type: PersistedAliasMapper
        tableName: tx_myextension_domain_model_conference
        routeFieldName: slug
    ```

    See [Routing aspects: mapping route placeholders to URLs](https://docs.typo3.org/permalink/t3coreapi:extbase-routing-aspects@main).

## How route variants are matched {#extbase-routing-enhancer-variants}

When TYPO3 receives a request, the enhancer tries each `routes` entry in
order. The first variant whose `routePath` pattern and `_controller`
match the incoming URL wins. When generating a URL, TYPO3 picks the first
variant that satisfies all required placeholders.

Order matters: put more specific routes before more general ones. A route
`/{conference_slug}` would swallow everything if listed before
`/page/{page}` — the paginated list would never match.

If no variant matches during generation, TYPO3 falls back to a plain query
string URL with the raw namespace parameters and a `cHash`.

## The `cHash` parameter {#extbase-routing-enhancer-chash}

When a URL contains dynamic parameters that are not fully constrained,
TYPO3 appends a `cHash` signature. This prevents arbitrary URIs from being
cached under the same content — both stopping the cache from growing without
bound and guarding against
[cache poisoning](https://en.wikipedia.org/wiki/Cache_poisoning), where an
attacker fills the cache with junk variants of a page.

Strict `requirements` and [aspects](https://docs.typo3.org/permalink/t3coreapi:extbase-routing-aspects@main) that
define a fixed set of valid values eliminate the need for `cHash` — but only
when *every* placeholder in the route is covered. If even one placeholder is
left unconstrained, TYPO3 still adds `cHash` to the whole URL.

A `PersistedAliasMapper` aspect on
a slug field removes the need for `cHash` for that placeholder, because the
mapper restricts it to a known set of database values rather than an open input.
A `d+` requirement alone does not — it still allows unbounded values — so only
a `StaticRangeMapper` (a fixed range)
removes `cHash` for a numeric placeholder.

> [!NOTE]
> **See also**
>
> [cHash and routing](https://docs.typo3.org/permalink/t3coreapi:routing-advanced-routing-configuration-enhancers@main) —
> background on when and why `cHash` is added.

The next step is defining the individual routes inside the enhancer — see
[Defining routes for Extbase plugins](https://docs.typo3.org/permalink/t3coreapi:extbase-routing-routes@main).
