by Yanick Witschi

Recap of the first Contao Core Developers Meeting 2026

Every year, the Contao Core development team meets twice for a short code sprint of four days. This time, we met again at the Unperfekthaus in Essen, Germany. Obligatory note: The developer meetings are crucial for the further development of Contao and therefore, as always, I call attention to the fact that the Contao Association funds these meetings. As a Supporter you can do your part.

Lukas

We were especially pleased about the participation of Lukas Bableck, who answered our call and even took time off work to attend the four-day meeting. We greatly appreciate this dedication and his commitment to Contao!

This time, a total of nine people worked together on Contao — a strong group with plenty of focus and a productive atmosphere. If you would like to learn more about Lukas, you can also find him in the "We are Contao" format.

Starting Point

At the last developer meeting, I noted that the question of the next version of Contao would deliberately remain open and that we intended to make the final decision in spring 2026. Accordingly, we discussed this topic intensively once again—and the decision has now been made:

The next version will be called Contao 6.

Anyone who regularly glanced at our roadmap will not be entirely surprised by this. It was already apparent there that after Contao 5.7, the plan was not for a version 5.8, but for a possible jump to 6.0. This step has now been officially confirmed. What remains important to us is clear classification: we continue to consistently adhere to Semantic Versioning. As soon as API breaks become necessary, there is no way around a new major version. At the same time, our proven principle of restraint remains unchanged. Just because a new major version gives us the possibility for breaking changes does not mean we will introduce them unnecessarily.

With Contao 6, our focus also remains on stability, predictability, and the smoothest possible upgrade paths. This is not about a revolution, but about a clean and necessary evolution of the platform — very much in line with what we already pursued with the move to Contao 5.

Migration to Output Encoding

As already announced in the report from the last meeting, the most important step in Contao 6 is the switch from input encoding to output encoding. Finally! After almost 20 years of Contao, thousands (!) of lines of preparatory code, new components, and carefully planned migration paths, we have now reached the point where we can actually take this step.

To put everything into proper perspective once again (even though we have repeated it many times by now), here is the (somewhat lengthy — I’m trying my best) short version of why we need to do this. And why output encoding is, in the long term, the only truly clean solution:

Why Input Encoding Is a Problem

Input encoding means we "sanitize" (or, more precisely, "encode") content already when it is stored. At first glance this sounds convenient, but it comes with a few unpleasant side effects:

  • Context is lost.

    Whether content is "dangerous" does not depend on the content itself, but on where it is later output: in HTML text, in an HTML attribute, in JavaScript, in a URL, in JSON, in an inline style, and so on. There is no such thing as a universal encoding. Any character can be a control character depending on the context — for example < and > in HTML — or not. What appears "correct" at the time of storage can suddenly be wrong when output in a different context — or lead to broken markup.

  • Data becomes distorted (and sometimes double-encoded).

    A classic case: content is already stored in encoded form and later encoded again on output — and suddenly the frontend shows &amp; instead of & or &lt; instead of <. This is not only messy but also makes further processing unnecessarily cumbersome.

  • The database is not the place for output details.

    The database should store as raw as possible what the user entered (or what is semantically intended) — without making assumptions at storage time about how and where that content will later be rendered. Input encoding mixes data storage with presentation concerns.

  • Security cannot be solved reliably this way.

    In the end, protection against XSS and related issues only works through correct output encoding in the respective context. Everything else is a workaround that will inevitably develop vulnerabilities as complexity grows and new output channels are added.

Why Output Encoding Is the Right Solution

Output encoding means we store content as unchanged as possible and only encode it at the moment of output — tailored to the specific context. This brings decisive advantages:

  • More secure because it is context-sensitive.

    HTML text is handled differently from HTML attributes, URLs differently from JavaScript or JSON. This context dependency is the core of proper XSS prevention.

  • More robust for integrations and further processing.

    Raw data can be cleanly exported, transformed, passed on via APIs, or used in other channels (feeds, JSON, headless scenarios, search, etc.) without first having to peel away layers of encoding.

  • That’s why Twig!

    Twig comes with output encoding built in (auto-escaping). This is not a "nice-to-have", but exactly the foundation required for this step.

One example that may feel particularly relevant today: LLMs and API integrations. Since the rise of LLM-powered workflows and API integrations, input encoding has become even more impractical. When content comes from external systems (or is generated by an LLM), it is not realistic to say: "Write me a text — but please make sure it is encoded exactly right for the Contao database."

The system providing the text does not know the eventual output context. Today the same content might be rendered in rich text, tomorrow in a teaser attribute, the day after tomorrow as JSON for an API, or in an email template. That is precisely why encoding must happen where the context is known: at the point of output.

A Few More Words About Twig

At this point, I deliberately want to speak up in favor of Twig once again — simply because the topic understandably concerns many people.

Yes: existing .html5 templates need to be migrated to Twig. Yes: this means effort. For you in the community — but certainly also for us on the core team. To be completely open: probably no one has had to migrate as many templates from .html5 to Twig for Contao as Moritz and Martin in the core. So we know exactly what we’re talking about.

