PHPackages                             wazum/sluggi - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Utility &amp; Helpers](/categories/utility)
4. /
5. wazum/sluggi

ActiveTypo3-cms-extension[Utility &amp; Helpers](/categories/utility)

wazum/sluggi
============

TYPO3 extension for URL slug management with inline editing, auto-sync, locking, access control, and redirects

14.12.0(2w ago)40543.5k↓40.9%30GPL-2.0-or-laterPHPPHP ^8.2CI passing

Since Mar 13Pushed 2mo ago4 watchersCompare

[ Source](https://github.com/wazum/sluggi)[ Packagist](https://packagist.org/packages/wazum/sluggi)[ Docs](https://github.com/wazum/sluggi)[ RSS](/packages/wazum-sluggi/feed)WikiDiscussions main Synced 2w ago

READMEChangelog (10)Dependencies (80)Versions (118)Used By (0)

 [![sluggi](Resources/Public/Icons/Extension.svg)](Resources/Public/Icons/Extension.svg)

sluggi
======

[](#sluggi)

*The URL Path Manager TYPO3 CMS deserves.*

[![Tests](https://github.com/wazum/sluggi/workflows/Tests/badge.svg)](https://github.com/wazum/sluggi/actions)[![PHP](https://camo.githubusercontent.com/fff68409e83d3ed7aa0b378c76366d72fa0fb1f875467162ce208929b7e753a7/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f5048502d382e32253230253743253230382e33253230253743253230382e342d626c75652e737667)](https://www.php.net/)[![TYPO3](https://camo.githubusercontent.com/667850b2b365a75f0f0d9e87136c7bd9cf8d69b71a89c876e121f5476c2ac2aa/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f5459504f332d31322e3425323025374325323031332e342e323625324225323025374325323031342e332532422d6f72616e67652e737667)](https://typo3.org/)[![Total Downloads](https://camo.githubusercontent.com/984b02c21e8ce2c192cce99e9d68e8214e70e0aed2626c71364f571407d47a3b/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f77617a756d2f736c756767692e737667)](https://packagist.org/packages/wazum/sluggi)[![GitHub Stars](https://camo.githubusercontent.com/8037d94f99709bd9e77807332096c8dd4cb1ab2ab6d0bc8f5e52b89da6d45ce1/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f73746172732f77617a756d2f736c756767693f7374796c653d666c6174)](https://github.com/wazum/sluggi/stargazers)[![License](https://camo.githubusercontent.com/77e900ae34f8da9ccccc42662fce61a94ab07ddbfe3f7d066178e824f3673dbd/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4c6963656e73652d47504c25323076322d626c75652e737667)](LICENSE)

URLs that stay in sync when titles change. Automatic redirects. Duplicate prevention on copy, move, and recycler restore. Locking, access control, conflict detection – for pages, news, events, or any record with a slug field. And a lot of attention to the details editors meet every day: save notifications that match what really happened, batch changes you can revert in one click, no redirects for pages that are still hidden, a slash in a title kept inside one segment – everything you need to manage URL paths with confidence.

Note

**TYPO3 13.4** has a core bug where `TemporaryPermissionMutationService` does not grant page-level access for redirect storage. Non-admin editors without the site root page in their webmounts cannot create redirects on slug changes. *sluggi* includes a workaround; this does not affect TYPO3 12 or 14.

One `composer require`, sensible defaults – [highly configurable](#configuration) when you need it.

 [**Try the interactive demo**](https://wazum.github.io/sluggi/)
 See every feature live in your browser – no installation required.

Installation
------------

[](#installation)

```
composer require wazum/sluggi
```

> **Version 14** is a complete rewrite of *sluggi* – modern, fully tested, and **compatible** with **TYPO3 12.4, 13.4.26+, and 14.3+**.
>
> This is the version you should install regardless of your TYPO3 version. Previous major versions are no longer maintained.

What You Get
------------

[](#what-you-get)

[![sluggi editor](Documentation/sluggi_full_editor_view.png)](Documentation/sluggi_full_editor_view.png)

**Automatic sync** – Rename a page, the URL updates. All child pages follow. Redirects are created automatically.

**Conflict detection** – Duplicate URLs are caught instantly with unique alternatives proposed.

**Locking** – Pin critical URLs so nobody accidentally breaks them.

**Access control** – Let editors change the last segment only, or restrict editing based on their page tree permissions.

**Duplicate prevention** – Unique slugs on copy, move, and recycler restore. No more 500 errors from slug collisions.

**Redirect control** – Editors choose whether to create redirects when changing a URL.

**Redirect info** – See how many redirects point to a page, with a direct link to manage them.

**Reserved URL paths** – Declare path prefixes pages must never occupy (e.g. `/api`, `/typo3`). Saving a reserved slug is rejected with an inline warning and a flash error.

**Any table** – Works with pages, news, events, or any record with a TCA slug field.

Features
--------

[](#features)

### Modern URL Path Editor

[](#modern-url-path-editor)

Out of the box, *sluggi* replaces the default slug field with a clean, focused interface:

[![Default view](Documentation/sluggi_default_view.png)](Documentation/sluggi_default_view.png)

### Auto-Sync: Change a Title, Update the URL

[](#auto-sync-change-a-title-update-the-url)

When sync is enabled, URL paths regenerate automatically when source fields (e.g. title, nav\_title) change. A badge on the source field shows it drives the URL:

[![Sync badge](Documentation/sluggi_sync.png)](Documentation/sluggi_sync.png)

- Per-record sync toggle – disable it for pages or records with manually crafted URLs
- Child pages update recursively when a parent path changes
- Redirects from old to new URL are created automatically via EXT:redirects
- Works for any table with a slug field (news, events, custom records)

### Lock URLs to Prevent Accidental Changes

[](#lock-urls-to-prevent-accidental-changes)

[![Locked URL](Documentation/sluggi_lock.png)](Documentation/sluggi_lock.png)

- Locked URLs cannot be edited and are skipped during auto-sync
- Optionally lock all descendant paths when an ancestor is locked – child pages show the inherited lock with a read-only path and a disabled toggle, as only unlocking the ancestor releases them
- Editing the full path auto-locks it to prevent sync from overwriting your work

Note

Moving a locked page updates its parent prefix to match the new location — the lock prevents edits, not relocations. The lock state itself is preserved, so the slug stays protected against further regeneration after the move.

### Translated Pages

[](#translated-pages)

[![Translated page](Documentation/sluggi_translated.png)](Documentation/sluggi_translated.png)

Translated pages inherit the sync and lock settings from the default language record. The toggles are disabled and display the parent's state – translations cannot override these flags independently. This ensures consistent URL behavior across all language versions.

### Non-Page Tables (News, Events, Custom Records)

[](#non-page-tables-news-events-custom-records)

[![Non-page table sync](Documentation/sluggi_non_page_sync.png)](Documentation/sluggi_non_page_sync.png)

Tables configured in `synchronize_tables` (e.g. `tx_news_domain_model_news`) get the same per-record sync toggle as pages. Editors can disable auto-sync for individual records where they've manually crafted a slug for SEO:

- Sync defaults to **on** for new records – slugs auto-generate from source fields
- Toggle sync **off** to keep a hand-crafted slug that won't change when the title is edited
- Source field badges appear when sync is active, showing which fields drive the slug
- Translated records inherit the sync state from their default language parent
- Sync state is stored in a separate reference table – no changes to your extension's database schema

### Granular Access Control for Editors

[](#granular-access-control-for-editors)

**Last segment only** – Non-admins edit just the final path segment. The parent path is read-only:

[![Last segment editing](Documentation/sluggi_last_segment_only.png)](Documentation/sluggi_last_segment_only.png)

**Full path editing** – A button lets permitted users temporarily unlock the full path:

[![Full path edit button](Documentation/sluggi_edit_full_path_url.png)](Documentation/sluggi_edit_full_path_url.png)

[![Full path editing enabled](Documentation/sluggi_transient_full_path_edit.png)](Documentation/sluggi_transient_full_path_edit.png)

**Hierarchy permissions** – Editing is restricted based on page tree permissions. Users can only modify segments for pages they're allowed to edit:

[![Hierarchy permissions](Documentation/sluggi_restricted_permissions.png)](Documentation/sluggi_restricted_permissions.png)

**Per page tree** – Backend group permissions apply to the whole installation. With page TSconfig you can enforce a policy for one branch or one site instead, and it applies to admins too. See [Enforcing a policy per page tree](#enforcing-a-policy-per-page-tree).

### Out-of-Sync URL Detection

[](#out-of-sync-url-detection)

When a page's URL path doesn't match the page hierarchy (e.g. after a page move, a manual admin edit, or a database import), *sluggi* detects the mismatch and informs the editor with a subtle amber highlight on the prefix, a one-time notification, and an inline note below the field:

[![Broken prefix](Documentation/sluggi_broken_prefix.png)](Documentation/sluggi_broken_prefix.png)

[![Broken prefix notification](Documentation/sluggi_broken_prefix_notification.png)](Documentation/sluggi_broken_prefix_notification.png)

The messages are tailored to the editor's permissions:

- Editors who can lock URLs are advised to either regenerate or lock the slug to keep a custom URL
- Editors without lock access are pointed to the regenerate button or advised to ask an administrator
- Admins see the full URL path with the mismatched portion highlighted

Locked and synced pages suppress the indicator entirely – locked pages have intentional custom URLs, and synced pages will self-correct on the next title change.

Note

When a page has a custom URL path (slug outside the editor's permitted hierarchy), the page is locked, AND the editor has no full-path-edit permission, the field is rendered read-only with a single "contact administrator" note. The regenerate-to-fix advice would be misleading there — the lock would block it. Editors with full-path-edit permission keep the regular controls and can still adjust the URL via the "Edit full path" button.

### Redirect Info

[](#redirect-info)

See at a glance how many redirects target a page, with a direct link to the redirects module:

[![Redirect info](Documentation/sluggi_redirect_display.png)](Documentation/sluggi_redirect_display.png)

Only shown to editors with access to the redirects module and `sys_redirect` table.

### Redirect Control

[](#redirect-control)

Let editors decide whether to create redirects when a URL changes:

[![Redirect modal](Documentation/sluggi_create_redirects.png)](Documentation/sluggi_create_redirects.png)

The choice applies recursively to all affected child pages. Self-referencing redirects are prevented automatically, and stale redirects are soft-deleted, never removed. Cleanup is limited to redirects that point to the affected page with the auto-created target format (`t3://page?uid=&…`) and a matching source host – redirects created manually via the link wizard (`t3://page?uid=` without parameters) or for other hosts are not touched.

The pre-save "Create Redirects?" modal and the post-save toast — including the accurate-count wording from [\#101226 / #99828](#fixes-for-typo3-core-issues) — both rely on *sluggi's* JavaScript handler, which is only loaded when `redirect_control` is enabled in the extension configuration. With `redirect_control` off, TYPO3 core's own (less accurate) toast is shown instead.

### Reserved URL Paths

[](#reserved-url-paths)

Prevent pages from using URL paths that conflict with external services (e.g. reverse-proxied `/api` endpoints, `/typo3` backend, `/fileadmin`).

Declare the reserved list per site in `config/sites//settings.yaml`:

```
sluggi:
  reservedPaths:
    - '/api'
    - '/typo3'
    - '/fileadmin'
```

To make the list editable through the Sites backend module instead of YAML, add the `wazum/sluggi` site set to your site's `config.yaml`:

```
dependencies:
  - wazum/sluggi
```

The "Reserved URL paths" field then appears under the site's Settings tab — one row per prefix, `+` to add, trash icon to remove:

[![Reserved URL paths in Site Settings](Documentation/sluggi_reserved_paths_settings.png)](Documentation/sluggi_reserved_paths_settings.png)

Applying the set gives you `/typo3` and `/fileadmin` as sensible defaults so editors can't accidentally shadow the backend or file mount. Remove them if you've moved `typo3MainDir` or renamed `fileadminDir`.

Matching is segment-aware: `/api` blocks `/api` and `/api/v1` but not `/api-docs` or `/apis`.

SlugReserved?`/api`✅`/api/v1`✅`/api-docs`❌`/apis`❌Editing a slug to a reserved value is rejected with a flash error; the slug stays at its previous value.

Creating a page on a reserved path — or moving one there — replaces its first path segment with a temporary one carrying a random suffix, so `/api` is saved as `/api-3f2b8c1d9e`, and a flash error asks the editor to set a proper path. The record itself is saved, so nothing else typed into the form is lost. Clearing the field instead is not an option: TYPO3 would fall back to the empty default, which ends up as `/` and collides with the site root. Editors rarely get this far — the inline warning appears while typing and the save is blocked client-side — it is the server-side safety net.

Redirects from reserved slugs are suppressed — a path that's handled by something else than TYPO3 would never route to the new slug anyway.

The feature is a no-op for sites that don't declare `sluggi.reservedPaths`.

### Re-apply URL Paths Recursively

[](#re-apply-url-paths-recursively)

Right-click any page in the page tree and select **More options &gt; Re-apply URL paths recursively** to regenerate URL paths for all descendant pages based on their current source fields (e.g. title, nav\_title):

[![Context menu](Documentation/sluggi_context_update.png)](Documentation/sluggi_context_update.png)

- Useful after reverting a slug change via TYPO3's undo notification, or when child pages have stale prefixes
- Slugs are regenerated from scratch using source fields – not by prefix replacement
- Hidden pages are included, locked pages are skipped (descendants still update unless `lock_descendants` is enabled)
- All changes share a single correlation ID – TYPO3's undo notification lets you revert everything at once
- Admin users only

### Copy URL to Clipboard

[](#copy-url-to-clipboard)

[![URL copied](Documentation/sluggi_url_copied_clipboard.png)](Documentation/sluggi_url_copied_clipboard.png)

Copying is read-only, so the button stays available even when the URL path itself cannot be edited – on locked or synchronized pages, on read-only fields, and for editors without access to the `tx_sluggi_sync` or `slug_locked` fields.

### Slug Normalization

[](#slug-normalization)

TYPO3 core turns a title like "Products/Services" into `/products/services` (two segments) instead of `/products-services` (one segment). *sluggi* fixes this globally – for manual edits, auto-sync, regeneration, and page tree inline editing. Optional underscore preservation (RFC 3986) is also available.

### Duplicate Prevention Where TYPO3 Core Doesn't

[](#duplicate-prevention-where-typo3-core-doesnt)

- **Copy**: Copied pages get unique slugs in the target location
- **Move**: Child slugs update to reflect the new parent path
- **Recycler restore**: Restored records get deduplicated slugs instead of causing 500 errors

### Excluded Page Types

[](#excluded-page-types)

Remove URL paths from page types that don't need them (Sysfolder, Recycler, Spacer). An upgrade wizard cleans up existing slugs.

Configuration
-------------

[](#configuration)

All features work out of the box with sensible defaults. Fine-tune via **System &gt; Settings &gt; Extension Configuration &gt; *sluggi***:

**Basic**

SettingDescriptionDefault`exclude_doktypes`Comma-separated doktypes excluded from slug path generation. The default `199,254` matches TYPO3 core's built-in exclusion of Spacer and Sysfolder (see `SlugHelper::resolveParentPageRecord()`). Add `255` to also exclude Recycler pages. If you use [b13/masi](https://github.com/b13/masi) to include sysfolders in URL paths, remove `254` from this list.`199,254``preserve_underscore`Keep underscores in URL paths instead of converting them to dashes. Useful when your URL convention or external systems require underscores (RFC 3986 compliant).Off`copy_url`Show a button to copy the full page URL to the clipboard. Saves editors from navigating to the frontend just to grab a link for emails, documents, or tickets.On`last_segment_only`Non-admin editors can only change the last segment of a URL path. The parent path stays read-only, preventing editors from accidentally breaking the site's URL hierarchy.Off`allow_full_path_editing`Show a button that lets permitted editors temporarily unlock the full path for editing (requires `last_segment_only`). The slug auto-locks afterwards to prevent sync from overwriting the custom path.OffNote

With [*masi*](#masi) installed, Recycler pages (`255`) are always left out of URL paths, whether or not `exclude_doktypes` lists them.

**Sync**

SettingDescriptionDefault`synchronize`Keep URLs in sync with page titles automatically. When an editor renames a page, the URL path updates instantly – no manual work, no stale URLs. Redirects from old to new are created via EXT:redirects.On`synchronize_default`Turn on sync for every newly created page. Editors can still disable it per page for manually crafted URLs.On`synchronize_tables`Extend auto-sync beyond pages to any table with a slug field. Comma-separated list, e.g. `tx_news_domain_model_news`. Each configured table gets a per-record sync toggle so editors can opt out individually. Supports multi-field generation with `fieldSeparator`.–**Lock**

SettingDescriptionDefault`lock`Let editors pin important URLs so they can't be changed accidentally. Locked paths are also skipped during auto-sync, giving you full control over critical landing page URLs.Off`lock_descendants`When a parent page has a locked URL, protect all child page URLs too. Useful for entire sections of your site where URL stability is critical (e.g. campaign landing pages). Child pages show the inherited lock in the form: the URL path is read-only and the lock toggle is disabled, since only unlocking the parent releases them.Off**Redirect**

SettingDescriptionDefault`redirect_control`Show a modal when a URL changes, letting the editor decide whether to create a redirect. Gives editors control instead of silently creating redirects they may not want. The choice applies recursively to all affected child pages.Off`show_redirects`Display the number of active redirects targeting a page below the slug field, with a link to the redirects module pre-filtered by target page. Only shown to users with access to the redirects module. Requires EXT:redirects.OffFor deployment or version-controlled configuration, set values in `config/system/additional.php`:

```
$GLOBALS['TYPO3_CONF_VARS']['EXTENSIONS']['sluggi'] = [
    'synchronize' => '1',
    'synchronize_default' => '1',
    'synchronize_tables' => 'tx_news_domain_model_news',
    'lock' => '1',
    'lock_descendants' => '0',
    'last_segment_only' => '1',
    'allow_full_path_editing' => '1',
    'exclude_doktypes' => '199,254,255',
    'copy_url' => '1',
    'preserve_underscore' => '0',
    'redirect_control' => '1',
    'show_redirects' => '1',
];
```

Site Configuration: Redirects &amp; Recursive Slug Updates
----------------------------------------------------------

[](#site-configuration-redirects--recursive-slug-updates)

*sluggi* requires [EXT:redirects](https://docs.typo3.org/c/typo3/cms-redirects/main/en-us/) which controls what happens when a page slug changes: whether child pages update recursively, whether redirects are created, how long they live, and which HTTP status code they use.

These settings are configured **per site** via [TYPO3 site sets](https://docs.typo3.org/m/typo3/reference-coreapi/main/en-us/ApiOverview/SiteHandling/SiteSettings.html). To activate them, add the `typo3/redirects` set to your site's `config.yaml` and override the defaults in `settings.yaml`.

### Step 1: Add the redirects site set

[](#step-1-add-the-redirects-site-set)

In `config/sites//config.yaml`, add `typo3/redirects` to the `dependencies` list:

```
base: 'https://example.com/'
rootPageId: 1
dependencies:
  - typo3/redirects
languages:
  # ...
```

### Step 2: Override settings

[](#step-2-override-settings)

Create `config/sites//settings.yaml` with the settings you want to change:

```
redirects:
  autoUpdateSlugs: true
  autoCreateRedirects: true
  redirectTTL: 0
  httpStatusCode: 301
```

### Available Settings

[](#available-settings)

SettingTypeDefaultDescription`redirects.autoUpdateSlugs`bool`true`Recursively update child page slugs when a parent slug changes. Works together with *sluggi*'s auto-sync – when a title change triggers a slug update on a parent page, all descendants get their slug prefix replaced automatically.`redirects.autoCreateRedirects`bool`true`Create redirect records from old to new URL when a slug changes. Only applies in the **live workspace** – editing in a workspace does not create redirects until the change is published.`redirects.redirectTTL`int`0`Lifetime in **days** for auto-created redirects. `0` means no expiration. When set, the redirect's `endtime` is calculated as creation time + TTL days.`redirects.httpStatusCode`int`307`HTTP status code for auto-created redirects. Does not affect manually created redirects. Common values: `301` (Moved Permanently – best for SEO), `302` (Found), `307` (Temporary Redirect – the default).> **How this relates to sluggi:** When *sluggi*'s auto-sync regenerates a slug (because the page title changed), EXT:redirects picks up the change and applies the settings above. If `autoUpdateSlugs` is enabled, child pages update recursively. If `autoCreateRedirects` is enabled, redirect records are created for the old URLs. The `redirectTTL` and `httpStatusCode` settings control the properties of those redirect records. When *sluggi*'s `redirect_control` feature is enabled, editors can override `autoCreateRedirects` on a per-change basis via a modal dialog.

### Ready-to-Use Configuration Presets

[](#ready-to-use-configuration-presets)

*sluggi* ships ready-to-use presets in [`Configuration/SiteSettings/`](Configuration/SiteSettings/). TYPO3's YAML loader supports [`imports`](https://docs.typo3.org/m/typo3/reference-coreapi/main/en-us/ApiOverview/YamlApi/Index.html#imports) with `EXT:` paths, so you can reference them directly from your `settings.yaml` without copying files.

Create `config/sites//settings.yaml` with an import:

```
imports:
  - { resource: 'EXT:sluggi/Configuration/SiteSettings/recommended.settings.yaml' }
```

You can override individual values after the import:

```
imports:
  - { resource: 'EXT:sluggi/Configuration/SiteSettings/recommended.settings.yaml' }

# Override just the TTL from the preset
redirects:
  redirectTTL: 180
```

**Available presets:**

PresetStatus codeTTLDescription[recommended](Configuration/SiteSettings/recommended.settings.yaml)301permanentBest for production sites where SEO matters[temporary-with-ttl](Configuration/SiteSettings/temporary-with-ttl.settings.yaml)30790 daysSites with frequently changing content (news, events, campaigns)[no-auto-redirects](Configuration/SiteSettings/no-auto-redirects.settings.yaml)––Recursive slug updates only, redirects managed externally or manually[manual-only](Configuration/SiteSettings/manual-only.settings.yaml)––No recursive updates, no auto-redirects, full manual control### How It All Fits Together

[](#how-it-all-fits-together)

What happens`autoUpdateSlugs``autoCreateRedirects`*sluggi* `synchronize`*sluggi* `redirect_control`Title changes → slug updates––**controls this**–Parent slug changes → child slugs update**controls this**–––Old URL → redirect to new URL–**controls this**––Editor chooses whether to create redirect–must be `true`–**controls this**For the full EXT:redirects documentation, see the [TYPO3 Redirects Setup](https://docs.typo3.org/c/typo3/cms-redirects/main/en-us/Setup/Index.html). For details on TYPO3 site sets and settings, see [Site Settings](https://docs.typo3.org/m/typo3/reference-coreapi/main/en-us/ApiOverview/SiteHandling/SiteSettings.html).

Permissions
-----------

[](#permissions)

Every *sluggi* feature integrates with TYPO3's standard backend user and group permissions. You decide per user group which editors can see and use which controls – the same way you manage access to any other field in TYPO3:

FieldPermission controls`pages:slug`Edit the URL path`pages:tx_sluggi_sync`Toggle sync on/off`pages:slug_locked`Lock/unlock URLs`pages:tx_sluggi_full_path`Use full path editingExample setup for a typical editorial team:

AdminSenior EditorEditorEdit URL path✓✓✓Toggle sync✓✓–Lock/unlock URLs✓✓–Full path editing✓✓–**Admin** – Full control over all URL features.

**Senior Editor** – Can lock critical URLs before a campaign launch, toggle sync for pages with manually crafted paths, and edit full URL paths when needed.

**Editor** – Can edit the last segment of a URL (with `last_segment_only` enabled), but cannot disable sync or unlock a locked URL. URLs stay consistent without extra training.

### Enforcing a policy per page tree

[](#enforcing-a-policy-per-page-tree)

Group permissions are global: one setting per backend group for the whole installation, and administrators always bypass them. To enforce a policy for a single branch or a single site, use page TSconfig on the topmost page of that branch – it is inherited by every page below.

Two standard TYPO3 settings are honoured for *sluggi*'s fields:

SettingEffect`TCAdefaults.pages.`Value for newly created pages (`tx_sluggi_sync`, `slug_locked`)`TCEFORM.pages..disabled`Hides the control, **also for administrators**Always synchronized URLs, no way to turn it off:

```
TCAdefaults.pages.tx_sluggi_sync = 1
TCEFORM.pages.tx_sluggi_sync.disabled = 1

```

Use both keys together. Each one alone gives you half a policy: `TCAdefaults` sets the value but leaves the toggle available, `TCEFORM` hides the toggle but does not decide the value.

`TCEFORM.pages..disabled` works for `tx_sluggi_sync`, `slug_locked` and `tx_sluggi_full_path`. `TCAdefaults` only applies to the two stored fields – `tx_sluggi_full_path` is a transient control and is never saved.

This affects newly created pages. Existing pages keep their current state; use **Update URL paths recursively** in the page context menu to change them.

User Settings
-------------

[](#user-settings)

Users can enable compact controls via **User Settings &gt; Personalization** to collapse controls behind a menu icon.

Slug Source Fields
------------------

[](#slug-source-fields)

*sluggi* reads the source fields for slug generation from the standard TCA [`generatorOptions.fields`](https://docs.typo3.org/m/typo3/reference-tca/main/en-us/ColumnsConfig/Type/Slug/Index.html) configuration of the `slug` column. All referenced fields are automatically detected — they get a badge in the backend and the frontend component listens to them for real-time sync.

For pages, TYPO3 core ships `['title']`. The override below is an example that puts the navigation title first and falls back to the page title:

```
'slug' => [
    'config' => [
        'type' => 'slug',
        'generatorOptions' => [
            'fields' => [['nav_title', 'title']],
        ],
    ],
],
```

For more configuration options (e.g. multiple fields, fallback chains, field separators), see the [TYPO3 TCA slug documentation](https://docs.typo3.org/m/typo3/reference-tca/main/en-us/ColumnsConfig/Type/Slug/Index.html).

For non-page tables, add the table name to the `synchronize_tables` extension setting. The slug auto-regenerates whenever a source field changes on save, unless the editor has disabled sync for that specific record via the per-record toggle.

### Changing the values a URL path is built from

[](#changing-the-values-a-url-path-is-built-from)

`ModifySlugGenerationSourceFieldsEvent` lets an extension replace the source field values a URL path is generated from, without touching the path itself. It fires on every generation path — new pages, the live preview in the backend, auto-sync, recursive updates, copy and move — so the path an editor sees is the path that gets saved.

An example: pages in a language written in a non-Latin script take their path segment from another language.

```
use TYPO3\CMS\Core\Attribute\AsEventListener;
use Wazum\Sluggi\Event\ModifySlugGenerationSourceFieldsEvent;

#[AsEventListener('my-extension/path-from-another-language')]
final readonly class GeneratePathFromAnotherLanguage
{
    private const SOURCE_LANGUAGE = [3 => 2, 4 => 0];   // page language => language to read from

    public function __invoke(ModifySlugGenerationSourceFieldsEvent $event): void
    {
        $record = $event->getRecord();
        $sourceLanguage = self::SOURCE_LANGUAGE[(int)($record['sys_language_uid'] ?? 0)] ?? null;
        if ($sourceLanguage === null) {
            return;
        }

        $title = $this->titleOfTranslation((int)($record['l10n_parent'] ?? 0), $sourceLanguage);
        if ($title !== null) {
            $event->setSourceFieldValues(['title' => $title]);
        }
    }
}
```

Three rules for a listener:

- **Source fields only.** `setSourceFieldValues()` rejects `uid`, `pid`, `sys_language_uid` and `l10n_parent`, because uniqueness is evaluated against the untouched record — changing them would make the check and the generated path describe different records.
- **Be idempotent.** With a post modifier installed (e.g. *masi*) the event fires twice for one path, once for generation and once before the post modifier rebuilds it. Setting the same values twice must be harmless.
- **Be cheap.** It fires once per page, including recursive updates across whole subtrees. Cache lookups; a database query per page turns a large update into a crawl.

`getConfiguration()` returns the configuration as seen at that call site, which is not necessarily the effective one: it may carry `columnsOverrides`, and *masi* applies page TSconfig afterwards. The event can also fire for an ancestor record while a parent prefix is being derived, so decide what to change based on `getRecord()`, not on the assumption that it is always the record being saved.

Regenerating a page because a *different* record changed is up to the listener — inject `Wazum\Sluggi\Service\SlugGeneratorService` from your own DataHandler hook. *sluggi*'s auto-sync only reacts to source fields of the record being saved.

Requirements
------------

[](#requirements)

- TYPO3 12.4, 13.4.26+, or 14.3+
- PHP 8.2+
- EXT:redirects

Suggested Extensions
--------------------

[](#suggested-extensions)

- [news-redirect-slug-change](https://github.com/georgringer/news-redirect-slug-change) – Redirects when news slugs change
- [ig-slug](https://github.com/internetgalerie/ig_slug) – Rebuild URL slugs in bulk
- [masi](https://github.com/b13/masi) – Exclude specific page slugs from subpage URL generation
- [content\_slug](https://github.com/sebkln/content_slug) – Slug field for human-readable content element anchors (`#my-section`)

*masi*
------

[](#masi)

[b13/masi](https://github.com/b13/masi) hooks in as a TCA `postModifier` and runs after slug generation, so both extensions work side by side.

- **Folders in URL paths:** keep `254` in `exclude_doktypes` to leave them out, remove it to let *masi* include them.
- **Recyclers are always skipped:** *masi* never puts a recycler in a path, so *sluggi* does the same while it is installed, regardless of `exclude_doktypes`.
- **One value per page:** *sluggi* couples `exclude_slug_for_subpages` to the default language, so it is not editable on a translation. A per-language value would not match the paths *sluggi* resolves from the rootline. Run the wizard *"Align the masi subpage exclusion with the default language"* if translations differ from an earlier setup.
- **Moving and copying keep the segment:** the page's existing last segment is carried to the new parent. A `postModifier` may still rewrite the prefix there, but not the segment. Regular edits are unaffected.

Fixes for TYPO3 Core Issues
---------------------------

[](#fixes-for-typo3-core-issues)

*sluggi* works around these known TYPO3 core issues:

- [\#109867](https://forge.typo3.org/issues/109867) – TYPO3 v14's "Create new page" modal wizard strips empty form values before posting. Combined with FormEngine's blur-based visible→hidden input sync, clicking *Next* before a field's `change`/`blur` has propagated silently creates pages with `title=''` and a misleading fallback slug. *sluggi* adds a client-side guard that commits the focused FormEngine input on each wizard button pointerdown, and a server-side hook that cancels matching new-page inserts (deferring to TCA / userTS defaults).
- [\#101226](https://forge.typo3.org/issues/101226) / [\#99828](https://forge.typo3.org/issues/99828) – TYPO3 core's post-save toast says "Slugs updated" (plural) even when only one page changed, and "redirects created" even when *sluggi* suppressed creation (hidden page, reserved path, self-referencing) — leaving editors with a "Revert redirects" button that does nothing. *sluggi* replaces the toast with one whose wording is driven by the actual counts: "URL path updated" / "URL path updated, redirect created" / "N URL paths updated" / "N URL paths updated, M redirects created". Cascade descendants whose slug actually changed count toward the page total; the "Revert redirects" button only appears when at least one redirect row was persisted and remains active.
- [\#108375](https://forge.typo3.org/issues/108375) – When multiple pages are updated in one operation — recursive slug updates (one DataHandler, many records) or multi-edit form saves (one DataHandler per record) — TYPO3 core assigns each page its own correlation ID, so the post-save "Revert update" notification only rolls back the last page. *sluggi* shares one correlation ID across the operation so the whole batch can be reverted with a single click.
- [\#108870](https://forge.typo3.org/issues/108870) – The "Revert update" notification after a slug change only rolls back child page slugs and redirect records, but not the parent page's own slug change. *sluggi* extends the rollback to include the parent page so the entire change is fully reverted.
- [\#106338](https://forge.typo3.org/issues/106338) – TYPO3 core creates an automatic redirect from the intermediate slug to the final slug even when a page is currently hidden (a draft never reachable to visitors). *sluggi* now skips the pre-save "Create Redirects?" modal on hidden pages and suppresses redirect-row creation for the directly-edited page server-side, so neither the prompt nor the row appears. Cascaded subpage redirects triggered by `SlugService::checkSubPages()` are evaluated per change-item against each descendant's own `hidden` flag — a visible descendant whose ancestor is hidden will still get its own cascaded redirect.
- [\#106349](https://forge.typo3.org/issues/106349) – TYPO3 core treats non-browsable doktypes (Spacer/SysFolder) as slug candidates, so a folder slug can collide with a regular page. *sluggi*'s default `exclude_doktypes` already clears slugs for these doktypes via `ClearSlugForExcludedDoktypes`, so the conflict cannot arise on default configuration. The upstream patch ([Gerrit 88558](https://review.typo3.org/c/Packages/TYPO3.CMS/+/88558)) is under review and will land in main/14.3/13.4.
- [\#106152](https://forge.typo3.org/issues/106152) – Restoring pages from the recycler does not check for slug conflicts, which can result in duplicate URLs and 500 errors. *sluggi* validates and deduplicates slugs on restore.
- [\#86740](https://forge.typo3.org/issues/86740) – TYPO3 core treats slashes in page titles as path separators, turning "Products/Services" into two URL segments instead of one. *sluggi* normalizes slashes to the fallback character globally.
- [\#103833](https://forge.typo3.org/issues/103833) – Renaming a slug back to a previous value can leave behind a self-referencing redirect. *sluggi* prevents creation of self-referencing redirects automatically.
- [\#97962](https://forge.typo3.org/issues/97962) – TYPO3 core always replaces underscores with the fallback character during slug generation. *sluggi* adds a `preserve_underscore` setting for RFC 3986 compliant URLs.
- [\#94003](https://forge.typo3.org/issues/94003) – When copying a page subtree, TYPO3 core changes the copied parent's slug immediately (e.g. appending `-1`) but fails to update child pages' slug prefixes accordingly. *sluggi* recalculates all slugs in the copied tree with correct parent prefixes.
- In TYPO3 13.4, `TemporaryPermissionMutationService` grants `tables_modify` for `sys_redirect` but does not grant page-level access. Editors without the site root page in their webmounts cannot create redirect records. *sluggi* bypasses page-level access checks for redirect creation DataHandler operations.

Upgrading
---------

[](#upgrading)

### 14.0.0

[](#1400)

**Configuration keys renamed**

The following extension configuration keys were renamed. Your existing values are **not** migrated automatically — update them manually in `config/system/additional.php` or via **Admin Tools &gt; Settings &gt; Extension Configuration**:

Old key (v12/v13)New key (v14)`exclude_page_types``exclude_doktypes`**URL path locking became opt-in**

Up to v13 locking a URL path was always available and only gated by the `pages:slug_locked` field permission. It is now the `lock` setting, and its default is **off** — so after upgrading, pages that already carry `slug_locked = 1` keep the flag but it is no longer evaluated: their URL path follows the page title again, and renaming a page above them moves it.

Set `lock = 1` to keep the previous behavior. The **"Enable URL path locking for sluggi"** upgrade wizard offers this and tells you how many pages are affected; it only appears while the setting is off and locked pages exist.

Note

The wizard writes to `config/system/settings.php`. If you keep extension configuration under version control in `additional.php` (or any file included after `settings.php`), that value wins over what the wizard writes — set `lock = 1` there instead. The same applies to every upgrade wizard that changes a setting.

**Configuration keys removed**

Removed keyReplacement`pages_fields`Configure source fields via TCA `generatorOptions.fields``whitelist`Use standard TYPO3 backend user/group permissions for `pages:slug``slash_replacement`Now always active, no longer configurableImportant

`pages_fields` defaulted to `[["nav_title","title"]]`, so installations that never changed it generated URL paths from the navigation title, falling back to the page title. TYPO3 core's TCA ships `['title']`, and that now applies: after the upgrade a path comes from the page title and the navigation title is ignored. Add the TCA override shown under [Slug Source Fields](#slug-source-fields) to keep the previous behavior.

**Redirect settings moved to site configuration**

Extension-level redirect keys (`redirect_lifetime`, `redirect_code`, `redirect_force_https`, `redirect_respect_query_parameters`, `redirect_keep_query_parameters`) were removed. Use TYPO3's native per-site redirect settings instead. Ready-made presets are shipped in `Configuration/SiteSettings/`.

### 14.2.0

[](#1420)

**`exclude_doktypes` default changed from empty to `199,254`** ([\#135](https://github.com/wazum/sluggi/issues/135))

Previous versions shipped with an empty `exclude_doktypes` default, which caused *sluggi*'s copy/move handlers to include Spacer and Sysfolder names in generated slug paths — contrary to TYPO3 core's built-in behavior in `SlugHelper::resolveParentPageRecord()`. The default is now `199,254` (Spacer, Sysfolder) to match core.

*sluggi* now also overrides core's `resolveParentPageRecord()` so that *all* slug generation paths (AJAX suggestions, new pages, copy, move, sync) consistently respect the `exclude_doktypes` setting. Without this override, TYPO3 core hardcodes the exclusion of Spacer and Sysfolder regardless of configuration.

**Upgrade wizard:** Run **Admin Tools &gt; Upgrade &gt; Upgrade Wizard** after updating. The wizard *"Set default excluded page types for sluggi"* sets `exclude_doktypes` to `199,254` for existing installations where it was empty.

***masi* users:** with *masi* installed the wizard sets `199` only and leaves folders to *masi*, because `254` in this list makes every folder disappear from subpage paths and the per-folder switch inert. If you ran the wizard before this behavior existed, remove `254` by hand if you want folders in URL paths. The wizard *"Clear slugs for excluded page types"* skips folders for the same reason — it empties slugs with a direct database update that writes no history and cannot be undone. See [*masi*](#masi).

Support and Feature Requests
----------------------------

[](#support-and-feature-requests)

Use the [issues tracker](https://github.com/wazum/sluggi/issues) on GitHub for support questions and new feature requests or ideas concerning the extension.

Credits and Sponsors
--------------------

[](#credits-and-sponsors)

Made with love for the TYPO3 community by [Wolfgang Klinger](https://wolfgang-klinger.dev/).

Many thanks to [plan2net GmbH](https://www.plan2.net/) for allowing me to work on the extension during my working hours and for great projects where this extension is already being used in real life.

Special thanks to [TU München](https://www.tum.de/) and other German universities that sponsored my time at *plan2net GmbH* to work on this extension (*applies to previous versions*).

License
-------

[](#license)

GPL-2.0-or-later

 [![sluggi](Resources/Public/Icons/Extension.svg)](Resources/Public/Icons/Extension.svg)

###  Health Score

67

—

FairBetter than 99% of packages

Maintenance91

Actively maintained with recent releases

Popularity51

Moderate usage in the ecosystem

Community16

Small or concentrated contributor base

Maturity90

Battle-tested with a long release history

 Bus Factor1

Top contributor holds 99.6% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~27 days

Recently: every ~15 days

Total

100

Last Release

20d ago

Major Versions

10.3.1 → 13.0.22025-02-14

10.3.2 → v11.x-dev2025-05-07

v11.x-dev → 13.0.32025-05-16

12.1.3 → 14.0.02026-02-09

13.0.4 → 14.8.22026-05-12

PHP version history (7 changes)1.0.0PHP &gt;=7.0.0

2.0.0PHP &gt;=7.2.0

12.0.0PHP ^8.1

11.0.0PHP ^7.4

11.1.0PHP &gt;= 7.4 &lt; 8.3

13.0.0PHP ^8.2

v11.x-devPHP ^7.4 || ^8.1

### Community

Maintainers

![](https://www.gravatar.com/avatar/f4ea950f22b43e010ba4f0a6effb0928a6a1d11f64d36cec0d030b757bdfa85e?d=identicon)[wazum](/maintainers/wazum)

---

Top Contributors

[![wazum](https://avatars.githubusercontent.com/u/146727?v=4)](https://github.com/wazum "wazum (249 commits)")[![peter-neumann-dev](https://avatars.githubusercontent.com/u/75690659?v=4)](https://github.com/peter-neumann-dev "peter-neumann-dev (1 commits)")

---

Tags

cmsextensionpathphprealurlredirectsseoslugtypo3typo3-cmstypo3-cms-extensiontypo3-extensionurlurl-managementurlslugroutingredirectseotypo3Speaking URLRealURLurl segmenturl path

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan

Code StylePHP CS Fixer

Type Coverage Yes

### Embed Badge

![Health badge](/badges/wazum-sluggi/health.svg)

```
[![Health](https://phpackages.com/badges/wazum-sluggi/health.svg)](https://phpackages.com/packages/wazum-sluggi)
```

###  Alternatives

[friendsoftypo3/content-blocks

TYPO3 CMS Content Blocks - Content Types API | Define reusable components via YAML

103574.3k69](/packages/friendsoftypo3-content-blocks)[yoast-seo-for-typo3/yoast_seo

Yoast SEO for TYPO3

571.8M9](/packages/yoast-seo-for-typo3-yoast-seo)[typo3/cms-redirects

TYPO3 CMS Redirects - Create manual redirects, list existing redirects and automatically createredirects on slug changes.

167.6M84](/packages/typo3-cms-redirects)[friendsoftypo3/visual-editor

TYPO3 CMS Visual Editor - Brings a modern WYSIWYG editing experience to TYPO3 CMS.

6010.0k3](/packages/friendsoftypo3-visual-editor)[eliashaeussler/typo3-form-consent

Extension for TYPO3 CMS that adds double opt-in functionality to EXT:form

15100.9k](/packages/eliashaeussler-typo3-form-consent)[pagemachine/typo3-formlog

Form log for TYPO3

23243.0k8](/packages/pagemachine-typo3-formlog)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
