Skip to content
Shops & CMS

TYPO3 Performance: Caching and Load Times Under Control

How the TYPO3 page cache works, which settings switch it off, and how the cache hash, the backends and the deployment decide the load time of a page.

15 min read TYPO3CachingLadezeit

TYPO3 serves pages quickly as long as the page cache does its job. But as soon as a plugin renders on every request, a campaign parameter changes the cache hash, or a release empties the cache tables, the work moves out of storage and back into PHP and the database. This article puts TYPO3 installations into the context of field data, shows which settings disable the page cache, how the cache backends differ, and why the first request after a deployment is the most expensive one of the day.

Key takeaways

  • In the field data of the Web Almanac 2025, 79 percent of TYPO3 sites measured on mobile reach good Core Web Vitals, so the platform holds up and the outliers come from the individual configuration.
  • A dynamic element does not cost a page its cache, but config.no_cache = 1 does: TYPO3 then stops storing the rendered result and builds every request from scratch.
  • Eleven values go into the cache hash by default; every extra parameter in the address creates another copy of the same page and dilutes the hit rate.
  • The database backend is the default and does not clean up on its own: without the scheduler task the cache tables keep growing until cache access itself becomes the bottleneck.
  • After a deployment the caches are empty and the first request blocks others because of cache locks, so warmup belongs on the live system before the symlink switch.
  • A full cache shortens server time, not load time in the browser: images, fonts and third-party calls decide when the page becomes usable.

Where TYPO3 stands in field data

For its CMS chapter, the Web Almanac evaluates field data from the July 2025 crawl. Of the TYPO3 sites measured on mobile, 79 percent reach good Core Web Vitals (Web Almanac 2025, CMS chapter, figure 12.8). For Largest Contentful Paint the share is 89 percent (figure 12.9), for Interaction to Next Paint 95 percent (figure 12.11). For comparison: across all websites measured on mobile, 62 percent achieved a good LCP score in 2025 (Web Almanac 2025, Performance chapter, figure 7.7). For LCP, TYPO3 sites measured on mobile therefore sit above the mobile average of the data set; in this measurement they meet the thresholds that apply in Core Web Vitals 2026 more often.

Those numbers say little about a specific project. They describe a population in which many installations run close to the defaults: page cache active, few dynamic elements, a manageable set of extensions. As soon as a content management system has grown over years, the picture shifts. Much like the common slowdowns in WordPress installations, the outliers rarely come from the core but from what was built around it: an extension that renders per request, an address structure full of campaign parameters, a cache backend that has not been cleaned out in years. In our experience, the difference between a brisk and a sluggish TYPO3 site lies in the cache configuration, not in the version number.

Measure first, then adjust

Every change to the cache configuration should start with a measurement taken with and without a cache hit. A request against an empty cache shows what the page actually costs in computing time; a second request shows how much of that the cache takes away. Only the difference tells you whether work on rendering pays off or whether the hit rate is the real subject. Anyone who measures only the warm request will consider an installation fast that is only fast under favourable conditions.

The page cache and what disables it

TYPO3 stores the rendered result of a page and serves it from the cache on the next request. How long such an entry stays valid is controlled by config.cache_period; the default is 86,400 seconds, that is 24 hours (TYPO3 TypoScript Reference, config). A value in the page field cache_timeout overrides the global setting as soon as it is greater than zero. Short lifetimes are not a contradiction to caching: the Core API documentation explicitly notes that even 30 seconds might improve performance in some scenarios. For an overview page that changes hourly, one minute of cache is better than none.

The quickest way to lose that cache is config.no_cache = 1. The default is 0, and for good reason: with 1, the result is no longer stored in the page cache at all (TYPO3 TypoScript Reference, config). Every request then runs through the full build from TypoScript, Extbase, Fluid and database queries. In practice the setting often ends up inside a condition that was once meant for a single page and later applied globally. If you want to know what that costs, measure the time to first byte once with and once without an active cache hit.

The documentation names the way out right next to the setting: instead of disabling the cache globally, the dynamic part is changed from USER to USER_INT, or from COA to COA_INT. TYPO3 then fetches the cached part of the page from storage and renders only the dynamic areas afterwards. A search form, a display of the logged-in name or a mini basket in the header then costs a fraction of what a fully uncached page costs. The price is care: every dynamic element runs on every request, including the one made by a search engine crawler.