What is important to us, however, is a clear classification: Twig is not the cause of the change, but a central part of the solution.

We did not introduce Twig because we felt like rebuilding templates or because we were bored. The decisive factor is — and remains — clean output encoding. Without a modern template engine, this goal simply cannot be achieved reliably, and I have now explained in detail why that matters. Developing our own engine would have made neither economic nor technical sense — the problem space is too complex and too security-critical. So we are deliberately relying on an established, battle-tested solution.

In the Symfony world — and therefore also in the Contao ecosystem — Twig is the de facto standard. Twig comes with secure auto-escaping out of the box. And above all: Twig is highly extensible.

This last point in particular is crucial for Contao. We do not use Twig "off the shelf"; we have deeply integrated it into our concepts. We have to — because Contao is simply powerful. You can see examples of this throughout the system:

  • Contao-specific functions for our own concepts such as insert tags, content elements, URL generation, and attribute generation
  • Numerous additional filters for CSP handling, formatting, and more
  • Completely custom language constructs for our schema.org integration as well as the slot system
  • And important in our ecosystem: the ability for extensions and the app to make adjustments to the same templates simultaneously and without conflict.

This extensibility is one of the main reasons why Twig works so well for Contao. It gives us not only a modern template syntax, but also a stable, future-proof foundation on which upcoming requirements can be implemented cleanly.

We are aware that changes always create friction. But this step is important — for security, maintainability, and the long-term evolution of Contao. The decision in favor of Twig was therefore not a matter of taste, but a very deliberate and pragmatic architectural decision. And we are convinced that it is already paying off today — and will do so even more in the future.

The Real Challenge: Correct Migration of Existing Data

So much for the theory — in practice, the major task is, of course, the migration. At the moment, many installations contain content that is already partially encoded in the database. We need to carefully and reliably "untangle" this without damaging content or causing it to display differently.

Martin and I have therefore worked on a concept that prepares the affected fields step by step throughout the entire Contao 5.7 series:

In Contao 5.7.x, the relevant areas will be identified and the necessary information will be stored in the database via the Contao migration framework (essentially as a "note" indicating what needs to be handled later and how). When upgrading to Contao 6, the migration framework can then read this information and carry out the actual transition in a transparent and traceable way.

For users, the process will change as little as possible — and that is important to us. The upgrade path remains "normal" and easy to plan:

  1. Update to the latest Contao 5.7.x
  2. Run the migrations
  3. Only then upgrade to Contao 6
  4. And run the migrations again there

So exactly the same procedure as from Contao 4.x to 5.x.

This ensures that, although the jump to Contao 6 is a major step in terms of data handling, for most people it will not feel different in day-to-day use — with clear intermediate steps and no surprises.

Shedding Ballast: Cleanup for Contao 6

You may remember the graphic from earlier blog posts: the Contao ship getting a little heavier with every additional BC layer. We are now at version 5.7 — which means we have already had seven releases to take on new ballast. Accordingly, the weight of our vessel is slowly increasing again.

Since we are taking the step to a new major version anyway, we are deliberately using the opportunity to once again break with old traditions and slim Contao down a bit. Not radically for the sake of radicalism, but selectively where it makes technical and long-term sense.

First and foremost, of course, in the course of the switch to output encoding: the .html5 templates will disappear — and with them the entire old template engine, including all interoperability layers with Twig, special escapers required for input encoding, and other historically grown complexity. Moritz has taken on this topic, and this step alone removes more than 12,000 lines of code from the core! Code we will no longer have to maintain in the future. Code we will no longer need to adapt to upcoming PHP versions. And above all, code that will no longer create subtle differences between the .html5 and Twig worlds.

All in all, this will make Contao noticeably leaner, clearer, and not least a bit more performant again.

In parallel, we have additional cleanup work on the radar — or in some cases already completed. However, this almost exclusively concerns BC layers that were properly marked as deprecated over the course of the 5.x series. Our guiding principle remains unchanged:

Remove only as much as necessary — never as much as possible.

A few concrete examples of this pragmatic approach:

  • Support for system/modules will remain.

    Why? Because it currently causes no significant maintenance overhead and there is no compelling reason to remove it now.

  • The ability to switch between the old (Contao 4.x) and new content elements (since Contao 5) will remain.

    The same applies here: as long as support is possible with reasonable effort, we will not take this flexibility away from the community.

There will be further cleanups — but always with a sense of proportion. Our goal is not a "big bang", but a healthy, well-maintainable core that will reliably carry the community through the coming years.

Contao 5.7 LTS

Activity traditionally increases noticeably around an LTS release — and Contao 5.7 is no exception. Experience shows this can even be visualized quite well: the closer a release gets to an LTS version, the more activity rises. After the release, the curve flattens somewhat until it picks up again in anticipation of the next LTS. With our two-year LTS cycle, this creates a very steady "up-and-down flow" in the project. Incidentally, this affects not only code contributions but also the number of issues reported by the community — and we are currently seeing this very clearly.

