Skip to content

Routing in TYPO3 v9: Getting a trailing slash or .html suffix in URL

The PageType Decorator enables both suffixes (and more). But when using it, all typeNum must be taken into account.

Introduction

To configure the desired URL suffix, RealURL provided the configurations defaultToHTMLsuffixOnPrev and acceptHTMLsuffix. The Routing feature added in TYPO3 v9 allows configuring the suffix, too. For this purpose, the PageType Enhancer exists.

The PageType Enhancer (or Decorator)

The PageType Enhancer allows adding suffixes for the different typeNum of a TYPO3 website. Its result is always appended at the end of a URL.

Reminder: PAGE objects in TYPO3 always have a type. You can assign an individual ID (integer) to a PAGE object with the typeNum property. If this property is missing in your TypoScript, the default value 0 is used. You can call the configured page types in the frontend with the GET parameter &type=123.

This Routing Enhancer can be used for different suffixes than just / or .html for default pages. Many TYPO3 websites contain special PAGE objects for RSS feeds or sitemaps. The PageType Enhancer allows calling these for example as rss.xml or sitemap.xml.

Important: As soon as the PageType Enhancer is used, you must configure mapping for all existing PAGE objects or typeNum! Otherwise, there will be problems with the call of these types (see the last section).

Initial problems with the Routing feature

"Speaking URLs" generated directly by the TYPO3 core probably are one of the biggest features that TYPO3 has received in the last years. To make it possible, some adjustments in the core were necessary. Naturally, there were some teething problems in the first minor versions of TYPO3 v9. They have been largely resolved by now.

The following YAML configurations are added to the Site Configuration. If this term does not mean anything to you already, you will find the necessary tools in the first part of the Routing tutorial series.

Configuration for URLs with a trailing slash

In this first example, I have listed the complete contents of a config.yaml. In your site, this information will differ slightly. The relevant part for the Routing is highlighted.

The subsequent configuration leads to the following URLs:

  • Homepage (root page): mydomain.com
  • Subpages: mydomain.com/subpage/
rootPageId: 7
base: 'https://mydomain.com/'
baseVariants:
  -
    base: 'http://mydomain.test/'
    condition: 'applicationContext == "Development"'
languages:
  -
    title: English
    enabled: true
    languageId: '0'
    base: /
    typo3Language: default
    locale: en_US.UTF-8
    iso-639-1: en
    navigationTitle: English
    hreflang: en-US
    direction: ''
    flag: en-us-gb
errorHandling: {  }
routes: {  }
routeEnhancers:
  PageTypeSuffix:
    type: PageType
    default: '/'
    index: ''
    map:
      '/': 0
      feed.xml: 9818
      sitemap.xml: 1533906435

Explanation of the Routing properties, identified by line number

22) routeEnhancers:

In this section, all Routing Enhancers are configured.

23) PageTypeSuffix:

As with any configured Routing Enhancer, this is an arbitrary but unique name. However, the identifier PageTypeSuffix seems to have established itself as the standard.

24) type:

PageType is the identifier of the PageType Enhancer.

25) default:

You define the suffix for all standard pages here. The default property is mandatory! Without this property, the entire Routing Enhancer will not be used.

26) index:

This basically is the slug field equivalent for the root page. If this property is missing, the default value "index" is used. We replace it with an empty string in the configuration above. The homepage (root page) can then be called with https://www.mydomain.com.

27) map:

Here we assign the desired suffix to each typeNum. To illustrate this, I have added two more typeNum: one for a potential RSS feed under type=9818, and one for the EXT:seo sitemap, which is called with typeNum 1533906435.

Configuration for URLs with an .html suffix

This Routing Enhancer generates the following URLs:

  • Homepage (root page): mydomain.com/index.html
  • Subpages: mydomain.com/subpage.html
routeEnhancers:
  PageTypeSuffix:
    type: PageType
    default: '.html'
    index: 'index'
    map:
      '.html': 0

The homepage will always be called with "index.html". In many cases, this is not the desired result. If you want to use the .html suffix, but want to call the start page without any suffix, you currently (TYPO3 9.5.14) still need your own PageType Enhancer.

A custom PageType Enhancer: avoid "index.html" on the root page

TYPO3 developer Daniel Dorndorf kindly provided a custom PageType Enhancer on TYPO3 Forge, which will solve our problem mentioned above. Therefore, the following solution is not mine. I just added the necessary registration of this Routing Enhancer in TYPO3.

To use this custom Enhancer, you have to provide it in a TYPO3 extension. This can just as well be your template extension ("Sitepackage"). You'll need two files: first, the file which contains the PHP class with the Enhancer. Second, the ext_localconf.php to register the new Routing Enhancer.

This CustomPageTypeDecorator allows to generate the following URLs:

  • Homepage (root page): mydomain.com
  • Subpages: mydomain.com/subpage.html

/Classes/Routing/Enhancer/CustomPageTypeDecorator.php

<?php

namespace Brand\Extensionname\Classes\Routing\Enhancer;

use TYPO3\CMS\Core\Routing\Enhancer\PageTypeDecorator;
use TYPO3\CMS\Core\Routing\RouteCollection;

/**
 * Class CustomPageTypeDecorator
 */
class CustomPageTypeDecorator extends PageTypeDecorator
{
    public const IGNORE_INDEX = [
        '/index.html',
        '/index/',
    ];

    public const ROUTE_PATH_DELIMITERS = ['.', '-', '_', '/'];

    /**
     * @param \TYPO3\CMS\Core\Routing\RouteCollection $collection
     * @param array $parameters
     */
    public function decorateForGeneration(RouteCollection $collection, array $parameters): void
    {
        parent::decorateForGeneration($collection, $parameters);

        /**
         * @var string $routeName
         * @var \TYPO3\CMS\Core\Routing\Route $route
         */
        foreach ($collection->all() as $routeName => $route) {
            $path = $route->getPath();

            if (true === \in_array($path, self::IGNORE_INDEX, true)) {
                $route->setPath('/');
            }
        }
    }
}

ext_localconf.php

<?php
defined('TYPO3_MODE') or die();

// Register custom PageTypeDecorator:
$GLOBALS['TYPO3_CONF_VARS']['SYS']['routing']['enhancers'] += ['CustomPageType' => \Brand\Extensionname\Classes\Routing\Enhancer\CustomPageTypeDecorator::class];

You can, of course, adapt the PHP Namespace \Brand\Extensionname\ (in both files!).

config.yaml

routeEnhancers:
  PageTypeSuffix:
    type: CustomPageType
    default: '.html'
    index: 'index'
    map:
      '.html': 0

Please note the new type. The index entry will be filtered by the CustomPageTypeDecorator.

You could also use this custom PageType Enhancer to configure trailing slashes (like the first example). And in fact, I used it in my first projects with TYPO3 v9 for this purpose, when the issue with duplicate slashes still existed.

When using the PageType Decorator, the mapping becomes mandatory!

This circumstance can lead to problems in unexpected places:

  • The "yoast_seo" extension uses a PAGE object for the snippet preview. This preview cannot be loaded without the appropriate mapping.
  • By default, the SEO sitemap in TYPO3 v9 is available via mydomain.com/?type=1533906435. If no mapping for this typeNum exists, the index of all sitemaps is still accessible, but all links to a single sitemap will bring you to the root page.

Therefore, you'll need to check for all configured PAGE objects in your TYPO3 installation, e.g. for a JSON view or a print view. Some might be added through installed extensions.

Some commonly used typeNum:

    map:
      # EXT:news / RSS feed:
      feed.xml: 9818
      # EXT:seo / SEO sitemap:
      sitemap.xml: 1533906435
      # EXT:yoast_seo / snippet preview:
      yoast-snippetpreview.json: 1480321830
Back to tutorial list