State of the pageWhat sits in the page cacheWhat runs per requestResponse headers
Standard page without dynamic partsthe complete pagedelivery from the cacheCache-Control with max-age
Page with a USER_INT elementthe cached frameonly the dynamic areasCache-Control: private, no-store
config.no_cache = 1no entry at allthe complete page buildCache-Control: private, no-store
Editor logged into the backendentry stays in placedelivery from the cacheCache-Control: private, no-store

Which row applies to a given request cannot be guessed. TYPO3 sends the cache headers only when four conditions are met at the same time: the page was cached, there are no *_INT or *_EXT objects on the page, no frontend user is logged in, and no backend user is logged in (TYPO3 TypoScript Reference, config.sendCacheHeaders). Otherwise the response goes out with Cache-Control: private, no-store. An editor checking the page while logged in therefore sees a different case from the one a visitor receives. If you want to know where the time in the backend actually goes, attach Server-Timing to the response and separate database, rendering and third-party calls from one another.

Cache hash: why parameters multiply the cache

A TYPO3 address can carry parameters, and every parameter can change the result. So that the cache does not serve the wrong version, TYPO3 builds a check value from the relevant inputs: the cache hash. According to the documentation, eleven values go into that hash by default, among them the page ID, the type, the group IDs, the site, the dynamic arguments and the rows of the TypoScript templates. Two requests with different hashes are two different pages as far as the cache is concerned. What that means for the number of entries and for the load on the database becomes obvious at the latest when a newsletter goes out with six campaign parameters.

cachedParametersWhiteList

Only the listed parameters are evaluated in the calculation. Effective but sharp: for every parameter not named, the hash calculation is skipped entirely.

requireCacheHashPresenceParameters

Defines which parameters are not accepted without a hash. This keeps externally generated addresses from running into the page build unchecked.

excludedParameters

The named parameters are ignored in the calculation. The usual place for campaign identifiers that do not change the result of the page.

excludedParametersIfEmpty

Counts a parameter only when it carries a value. Useful for filters that a form submits even when nothing is selected and that therefore often stay empty.

excludeAllEmptyParameters

Applies the same rule to all parameters. One line instead of a list, but without an exception for parameters whose empty state carries meaning.

enforceValidation

Validates addresses even when no hash was supplied at all. Sensible when parameters come from external sources and the cache should not grow at will.

The options only work when they match the actual address structure. A parameter that does not change the output belongs on the exclusion list; if it stays in, every campaign creates its own copy of every landing page, and the cache fills up with variants that nobody requests a second time. The other way round, a parameter that steers the output must not be excluded: the cache would then serve the version built for the previous visitor. The check is unspectacular and quickly done: group the addresses from the access log by parameter, look at each group once, and decide whether the parameter changes the result.

The hit rate is the metric, not the cache size

A large cache table is no sign of good caching; it is often the opposite: many entries that are each read once. What carries meaning is the share of requests answered from the cache. Anyone who knows that share can see immediately whether optimising the rendering pays off at all, or whether the number of variants has to come down first.

Cache backends and lifetimes

Beneath the page cache sits the caching framework with several cache groups and one backend per group. Without a configuration of its own, TYPO3 writes into the database: according to the documentation, the database backend is the default if no backend is specifically set. An entry without its own lifetime is valid for 3,600 seconds (TYPO3 documentation, cache backends). That figure does not concern the page cache but the default of the backends themselves; it explains why a self-defined application cache is cold again after an hour although nobody flushed it. How the layers above behave is described in the article on Varnish, Redis and the browser cache.

The database backend does not clean up on its own; the documentation explicitly points to the scheduler task for garbage collection. Without that task, the cache tables and their tag tables keep growing, and access to the cache becomes a load in itself. For big cache tables the documentation names the Redis backend as an alternative that reduces load on the database server. The switch is a matter of a few lines in the system configuration; deciding which group should move is the actual work. In which order the layers take effect is summarised on our page about caching strategies.

config/system/additional.php
<?php

