Luminova Framework

PHP Luminova: Page Caching & Static Content

Last updated: 2025-11-28 11:34:50

Optimize page delivery with cached content. Store and reuse pages for faster load times, supporting static URLs, file extensions, and versioned caching.

The Luminova template content cache is a built-in system that allow you to cache the final output of a page and serve it like a static file. It works with URLs that end in an extension (such as .html) or URLs without an extension. If a URL has no extension, the request goes through your controller first. This allow you to inspect the request, modify the content, reuse an existing cached version, or let Luminova decide whether to serve the cached output or rebuild the template.


How It Works

When a page is requested, Luminova checks whether template caching is enabled. If it is, the generated content is saved for future use.

By default, cached files are stored under:

/writeable/caches/default/

You can change this directory inside your App\Config\Template configuration class.

Cached content is grouped into folders based on your application app.version. This is useful when your site uses versioned paths, common in docs and API guides websites. When you publish a new version, older cached pages remain available, avoiding unnecessary 404 errors for users browsing older documentation.


Cache Performance Testing

These tests compare page load performance under different caching scenarios in Luminova.

1. Content Caching DisabledNo caching is used; the page is rendered fully on each request.

Performance Metric for No-Cache Rendering Screenshot

Execution: 96.41 ms | Database: 11.92 ms | Memory: 2.61 MB | Files: 58


2. Regular Content Cache RenderingPage content is cached after the first request, reducing database queries and execution time.

Performance Metric for Regular Cache Rendering Screenshot

Execution: 26.61 ms | Database: 0.00 ms | Memory: 1.26 MB | Files: 38


3. Static Cache Extension Suffixing RenderingUsing a static file extension (e.g., .html) bypasses controllers and middleware, serving content almost instantly.

Performance Metric for Static Cache Rendering Screenshot

Execution: 10.63 ms | Database: 0.0 ms | Memory: 202.21 KB | Files: 22

Comparison:

  • Enabling regular caching already cuts execution time by ~3.5×.
  • Using static cache extensions further reduces execution time by ~9×, lowers memory usage, and decreases file loads.
  • Static caching is ideal for pages that rarely change and are accessed frequently.

Page Cache Setting

Several environment variables control how the cache behaves:

  • Enable Page Caching:Set page.caching to true in your .env file to activate page caching.

  • Choose Cache Directory:If needed, update the cache storage path in your application’s Template configuration class located at/app/Config/Template.php.

  • default.cache.controlDefines the Cache-Control header sent to the client.

  • page.cache.expirySets how long cached pages remain valid before they are refreshed.

  • page.cache.app.versionsAllows Luminova to serve cached pages from older application versions.

  • page.cache.latest.contentAllow you to specify array of URI patterns that should always use the latest application version when caching.

  • page.caching.uri.queryControls how Luminova builds cache keys from URLs. When enabled, query parameters (for example, company/about?foo=bar) create separate cache entries. When disabled, all variants are treated as the same URL (company/about).

Together, these settings give you flexible control over how pages are cached, reused, and refreshed across different versions of your application.


Cache Versioning

Luminova supports version-controlled caching. This allow the framework to serve cached content from older application versions when the current version has no matching cache. You enable this by listing older versions under page.cache.app.versions.

For example, if your current app version is 1.2.0 and you still want to serve old caches under 1.0.0 and 1.1.0.

Example:

This tells the caching system to fallback to those versions when needed.

page.cache.app.versions = [1.0.0, 1.1.0]
  • https://example.com/1.0.0/about and https://example.com/1.1.0/about — each serves the correct versioned content.
  • https://example.com/1.2.0/about — always serves the latest version’s cache.

Note:Versioned caching works best with versioned URLs.Non-versioned paths (like /about) always return the latest version because the cache key never changes.


Cache Priority Version

You can also define URI patterns that should only use the latest version’s cache. Add these patterns to the page.cache.latest.content.

Example:

page.cache.latest.content = ['/', 'blog', 'blog/*/foo', 'foo/*']

The framework then searches for cached content for these paths only in the current version’s cache directory.


Non-Expiring Content

Luminova allows you to create static content that doesn’t expire by configuring the cache and HTTP headers correctly. To do this, set page.caching.immutable to true in your environment configuration. This tells browsers to cache the content for a long period, based on page.cache.expiry, and the immutable directive ensures the content won’t be revalidated during that time:

Examples:

Set cache immutable variable to true:

page.caching.immutable = true

The HTTP header will look like:

Cache-Control: public, max-age=31536000, immutable

set page.cache.expiry to 0 (or a very high value) to prevent the cache from being cleared:

page.cache.expiry = 0

For assets like images or CSS that you want to cache indefinitely, it’s recommended to use versioned URLs. Appending a version number to the file name, path or query parameter (e.g, ?version=<?= APP_VERSION;?>), ensures that browsers fetch updated content when it changes, while older versions remain cached:

<img src="<?= asset('images/v-' . APP_VERSION . '/logo.png'); ?>" alt="Logo">
<link rel="stylesheet" href="<?= asset('css/v-' . APP_VERSION . 'styles.css'); ?>">

Finally, configure your web server to serve these files with a long-term caching strategy.

For example, in Nginx:

location /static/ {
    expires 1y;
    add_header Cache-Control "public, max-age=31536000, immutable";
}

With this setup, Luminova serves static content efficiently, keeps it cached for a long time, and allows safe updates using versioned assets.


Static Cache URL Extension Suffixing

By appending extensions to URLs when caching is enabled, Luminova can serve content as partially static. In this mode, only the framework’s autoloaded modules and Luminova\Cache\StaticCache are loaded before serving the cached page. Controller methods and other modules are skipped, which can reduce view response time by up to 30% compared to standard caching without a URL extension.


