Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Html Cache Page Bundle Laravel Package

yurikzt/html-cache-page-bundle

Caches fully rendered HTML pages and serves them on repeat requests, skipping template rendering and database queries. Simple to integrate, configurable cache lifetime, and noticeably faster responses for content-heavy PHP sites.

View on GitHub
Deep Wiki
Context7

Getting Started

v1.0.0 is the first public release: a basic HTML cache targeting Symfony 6+. That framing matters more than anything else in the release notes — this is a Symfony bundle, not a Laravel package, and the release notes disclose no README, config schema, or API surface. Treat the assessment below as a structured starting point to validate against the source, not as confirmed API documentation.

Where to look first, in this order:

  1. composer.json — the hard constraints. Confirm symfony/http-kernel and symfony/http-foundation are ^6.0, and check for a type: symfony-bundle declaration plus any extra.symfony block. This tells you whether Laravel can pull it in at all.
  2. The bundle class (typically *Bundle.php) and its DependencyInjection/Extension.php — the config tree here is the package's public API.
  3. Whatever wraps the response: an event subscriber on kernel.response, a HttpCache/Store implementation, or a middleware. That class tells you what is actually cached (status codes, content types, methods) and what the cache key is built from.
  4. Any service tagged as a cache store or key generator — these are your extension points.

Minimal steps in a Laravel app:

composer require <vendor>/<package>:^1.0

There is no Laravel package discovery for Symfony bundles, so nothing will auto-register. You will hand-wire it:

// config/app.php providers — there will be nothing to add here.
// Instead, bind the underlying cache service yourself:
$this->app->singleton(PageCache::class, fn ($app) => new PageCache(
    storage_path('framework/page-cache'),
    $app['config']->get('page-cache.ttl', 3600),
));

Before writing any of that, verify compatibility: Laravel 9 and 10 ship Symfony 6 components and are plausible hosts; Laravel 8 pins Symfony 5 and will not install.

First use case: cache fully rendered HTML for anonymous GET requests on public content pages (marketing pages, docs, product listings). That is what "basic HTML cache" implies — whole-response caching, not fragment or query caching. Start with one route, confirm the cache key and TTL behaviour, then widen.

Implementation Patterns

Middleware over the framework bridge. The highest-leverage move is to not register the Symfony bundle in Laravel. Instead, take the one or two classes that do the caching and drive them from a Laravel middleware. Laravel's own Symfony\Component\HttpKernel\HttpCache\HttpCache already exists in the framework, so a bundle that re-wraps it buys you mostly configuration — you can replicate that in ~40 lines and keep full control.

class CacheHtmlResponse
{
    public function handle($request, Closure $next)
    {
        if (! $this->shouldCache($request)) {
            return $next($request);
        }

        $key = $this->keyFor($request);

        if ($cached = Cache::get($key)) {
            return response($cached)->header('X-Page-Cache', 'HIT');
        }

        $response = $next($request);

        if ($response->isSuccessful() && ! $response->headers->has('Set-Cookie')) {
            Cache::put($key, $response->getContent(), now()->addMinutes(60));
        }

        return $response->header('X-Page-Cache', 'MISS');
    }
}

Key generation is the whole design. Build the key from the things that legitimately vary the HTML: path, sorted query string, locale, currency/session-derived display preferences, and any A/B bucket cookie. Never let raw REQUEST_URI through — parameter order and trailing slashes will fragment your cache into uselessness. Hash the normalized tuple.

Invalidation by tag or version, not by flush. The package is "basic," so assume no tag support. Two patterns that survive contact with production:

  • Versioned keys: store page-cache:version:{page} and include it in the key. On model save, increment the version. Old entries expire naturally via TTL.
  • Path-prefix sweep: write cache files under a path derived from the route, then delete by prefix in an observer on saved/deleted.
Product::observe(new class {
    public function saved(Product $p) { Cache::increment("page-version:product:{$p->id}"); }
});

Warm the cache out-of-band. A queued job that iterates your sitemap and issues internal requests through the same middleware keeps hit-rate high after deploys and invalidations. Pair with a php artisan page-cache:warm command so it is runnable by hand and by the scheduler.

Keep the boundary thin. Wrap the package in one adapter class with get/put/forget. If v1.x stalls or the Symfony coupling becomes painful, you swap the adapter for spatie/laravel-responsecache and change nothing else. Do not let the package's types leak into controllers.

Gotchas and Tips

  • The Symfony 6+ constraint is the primary risk. Laravel's Symfony component versions track the Laravel major: Laravel 10 → Symfony 6.3/6.4, Laravel 11 → Symfony 7.x. On Laravel 11 this package may refuse to install outright. Check the constraint before anything else — a composer why-not will settle it in seconds.
  • No auto-discovery, no service provider, no config publish. Every configuration value must be passed by hand. There is no php artisan vendor:publish here, and no .env keys.
  • Middleware ordering is load-bearing. Place your cache middleware after StartSession if your key depends on session state, or before it if you want cache hits for anonymous users to skip session hydration entirely. Either is defensible; picking one by accident is not.
  • Never cache a response with Set-Cookie, Cache-Control: no-store/private, or a non-200 status. A basic HTML cache will typically do what you tell it. Leaking a CSRF token or a personalised cart badge into a shared cache is the classic failure mode, and it is silent.
  • php artisan cache:clear will not clear it. If the package writes to its own directory, Laravel's cache commands are irrelevant to it. Add a dedicated page-cache:clear command, or you will discover this during an incident.
  • Debugging: add a header, not a dd(). An X-Page-Cache: HIT|MISS header (and the resolved key, behind a debug flag) answers 90% of "why is the page stale" questions. Also verify filesystem permissions on whatever directory it writes to — a silent write failure looks exactly like a permanent miss.
  • v1.0.0 with minimal release notes should be pinned exactly ("1.0.0", not "^1.0") until you have read the source and watched the repository for follow-up releases. First releases of small Symfony bundles commonly change constructor signatures and config keys in the 1.x line.
  • Extension points to identify before you build on it: the cache store/driver interface, the key generator, and the TTL configuration. If none of those are injectable, the package is a closed box and you should prefer the middleware approach above.
  • Treat "basic" as a scoping statement, not modesty. No tag invalidation, no Vary-aware keying, no ESI/fragment support, no stale-while-revalidate. If your day-to-day needs any of those, this is a prototype tool — plan the adapter so replacing it is a one-file change.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
coleus/frame
thtg88/journalism
filagora/filagora-ai-forums
rlacerda83/query-parser
socialdept/atp-orm
cboxdk/laravel-assistant-client
socialdept/atp-resolver
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings