---
title: "EXT:redirects"
manual: "Headless"
version: "main"
permalink: "https://docs.typo3.org/permalink/friendsoftypo3/headless:integrations-redirects@main"
source: "Integrations/Redirects.rst"
rendered: "2026-10-14T09:29:03+00:00"
---

# EXT:redirects {#integrations-redirects}

Install `EXT:redirects` (`composer require typo3/cms-redirects`) and
the standard redirect manager works as usual. EXT:headless then
automatically swaps the relevant frontend middlewares so a matched
redirect surfaces to a headless frontend as JSON rather than a `30x`
response — no feature flag required.

The replaced middlewares are:

-   `typo3/cms-frontend/base-redirect-resolver` →
    `FriendsOfTYPO3HeadlessMiddlewareSiteBaseRedirectResolver`
-   `typo3/cms-frontend/shortcut-and-mountpoint-redirect` →
    `FriendsOfTYPO3HeadlessMiddlewareShortcutAndMountPointRedirect`

Matched redirects from the redirect manager are turned into JSON by the
`headless/RedirectWasHit` event listener (see below) — the core
`redirecthandler` middleware stays in place.

> [!NOTE]
> The middleware swap only registers when `EXT:redirects` is
> installed. Without it, *all* redirects — including plain shortcut
> and mount-point pages — are served as real HTTP `30x` responses
> even in headless mode. The same applies per request in MIXED mode:
> without exactly `Accept: application/json` as the first Accept
> header value, redirects degrade to real `30x` responses.

Requests for the headless page-content type (`?type=834`) bypass
shortcut and mount-point redirect handling entirely and are resolved
downstream instead.

## JSON response shape {#json-response-shape}

A matched redirect produces:

```json
{
  "redirectUrl": "https://example.com/new-target",
  "statusCode": 301
}
```

`redirectUrl` is run through `UrlUtility::prepareRelativeUrlIfPossible()`,
so internal targets come back as relative paths (`/new-target`) when
they land on the same frontend host.

The HTTP response itself is always `200` — `statusCode` tells the
frontend which redirect to perform, and the frontend must apply the
usual method semantics itself (`301`/`302` may switch to GET,
`307`/`308` preserve the request method). Its value depends on the
source:

-   redirect-manager records: the record's configured target status
    code;
-   shortcut/mount-point and site-base redirects: the HTTP code the core
    middleware would have sent (typically `307`).

## Customising the redirect {#customising-the-redirect}

The JSON envelope is built by
`FriendsOfTYPO3HeadlessEventListenerHeadlessRedirectResponseListener`,
which listens to the core `TYPO3CMSRedirectsEventRedirectWasHitEvent`
(identifier `headless/RedirectWasHit`). Register your own listener for
the same event `after` it and replace the response:

```php
final readonly class TagRedirectsForAnalytics
{
    public function __invoke(RedirectWasHitEvent $event): void
    {
        $response = $event->getResponse();
        if (!$response instanceof JsonResponse) {
            return;
        }

        $payload = json_decode((string)$response->getBody(), true);
        $payload['redirectUrl'] .= '?utm_source=redirect';
        $event->setResponse(new JsonResponse($payload));
    }
}
```

```yaml
# Configuration/Services.yaml
services:
  Vendor\MyExt\EventListener\TagRedirectsForAnalytics:
    tags:
      - name: event.listener
        identifier: 'myext/redirect/analytics'
        after: 'headless/RedirectWasHit'
```

Page targets carrying Extbase plugin parameters in the typolink
`additionalParams` segment are rebuilt through the site router by
`FriendsOfTYPO3HeadlessRedirectsTargetUrlResolver` (the core
`RedirectService` drops that segment unless "keep query parameters"
is enabled). Override or extend that service via `Services.yaml`
when you need different target URL resolution.

## Short URLs & QR codes (5.x) {#short-urls-qr-codes-5-x}

The "Short URLs" and "QR Codes" backend modules of `EXT:redirects` show
source URLs on the *TYPO3* host — useless when the public site lives on the
frontend domain. With headless, both modules (and the QR-code/short-URL
fields in redirect records) resolve source URLs against the site's
`frontendBase` instead, so copied links and scanned codes land on the
public frontend. No configuration needed beyond `frontendBase`; resolution
logic can be replaced by overriding
`FriendsOfTYPO3HeadlessRedirectsSourceUrlResolver` via `Services.yaml`.