Our meeting took place just six days after the release of Contao 5.7 LTS — accordingly, the influx of bug reports on GitHub was high. This is hardly surprising, as a large part of the community typically migrates directly from LTS to LTS, while intermediate versions see significantly less widespread adoption. The result: many installations encounter new changes only with the LTS release — and feedback then arrives in concentrated form.

We used the meeting time intensively and were able to fix more than 60 reported bugs — a strong result and an important contribution to the stability of the newly released LTS. The new Twig page layout also received noticeable improvements. This led to several larger pull requests, on which Andy in particular worked extensively. It is a great example of how, alongside the wave of bug fixes, continuous quality work on the core is also moving forward.

Improvements to the Virtual Filesystem

With a view toward Contao 6, quite a bit has happened in the Virtual Filesystem (VFS). The VFS has been part of Contao for several years now and is continuously being improved. This time, the focus is on two specific enhancements that I have worked on myself.

Versioned Public File URLs

For publicly accessible files delivered via the VFS, Contao now generates versioned URLs. In other words, something like /files/downloads/about-us.pdf automatically becomes /files/downloads/about-us.pdf?version=d78fda63144c5c84.

The additional version parameter ensures that the URL automatically changes whenever the file changes. This significantly improves caching behavior in particular: browsers and proxies can cache files aggressively without users seeing outdated content.

Protected Files: Much Simpler and Safer

Previously, developers had to take care of delivering protected files correctly and checking permissions themselves. Although there was already some support via the contao.filesystem.file_download_helper service, the logic still had to be implemented individually in each controller.

With Contao 6, this becomes much simpler.

Now it is sufficient to pass the appropriate TemporaryAccessOption when calling $vfs->generatePublicUri()— Contao handles the rest automatically. This not only reduces boilerplate code but also ensures consistent and secure behavior across all integrations.

Fritz has already integrated this new approach into the player content element. This means protected videos can now be delivered cleanly and in a controlled way—a frequently requested scenario that is now possible without custom solutions.

In short: with Contao 6, the VFS becomes not only more secure but also significantly more pleasant to use — both for extensions and for core features themselves.

Miscellaneous

This blog post is already quite long — and my energy is fading — yet there would be much more to tell. As is often the case, not everything we are working on is ready to be announced, and some things simply (still) do not belong in the public spotlight.

Still, I would like to note a few noteworthy points:

  • Moritz has moved additional HTML code from our DataContainer classes into Twig templates. Another step toward a clearer separation of logic and presentation.
  • Leo once again worked on PHPUnit compatibility with newer versions. This is almost becoming a regular routine at our meetings — but an important one.
  • There is also news regarding dependencies: the minimum version of Doctrine DBAL will be raised to v4.
  • The minimum PHP version will also be set to at least PHP 8.4. Among other things, this allows us to use native lazy objects in the dependency injection container as well as modern proxy mechanisms in Doctrine ORM.
  • Backend theme support will be removed. At the same time, Sebastian continues to work intensively on modernizing our backend build chain and is consistently throwing out one MooTools-based script after another. It might be tight timing-wise for Contao 6 — about 80% is already done. But you know the Pareto principle.
  • Dave has continued the migration of frontend modules to content elements and has finally also taken on our old Email class. It will be deprecated in Contao 6, and the core will then be fully switched to the Symfony Mailer.
  • Lukas has been working on implementing native support for page images — including og:image tags—directly in the core, a feature many will be happy about.
  • Also from Lukas: a new filter in the Template Studio that makes it easier to find overridden templates. Fantastic!

By the way: During this meeting, we worked on over 100 (!) pull requests — more than 70 of them have already been merged. At the same time, numerous proofs of concept, new ideas, and additional workstreams for the Contao 6 series are underway. There is certainly no shortage of material for the coming months and meetings.

Finally, a heartfelt thank-you to our project team for the now almost traditional snack box — it is a highlight every time!

Special thanks go to Hagen, who even fired up his 3D printer to produce matching Contao cookie cutters. Marcus’s wife and daughter then lovingly brought them to life as delicious Contao cookies — and truly delighted us! And a big thank-you to Marcus himself for the handwritten letter, which we greatly appreciated. Many thanks to all of you for the effort and the wonderful surprise — we truly appreciate it.

You can find photos in the attachment to the blog post.

That's all Folks!

– Yanick

Yanick Witschi

About Yanick Witschi

Yanick is Contao's caching and resolver hero and is partly responsible for making your coffee break a lot shorter since the switch to Composer 2. He was co-founder and the first president of the predecessor of the Contao Association. As a core developer, he puts a lot of heart and soul into Contao. At terminal42, he regularly takes off with clients. He also loves cooking, basketball, tennis, politics, the African continent and astrophysics.

Add a comment

What is the sum of 7 and 5?