TYPO3 Canonical-Url

veröf­fent­licht am 28 June 2018
zuletzt bearbeitet am 30 July 2018

Eine echte Canonical Url in TYPO3 die sich nicht durch AdWords Parameter und Ähnli­chem austricksen lässt und keine Sicher­heits­lücken öffnet.

update vom 18.7.2018: Ich hab eine winzige canonical url extension gebaut die nichts weiter tut als die canonical url korrekt zu generieren.

Falsche Lösungen

Um zu verste­hen, was das Problem ist zeig ich erst einmal, wie man es nicht macht.

Weg 1 (der Einfache)

Intuitiv würde man über TypoS­cript einfach die aktuelle Seite referen­zie­ren. Das sähe so aus:

page.headerData.20 = TEXT
page.headerData.20 {
    wrap = <link rel="canonical" href="|">
    typolink.parameter.data = page:uid
    typolink.returnLast = url
}

Elegant und kurz und funktio­niert auch solang man keine Erwei­te­rungen benutzt, da dieser Weg keine Get-Pa­ra­meter übernimmt.

Weg 2 (der Fleißige)

Bei diesem Weg macht man sich die Mühe und definiert jeden Parame­ter, der im Canonical vorkommen kann, einzeln.

page.headerData.20 = TEXT
page.headerData.20 {
    wrap = <link rel="canonical" href="|">
    typolink.parameter.data = page:uid
    typolink.returnLast = url
    typolink.forceAbsoluteUrl = 1
    
    typolink.useCacheHash = 1
}

[globalVar = GP:tx_news_pi1:news > 0]
page.headerData.20.typolink.additionalParams = &tx_news_pi1[news]={GP:tx_news_pi1:news}
page.headerData.20.typolink.additionalParams.insertData = 1
[end]

Auch dieser Weg funktio­niert, aber wird schwer sobald man auch noch Kombi­na­tionen an Parame­tern beachten muss. Außerdem ist er einfach nervig.

Weg 3 (der Furchtbare)

Ich kenne das ja, man hält sich für Klug und sucht eine allge­meine Lösung. Diese sieht dann meistens so aus:

page.headerData.20 = TEXT
page.headerData.20 {
    wrap = <link rel="canonical" href="|">
    typolink.parameter.data = page:uid
    typolink.returnLast = url
    typolink.forceAbsoluteUrl = 1
    
    # bitte nicht so machen !
    typolink.addQueryString = 1
    typolink.addQueryString.method = GET
    typolink.addQueryString.exclude = id, cHash, tx_indexedsearch[sword], ...
    typolink.useCacheHash = 1
}

Das löst das Problem und alle Parameter werden übernom­men. Es gibt sogar die Möglich­keit einzelne Parameter auszu­schlie­ßen. Super, oder? Was ist also das Problem? Nun, es ist sogar grob dokumentiert.

Wenn du diesen Weg also nutzt solltest du den Canonical lieber gleich weg lassen. Der einzige Grund den Link so zu bauen ist um SEO tools aus zu tricksen.

Wie also richtig?

Nach etwas Überle­gung hab ich fest gestellt das TYPO3 einem das Problem unabsicht­lich bereits löst.

Der cHash Mechanismus von TYPO3 verhin­dert eine Flut an unsin­nigen Parame­tern im Cache in dem er durch eine Checks­umme verifi­ziert das die Parameter tatsäch­lich von der TYPO3 Instanz generiert wurden. Er ist daher ideal für unseren Zweck da es von außen nicht möglich sein sollte Parameter hinzu­zu­fü­gen. Es gibt in TypoS­cript aller­dings keinen vorge­se­henen Weg an die Werte heran­zu­kom­men, also müssen wir uns einen schaffen.

namespace Extension\Hook;

class CanonicalParametersGetDataHook implements ContentObjectGetDataHookInterface
{
    public function getDataExtension($getDataString, array $fields, $sectionValue, $returnValue, ContentObjectRenderer &$parentObject)
    {
        if ($getDataString !== 'canonical_parameters') {
            return $returnValue;
        }

        // das chash_array enthält alle Parameter aus denen die Checksumme generiert wird.
        // Vorsicht: Auch der encryptionKey ist darin enthalten und muss unbedingt entfernt werden.
        $cHash_array = $GLOBALS['TSFE']->cHash_array;
        unset($cHash_array['encryptionKey']);
        return GeneralUtility::implodeArrayForUrl('', $cHash_array);
    }
}
// ext_localconf.php
$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_content.php']['getData'][$_EXTKEY] =
    \Extension\Hook\CanonicalParametersGetDataHook::class;

Nun haben wir uns eine schöne getText Funktion gebaut. Diese können wir so verwenden:

page.headerData.20 = TEXT
page.headerData.20 {
    wrap = <link rel="canonical" href="|">
    typolink.parameter.data = page:uid
    typolink.returnLast = url
    typolink.forceAbsoluteUrl = 1
    
    typolink.additionalParams.data = canonical_parameters
    typolink.useCacheHash = 1
}

Und tada, alle Get-Pa­ra­meter die für das generieren der aktuellen Seite verant­wort­lich waren sind in der url wieder vorhanden.

Parameter können außerdem auf 2 Wege ausge­schlossen werden.

  1. [FE][cHashExcludedParameters] entfernt den parameter nun sowohl aus der canonical als auch aus der cache relevance und ist daher ideal für zB. AdWords, Suchparameter …
  2. In dem Hook selbst kann man einfach weitere Parameter entfernen. Dadurch sind diese immer noch für den Cache relevant. Die ist sinnvoll für zB. eine zurück url

Einen wirkli­chen Nachteil dieser Methode hab ich noch nicht gefun­den, außer das man natür­lich etwas PHP braucht. Vielleicht bau ich auch nochmal eine Exten­sion die nichts weiter tut als ein canonical tag und die getText Funktion bereit zu stellen. Ich habe nun eine canonical url extension gebaut die den weg oben nochmal sauber implementiert.

Falls du noch etwas mehr wissen willst empfehle ich dir die Klasse/­Me­thode \TYPO3\CMS\Frontend\Page\CacheHashCalculator::getRelevantParameters einmal zu überflie­gen. Diese ist für die Liste in cHash_array verantwortlich.