// Keep the page cache in Redis and take load off the database server
$GLOBALS['TYPO3_CONF_VARS']['SYS']['caching']['cacheConfigurations']['pages'] = [
    'backend' => \TYPO3\CMS\Core\Cache\Backend\RedisBackend::class,
    'options' => [
        'database' => 3,
    ],
];

The NullBackend does not belong on a live system

During development the NullBackend is a popular setting so that every change is visible immediately. The TYPO3 documentation is clear on this point and warns against using it in production because it slows the system down considerably. On a production system the setting means exactly this: every request without a single cache hit, permanently. A look into the system configuration therefore belongs in every acceptance check before an installation goes live.

Deployment: the first request after the switch

After a release the caches are empty. The documentation describes the consequence plainly: the first request after a deployment usually takes a major amount of time and blocks other requests because of cache locks. Rolling out at peak time therefore shifts part of the computing work onto the first real visitors, and precisely at the moment when the load is highest. How much headroom the server has for that moment depends on server optimisation and on the number of processes that can work in parallel.

Warmup therefore belongs in a specific place: according to the documentation on the live system, in the final folder of the new release, and ideally before switching to that release. Caches prepared on a continuous integration server are usually useless because the cache hashes there do not match those of the live system. The pattern stays the same: warm the system groups, then switch, then discard the page cache. How much PHP time the build actually costs becomes visible through OPcache, JIT and the FPM workers.

deploy/release.sh
# Warm the system groups in the new release before the symlink is switched
cd /var/www/releases/2026-09-21
vendor/bin/typo3 cache:warmup --group system

# Only then switch over to the new release
ln -sfn /var/www/releases/2026-09-21 /var/www/current

# Discard the page cache so that no old versions keep being served
cd /var/www/current
vendor/bin/typo3 cache:flush --group pages

What slows a site beyond the cache

A full cache shortens server time, not load time in the browser. As soon as the response arrives within a few milliseconds, images, fonts and scripts decide when the page becomes usable. For images from the TYPO3 file storage that means: matching sizes instead of an original several thousand pixels wide, modern formats, and fixed dimensions in the markup so that nothing jumps while loading. The full chain from the original to the delivered file is covered in the article on WebP and AVIF.

  1. Record the hit rate of the page cache across a typical day, separated into logged-in and anonymous requests.
  2. List every element that runs as USER_INT or COA_INT and check per element whether the dynamic part really has to be dynamic.
  3. Group the addresses from the access log by parameter and put campaign identifiers on the exclusion list of the cache hash.
  4. Set up the scheduler task for garbage collection and watch the size of the cache tables over several weeks.
  5. Add warmup of the system groups to the deployment and discard the page cache only after the switch.
  6. Repeat the same measurement after each of these changes so that the gain is evidenced rather than asserted.

The second group of brakes sits behind the page: form journeys, availability lookups, price calculations, connections to an inventory system. They often run inside a dynamic element and therefore on every request, and they bring a runtime along that nobody controls in house. How timeouts there affect the page build, and which safeguards have proven themselves, is covered in the article on third-party systems in the page build.

A TYPO3 site does not become fast because a cache is switched on. It becomes fast once you know which share of requests actually hits that cache - and what happens to the rest.

Project experience from performance work on content management systems

Behind a login the page cache largely disappears anyway. What counts there is what the server does per request, and there every unnecessary query weighs twice as much because it is no longer spread across many visitors. How to measure that area without distorting the measurement through the login itself is described in the article on speed behind the login.

This article is based on data from: Web Almanac 2025 (CMS chapter), TYPO3 TypoScript Reference (top-level object config) and TYPO3 Core API Reference (caching framework, backends and configuration).

Related Articles

Front-end optimisation

Finding and Removing Unused CSS for Faster Loads

How much CSS a page really needs, how to measure the unused share reliably and in which order to remove ballast without breaking the rendering.

13 min read
Server & hosting

Redirect Chains: The Hidden Delay Before the First Byte

Every redirect costs a round trip before the first byte. Learn how to detect HTTP, www and slug chains and reduce them to a single hop without losing SEO value.

12 min read
Server & hosting

AI Crawler Load: When Bots Slow Down Your Server

Bots generate over half of all HTML requests. Why AI crawlers hit uncacheable long-tail URLs, how to measure the load and throttle it instead of blocking.

14 min read