Static Caching Setup

Static caching in Luminova works by associating cached pages with specific file extensions. To enable this, define the allowed extensions in page.caching.statics. Multiple types can be separated with a pipe |:

page.caching.statics = html|json

Common Types:

html, json, txt, xml, rdf, atom, rss, css, js

Note:Luminova can cache any content type the browser can render.The types listed above are just the defaults within view class.

Controller Examples:

The extension you define must match the type used when rendering the view via the Luminova\Template\View class methods.

namespace App\Controllers\Http;

use Luminova\Base\Controller;
use Luminova\Attributes\Route;
use Luminova\Attributes\Prefix;

#[Prefix('/(:base)')]
class MainController extends Controller
{
    #[Route('/page')]
    public function htmlView(): int 
    {
        return $this->view('home', type: 'html'); // Or View::HTML
    }

    #[Route('/page/style')]
    public function cssView(): int 
    {
        return $this->view('style', type: 'css'); // Or View::CSS
    }

    #[Route('/page/settings')]
    public function jsonView(): int 
    {
        return $this->view('settings', type: 'json'); // Or View::JSON
    }
}

After defining the allowed types, append the corresponding extension to your request URL.

For example, using .html to serve static contents immediately:

https://example.com/page.html
https://example.com/page/style.css
https://example.com/page/settings.json

This tells Luminova to check for a cached page first. If a valid cached version exists, it is served immediately; otherwise, the page is rendered normally and the compiled content is stored for future requests.


Cache Extension Suffixing Examples

For a blog page at:

https://example.com/blog/how-to-install-luminova

Appending .html serves it as static content:

https://example.com/blog/how-to-install-luminova.html

If a cached version exists, it loads instantly. If not, the page is rendered normally and the compiled HTML is saved.

Accessing the page with an unsupported extension, like:

https://example.com/blog/how-to-install-luminova.json

will fail unless a .json version is provided in your controller.


Caching Conflicts with Error Pages

Sometimes a cached page can be mistakenly replaced by an error page.

For example, if you visit (https://example.com/page/action/about.json), and it triggers a 404, Luminova may cache the 404.php page using the same key as the intended page (about or about.html). This will happen because URL-based cache key doesn't use extensions when hashing cache keys.

Effect:Subsequent visits to:

https://example.com/page/action/about
https://example.com/page/action/about.html

may serve the cached error page instead of the correct content.

Bast Practice:

When using a custom error page or password protected pages, that you renders manually based on certain conditions, ensure that this page are excluded from caching.


Preventing Error Pages from Being Cached

To avoid this, exclude error pages from caching. You can do this globally in your application:

namespace App;
use Luminova\Foundation\Core\Application as CoreApplication;

class Application extends CoreApplication
{
    protected function onCreate(): void 
    {
        $this->view->noCaching([
            '404',
            '4xx',
            '5xx'
            'error',
            'login'
        ]);
    }
}

Tip:You can also disable caching for specific pages within a controller before rendering:$this->tpl->noCaching('error');


Important Limitation

When a specific .extension is appended to a view request URL (e.g., .html, .json), Luminova treats the request as static content. As a result, application events, middleware, and after-middleware will not be executed.

Note:

This is intentional. Treating the request as static ensures the cached version is optimized for speed and performs better than a non-static cache request.


Canonical URL for Static Pages

Adding a canonical link tag to your pages helps search engines identify the preferred URL, preventing duplicate content issues when multiple versions of the same page exist. For static cached pages, include the canonical URL in the <head> section of your page.

Examples:

If you are using luminova structured data embedding module:

// /app/Application/php
namespace App;

use \Luminova\Component\Seo\Schema;
use \Luminova\Foundation\Core\Application as CoreApplication;

class Application extends CoreApplication
{
    /**
     * @var Schema|null $Schema;
    */
    public ?Schema $schema = null;

    protected function onCreate(): void
    {
        $this->schema = new Schema(); 
        $uri = $this->getUri();

        $this->schema->setUrl(APP_URL, $uri);
        $this->schema->setCanonical(APP_URL . $uri);

        if($uri){
            $this->schema->setCanonical(APP_URL . "{$uri}.html");
        }
    }
}

Then In Template

<head>
<?= $this->app->schema->getHeadTags();?>
<?= $this->app->schema->getJsonLdScript();?> 
</head>

Result:

<link rel="canonical" href="https://www.example.com/blog/how-to-install-luminova"/>
<link rel="canonical" href="https://www.example.com/blog/how-to-install-luminova.html"/>

In this example, both the standard URL and the .html static version are shown. Using a canonical tag ensures search engines index the correct URL for your content.


How Luminova Stores Cached Content

Luminova’s caching system stores pre-rendered page content along with metadata to deliver pages quickly and reduce server load. Each cached page includes information like Content-Type, Expiry, ETag, and whether the cache is immutable.

When a page is requested, the framework first checks the metadata to see if a valid cached version exists. If it does, the cached content is served immediately, skipping the normal rendering process. If the cache has expired or is invalid, the page is rendered normally and stored for future requests.


Benefits of This Approach

  • Faster Page Loads: Serving pre-rendered content reduces server processing time.
  • Flexible Cache Control: Metadata like MaxAge and CacheImmutable allows fine-tuned control over caching behavior.
  • Optimized Performance: Frequently accessed pages that rarely change benefit the most, improving overall site speed and reducing server load.

This mechanism provides a balance between performance and control, making Luminova caching suitable for both static pages and dynamic content that needs occasional updates.