Neuerungen in Contao 6
Contao 6.0 ist am 26. August 2026 erschienen. Bei einem Minor-Sprung reicht meist eine Liste der neuen Funktionen – bei einer Major-Version nicht. Hier ist vieles nicht neu, sondern weg: das Input-Encoding, die HTML5-Templates, ein Teil der PHP-API und etliche JavaScript-Hilfen im Backend.
Deswegen ist diese Referenz zweigeteilt: erst das, was Redakteur:innen im Alltag merken, dann das, was Entwickler:innen anfassen müssen.
Vorweg der Hinweis, der über allem steht: Contao 6.0 ist keine LTS-Version und wird nur bis Februar 2027 gepflegt. Ob du überhaupt jetzt schon wechseln solltest, klärt Contao 5 oder 6: Welche Version soll ich verwenden?.
Was Redakteur:innen merken
Weniger Klickwege beim Verschachteln, mehr Möglichkeiten bei geschützten Dateien – und ein Menüpunkt weniger.
- Der Menüpunkt „Templates" ist verschwunden. Contao ist vollständig auf Twig umgestellt, damit entfällt die alte Template-Verwaltung im Backend.
- Verschachtelte Elemente direkt einfügen: Inhaltselemente lassen sich unmittelbar in Akkordeons, Elementgruppen und Slider einsetzen, ohne vorher eine Ebene tiefer zu navigieren. Dazu kommt eine „Einfügen in"-Operation in der Übersicht.
- Geschützte Dateien in Video- und Download-Elementen: Contao erzeugt dafür automatisch temporäre URLs. Vorher war für geschützte Downloads eine Sonderlösung nötig.
- Seitenbild in den Seiteneigenschaften: Ein neues Bildfeld, das in der Suche und im JSON-LD der Seite auftaucht.
- JSON-LD-Typ pro Nachrichtenarchiv wählbar:
NewsArticle,ArticleoderBlog– relevant, wenn ein Archiv eigentlich ein Blog ist. - Kommentare ohne BBCode: Die BBCode-Unterstützung ist entfallen, bestehende Kommentare werden zu Klartext migriert. URLs werden dafür automatisch verlinkt.
- Passwort ändern nur noch im richtigen Modul: Im Modul „Persönliche Daten" geht das nicht mehr, dafür braucht es das Modul „Passwort ändern". Wer in einem Mitgliederbereich bisher nur „Persönliche Daten" eingebunden hat, muss das Modul ergänzen – sonst können Mitglieder ihr Passwort nach dem Update nicht mehr ändern.
- Backend-Breite sitzt jetzt in den Backend-Einstellungen statt im Benutzerprofil.
- Barrierefreiheit verbessert: ARIA-Labels dort, wo kein
<label>existiert, und der aktive Eintrag in der Breadcrumb wird als<strong>ausgezeichnet. - Nur noch ein Backend-Theme. Mehrere Backend-Themes sind nicht mehr möglich.
Die eine große Änderung: Das Input-Encoding fällt weg
Das ist die Umstellung, aus der die meisten anderen folgen – und die einzige, die auch Redakteur:innen indirekt trifft.
Bisher hat Contao Eingaben beim Speichern kodiert: Aus < wurde < in der Datenbank. Ab Contao 6 landen die Werte roh in der Datenbank, und die Kodierung passiert erst bei der Ausgabe.
- Warum das gut ist: Derselbe Inhalt lässt sich in verschiedenen Kontexten wiederverwenden – HTML, JSON-LD, XML-Feeds, APIs. Vorher war der Inhalt auf HTML festgelegt.
- Was das im Alltag bedeutet: Läuft alles sauber über Twig, merkst du nichts. Taucht nach dem Update irgendwo
&oder<im sichtbaren Text auf, ist das der Hinweis auf eine Stelle, die noch selbst kodiert – meist eine Erweiterung oder ein eigenes Template. - Für die Bestandsdaten gibt es eine Migration. Die läuft beim Update mit und stellt die vorhandenen Inhalte um.
- Double Encoding ist jetzt standardmäßig aktiv. Die Escape-Filter
contao_htmlundcontao_html_attrsowie die Contao-Escaper-Strategie, die das früher verhindert haben, sind entfernt. decodeEntitiesunduseRawRequestDatasind entfallen. HTML erlaubst du weiterhin überallowHtml; bei Feldern mitrtewird HTML automatisch bereinigt, abschalten lässt sich das überpreserveTags.
Was Entwickler:innen anfassen müssen
Ausgabe kodieren
- Gibst du HTML aus einem Hook, Callback oder Event-Listener zurück, ohne über Twig zu gehen: mit
StringUtil::specialchars()oderhtmlspecialchars()kodieren. - Enthält eine Eingabe HTML, das erhalten bleiben soll: Twig-Filter
sanitize_html('contao')oder den Servicecontao.html_sanitizer. |rawist jetzt gefährlicher als vorher. Alles, was du damit ausgibst, muss vertrauenswürdig sein. Im Zweifel|sanitize_html('contao')verwenden.- Brauchst du Escaping ohne Double Encoding, übergibst du
falseals drittes Argument anStringUtil::specialchars().
Templates
.html5-Templates funktionieren nicht mehr. Das Legacy-Template-System ist entfernt, alles läuft über Twig.- Der Twig-Namespace
@Contao_Globalheißt jetzt@Contao_User. In den meisten Fällen brauchst du ihn ohnehin nicht explizit – nimm den verwalteten@Contao-Namespace. - Der Filter
|insert_tag_rawheißt jetzt|insert_tag_htmlund erwartet vorkodierte Eingaben. Ausfoo|insert_tag_raw|rawwird alsofoo|raw|insert_tag_html– besser gleichfoo|sanitize_html('contao')|insert_tag_html. - Die verbliebenen Symfony-Bundle-Templates sind weg:
@ContaoCore/Error/layout.html.twig→@Contao/error/_layout.html.twig@ContaoCore/Image/Studio/_macros.html.twig→@Contao/component/_figure.html.twig@ContaoCore/Image/Studio/figure.html.twig→@Contao/component/_figure.html.twig@ContaoCore/blank.html.twig→ ersatzlos
- Der
FigureRendererund derfigure-Insert-Tag rendern jetzt standardmäßigcomponent/_figure.
DCA und Models
child_record_callbackist entfernt, nutze stattdessenlabel_callback.- Der Rückgabewert des
label_callbackwird jetzt HTML-kodiert. Brauchst du echtes HTML, gib einRecordLabel-Objekt mit gesetztemhtmlLabelzurück. - Models casten Werte automatisch auf den passenden Typ. Lässt sich ein Wert nicht konvertieren, fliegt eine Exception:
$contentModel->id = '123'wird zur Ganzzahl,'not_an_int'wirft. - Fehlende Werte liefern jetzt den Default statt
null. Das ist die Änderung, die am leisesten kaputtgeht – prüfe eigenen Code auf=== nullundis_null(). - SQL-Definitionen sind auf Array-Notation umgestellt.
tl_member.languageenthält keine Ländercodes mehr. Brauchst du Locales wiede_AT, stellst du das per#[AsCallback('tl_member', target: 'fields.language.options')]wieder her.
PHP-API
Widget::generate()gibt es bei Frontend-Formular-Widgets nicht mehr – nutzeWidget::parse().- Header, die per
header()gesetzt werden, werden nicht mehr in die Symfony-Response gemerged. Arbeite mit demResponse-Objekt. - Die Messenger-Marker-Interfaces
HighPriorityMessageInterface,NormalPriorityMessageInterfaceundLowPriorityMessageInterfacesind entfallen. Stattdessen:#[AsMessage('contao_prio_high|contao_prio_normal|contao_prio_low')]. - Swift Mailer wird nicht mehr unterstützt.
- Doctrine DBAL 3 wird nicht mehr unterstützt.
- Entfernt: der
RenderPageEvent, derFilterPageTypeEventund derEntityCacheTags-Service.
JavaScript im Backend
Die Rückwärtskompatibilitäts-Schichten sind raus, Ersatz sind Stimulus-Attribute und -Controller:
Backend.enableToggleSelect→data-contao--check-all-target="input"Theme.setupCtrlClick→data-contao--deeplink-target="primary"Backend.initScrollOffset/Backend.getScrollOffset→data-contao--scroll-offset-target="scrollTo"AjaxRequest.toggleFieldset→ Controllercontao--toggle-fieldsetTheme.setupTextareaResizing→ Controllercontao--textarea-autogrowTheme.setupMenuToggle,Theme.setupProfileToggle,Theme.setupSplitButtonToggle→ Controllercontao--toggle-receiverundcontao--toggle-sender- Alle übrigen
Theme.*-Methoden sind ersatzlos entfernt.
Backend-Anpassungen laufen jetzt über die Konfigurationsoptionen contao.backend.custom_css und contao.backend.custom_js.
Dateisystem
- Die DBAFS nutzt jetzt einen 128-Bit-xxHash statt md5. Sorge vor dem Update dafür, dass Datenbank und Dateisystem synchron sind, etwa mit
contao:filesync. Die alten md5-Hashes entfernt eine Migration; neu aufgebaut werden sie bei der ersten Synchronisierung nach dem Update. - Der
{{file::*}}-Insert-Tag basiert jetzt auf dem Virtual File System und kann mit geschützten Dateien umgehen. - Das Log-Verzeichnis heißt
var/logstattvar/logs.
Schon jetzt deprecated – Ziel ist Contao 7
Das läuft in Contao 6 noch, verschwindet aber in der nächsten Major-Version. Wer jetzt sowieso Hand anlegt, erledigt es gleich mit:
- Die
Input-Klasse – nutze das Request-Objekt. Hybrid,ContentElementundModule– nutze Fragment-Controller.- Contaos Service-Annotationen – nutze PHP-Attribute, also
#[AsCallback(…)]statt/** @Callback(…) */. $GLOBALS['objPage']undglobal $objPage– nutzecontao.routing.page_finder.$GLOBALS['TL_LANGUAGE']– nutze die Locale des Requests.- Der
<base>-Tag – nutze absolute Pfade für Links und Assets. - Die Twig-Filter
|insert_tag_rawund|input_encoded_to_plain_text.
Darauf kannst du dich verlassen: Contao folgt Semantic Versioning, und laut der offiziellen VERSIONING.md muss jede inkompatible API-Änderung in einer neuen Major-Version erscheinen. Die Punkte oben überstehen also 6.1, 6.2 und auch die LTS 6.3.
Für Templates gilt das ausdrücklich nicht. Sie zählen laut derselben Regel nicht zur API und dürfen sich in Minor- und sogar in Bugfix-Releases ändern – auch dann, wenn das ein angepasstes Template zerlegt oder CSS-Nacharbeit am neuen Markup erzwingt. Die Zusage „bis Contao 7 passiert nichts" gilt für die PHP-API, nicht für dein Theme.
Systemvoraussetzungen
Der Sprung ist deutlicher als bei Contao 5:
- PHP 8.4
- Symfony 7.4 oder 8
- Doctrine DBAL 4.4, Doctrine ORM 3.6
- Twig 3.21
- TinyMCE 5, 6, 7 oder 8
Gerade PHP 8.4 ist der Punkt, an dem es bei Shared Hosting klemmen kann. Das gehört vor die Update-Entscheidung, nicht mittendrin.
Vor dem Update
- Zuerst auf die aktuellste Contao 5.7 aktualisieren. Ein direkter Sprung von älteren Versionen funktioniert nicht, weil nur in 5.7 alle nötigen Migrations vorliegen.
- Dateisystem synchronisieren (
contao:filesync), damit die Hash-Umstellung sauber greift. - Erweiterungen prüfen. „Contao 5 kompatibel" sagt über Contao 6 nichts aus – durch den Wegfall des Input-Encodings und der HTML5-Templates ist der Anpassungsbedarf real.
- Nach dem Update noch einmal eine Dateisystem-Synchronisierung laufen lassen.
Siehe auch: