Cache Settings

Hey everyone, it looks like that the cache settings did change to version 9. Is there a documentation for it? I have for instance the feeling, that the page cache is not anymore working, is this true? And it also looks like the response header cache settings get not anymore set? Thanks for some inputs.

Hi, I came on to ask a similar question which response header are you looking at? It would be good to know how we can check if full page caching is working.

I think I just got full page caching working by selecting “On in all cases” in the caching dashboard page, I seem to get a result in the cache-control header now.

Be careful with the use of ‘On in all cases’. By explicitly ignoring block cache preferences, you could introduce problems with user specific content or interactions.

For example, a page could be cached with a block showing admin specific information, then viewed by a visitor. A form input could be cached and always shown, but never a form response. A page could be cached showing an alert message, then the alert repeated to everyone for the next N hours.

I am not saying all uses of ‘On in all cases’ are dangerous or will fail. For some sites or some pages with vanilla content and no interaction ‘On in all cases’ is both safe and fast. For highly in-page interactive sites it will likely cause issues. You need to assess carefully how it applies specifically to each page in a site.

1 Like

Thats the funny thing, my cache-control gets not set. In the past it was like this and it was dangerous like @JohntheFish describes.
But when i analyze it now, it looks like it gets not anymore set. It is now on the way to solve with the normal file cache way of doing it. But on my tests both stuff is some how included but not working. So i have the feeling they want to move away from cache-control to file cache. Which i also would say makes more sense, but both is still not completely done. But i wanna know whats the idea, so i can work for a solution? Or check whats going wrong?

Concrete5 CMS Caching Guide

Block Caching

1. btCacheBlockRecord

  • Default: true
  • BlockDeveloper setting: block_record
  • Functionality:
    • When Config::get('concrete.cache.blocks') is enabled, block fields (usable in view as $this->set($key, $value)) are cached in the Blocks table by bID.
    • Database-wise, all blocks derive from the Block table. This means that the fields from the specific block (e.g., btAutonav) are in the btCachedBlockRecord field of the parent table.
    • Can save a database request. Although the default is true, it still needs to be activated in the settings.

2. btCacheBlockOutput

  • Default: false
  • BlockDeveloper setting: block_output
  • Functionality:
    • Set via block dialog (context menu) in CMS admin area or in Block Controller/Developer.
    • Requires Config::get('concrete.cache.blocks') to be enabled.
    • Stores rendered view in the database.
    • Changes to the block set the lifetime of the current OutputCache to 0, causing it to be re-cached.

Important considerations:

  • Asset registration should only occur in BlockController’s registerViewAssets function, as this is loaded separately with the cached view.
  • For Ajax requests with tokens, ensure cache expires before token:
    loadImagesToken: '<?= App::make(Token::class)->generate($this->controller::GET_IMAGES) ?>'
    
    Default token validity: const VALID_HASH_TIME_THRESHOLD = 86400; // 24 hours
    Default cache is 5 years or Lifetime (see next point).
  • If client URL parameters affect the BlockController view (e.g., ?key=value), block output cache must not be activated. These are not cached separately. This is usually an architectural issue. Consider switching to route parameters (/view/?key) or handling the view changes directly on the client side.

Caching conditions:

($this->viewToRender == 'view' && $config->get('concrete.cache.blocks') && $this->block instanceof Block
&& $this->block->cacheBlockOutput() && is_object($c) && $c->isPageDraft() === false)

3. btCacheBlockOutputLifetime

  • Default: 0 (seconds, equivalent to 5 years)
  • BlockDeveloper setting: block_output_lifetime
  • Functionality:
    • Set via block dialog or in Block Controller/Developer.
    • If 0, lifetime is 5 years; otherwise:
      $btCachedBlockOutputExpires = time() + $lifetime;
      
    • For Ajax tokens with 24h threshold, set btCacheBlockOutputLifetime < 86400.

4. btCacheBlockOutputOnPost

  • Default: false
  • BlockDeveloper setting: block_output_on_post
  • Functionality:
    • Caches only on POST requests.
    • Requires btCacheBlockOutput to be enabled.
    • Condition:
      $_SERVER['REQUEST_METHOD'] != 'POST' || ($this->block->cacheBlockOutputOnPost() == true)
      

5. btCacheBlockOutputForRegisteredUsers

  • Default: false
  • BlockDeveloper setting: block_output_for_registered_users
  • Functionality:
    • Caches only for logged-in users.
    • Requires btCacheBlockOutput to be enabled.
    • Condition:
      !$u->isRegistered() || ($this->block->cacheBlockOutputForRegisteredUsers())
      

Overrides Cache

  • Config: 'concrete.cache.overrides'
  • Stores file locations in ConcreteCMS using a key/value store.
  • Key is a hash value of the file handler.

Full Page Caching

Settings

  • 'concrete.cache.pages'
  • 'concrete.cache.full_page_lifetime'
  • 'concrete.cache.full_page_lifetime_block'

Options:

  • 0: Manual activation per page
  • blocks: Enabled if all blocks on the page allow (no block has btCacheBlockOutput disabled)
  • all: Always enabled

Implementation:

  • Uses FilePageCache class by default, storing pages as files (not in DB).
  • Caching check occurs in DefaultBooter::bootHttpSapi().
  • Cached pages bypass rendering entirely.

Lifetime calculation:

// See getCollectionFullPageCachingLifetimeValue() in Page class
$app = Application::getFacadeApplication();

if ($this->cCacheFullPageContentOverrideLifetime == 'default') {
    $lifetime = $app['config']->get('concrete.cache.lifetime');
} elseif ($this->cCacheFullPageContentOverrideLifetime == 'custom') {
    $lifetime = $this->cCacheFullPageContentLifetimeCustom * 60;
} elseif ($this->cCacheFullPageContentOverrideLifetime == 'forever') {
    $lifetime = 31536000; // 1 year
} else {
    if ($app['config']->get('concrete.cache.full_page_lifetime') == 'custom') {
        $lifetime = $app['config']->get('concrete.cache.full_page_lifetime_value') * 60;
    } elseif ($app['config']->get('concrete.cache.full_page_lifetime') == 'forever') {
        $lifetime = 31536000; // 1 year
    } else {
        $lifetime = $app['config']->get('concrete.cache.lifetime');
    }
}

if (!$lifetime) {
    // we have no value, which means forever, but we need a numerical value for page caching
    $lifetime = 31536000;
}

Respect Block Cache Lifetime

  • Important to enable for respecting block-specific lifetimes in HTTP cache-control headers.
  • Note: This may reduce cache time to 300 seconds due to autonav controller.

Asset Caching

CSS and JavaScript Post-Processing

  • Config: 'concrete.cache.assets'
  • Combines assets by type and combination allowance.
  • Minification should be done beforehand.
  • Processing occurs in JavascriptAsset and CssAsset classes.

Theme CSS Cache

  • Config: 'concrete.cache.theme_css'
  • Caches compiled LESS files when true.

LESS Output Compression

  • Config: 'concrete.theme.compress_preprocessor_output' (default: true)
  • Config: 'concrete.theme.generate_less_sourcemap' (default: false)

HTTP Header Cache Control

Avoid setting cache-control in .htaccess. Let CMS handle it through Full Page Caching settings.

1 Like

Hey everyone, I have something new to add here. In PacheCacheRecord we have a validate function, to validate if we can deliver the cached version of a page.
Now i have the setup, that the website is behind a reverse proxy (terminates tls) and a WAF. So the request comes in with http, so it will not match canonicalUrl schema which is https.
Solutions:

  • add an alternative canonical url → then it will take this one and show to the enduser that parts are insecure on this site
  • remove canonical url completly → same effect as before
  • life with the case, that page cache never will work
  • Ask here @andrew why we do this schema check in validate() and if maybe an domain check would be enough?
  • Or the best solution: Use a certificate after WAF so that it comes in with https to the website (aslo selfsigned would be fine if your proxy accept it). Maybe also your reverse proxy accepts middlewares and you can enter the WAF there. Then you just do a routing with tls and selfsigned certificate. If somebody is interested in a config for traefik let me know.

Why is the request http, can you not fix that?
Or can you 301 redirect to https?

Web Application Firewalls (WAFs) need to inspect incoming and outgoing content to function effectively. It’s quite common in many network architectures to position WAFs behind a proxy that terminates TLS connections. This allows the WAF to analyze the decrypted traffic.

In my case, I resolved the issue by implementing a middleware solution and adding a self-signed certificate for communication between the proxy and the webserver. This setup maintains security while allowing the WAF to perform its necessary inspections.

But i’am still not sure if the check needs to be there. Why should it not be a valid cache entry in the case of being behind an proxy which terminates tls? In some cases this setup is the reason for using canonical url and than the check will always be false.

Usually reverse proxies send to nginx/apache additional headers that forward the original protocol (http or https), as well as the user IP address.
In order to let Concrete accept these headers you have to configure the trusted proxy headers in the Concrete dashboard.

That way everything will work without issues.

@Lemonbrain this is a really great overview! I want to help ensure this can be found easily by others, so I turned this post into a tutorial for you here: Concrete5 CMS Caching Guide :: Concrete CMS

Hey Myq, can i edit the tutorial. So we can add also the stuff about proxy and mlocati s input?

…/index.php/dashboard/system/permissions/trusted_proxies

You can find the settings here.

@Lemonbrain of course! I put your user as the author, so if you log into My Contributions you should see the link to edit it. Thanks!

1 Like

@Lemonbrain great work!

One small thing:

This should say “Caches also for logged-in users” instead of “only”.

thanks, is corrected

1 Like