The Extbase plugin route 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.

How the plugin namespace is derived 

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:

tx_<lowercased_extension>_<lowercased_plugin>
Copied!

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
routeEnhancers:
  ConferencesPlugin:
    type: Extbase
    namespace: tx_myextension_conferences
    # … routes …
Copied!

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.

Enhancer configuration 

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
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
Copied!

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 expressions with access to page (the full page record array), site, and siteLanguage.

New in version 14.2

Expression language support in limitToPages was added.

Match by backend layout — useful when layout reliably identifies plugin pages:

limitToPages:
  - 'page["backend_layout"] == "pagets__conferences"'
Copied!

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:

EXT:my_extension/Configuration/TCA/Overrides/pages.php
\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addTcaSelectItem(
    'pages',
    'module',
    [
        'label' => 'Conference plugin',
        'value' => 'conferences',
    ],
);
Copied!

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

limitToPages:
  - 'page["module"] == "conferences"'
Copied!

Plain page UIDs:

limitToPages:
  - 42
  - 99
Copied!

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()) 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:

routes:
  - routePath: '/{conference_slug}'
    _controller: 'Conference::show'
Copied!

See Defining routes for Extbase plugins 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:

aspects:
  conference_slug:
    type: PersistedAliasMapper
    tableName: tx_myextension_domain_model_conference
    routeFieldName: slug
Copied!

See Routing aspects: mapping route placeholders to URLs.

How route variants are matched 

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 

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, where an attacker fills the cache with junk variants of a page.

Strict requirements and aspects 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.

The next step is defining the individual routes inside the enhancer — see Defining routes for Extbase plugins.