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

Laravel Cloudflare Cache Laravel Package

yediyuz/laravel-cloudflare-cache

Integrate Cloudflare edge caching into Laravel: cache responses, automatically purge URLs or tags on model changes, and manage cache behavior from familiar Laravel APIs. Reduce origin load and speed up responses with minimal configuration.

View on GitHub
Deep Wiki
Context7

Getting Started

  1. Install and publish config:
    composer require yediyuz/laravel-cloudflare-cache
    php artisan vendor:publish --tag="cloudflare-cache-config"
    
  2. Set the minimum environment variables in .env:
    CLOUDFLARE_CACHE_API_TOKEN=...
    CLOUDFLARE_CACHE_ZONE_ID=...
    CLOUDFLARE_CACHE_ENABLED=true
    
    The API token needs Zone → Cache Purge permission (and Zone → Zone Settings if you plan to toggle development mode). Never use a Global API Key — use a scoped token.
  3. Look here first:
    • config/cloudflare-cache.php — TTLs, tag prefix, bypass rules, environment gating.
    • The package’s middleware (usually CloudflareCache in the HTTP kernel or aliased) — this is what actually writes Cache-Control / CDN-Cache-Control headers to responses.
    • The facade (e.g. CloudflareCache::purge(), ::purgeTags()) — used from observers, jobs, and deploy scripts.
  4. First use case: cache a public marketing page at the edge.
    Route::get('/pricing', PricingController::class)
        ->middleware('cloudflare-cache:3600');
    
    Hit it once to warm the edge, then verify with:
    curl -I https://example.com/pricing | grep -i -E 'cf-cache|age|cache-control'
    
    You want cf-cache-status: HIT and/or an increasing age.

The mental model: the package does not cache anything itself. It shapes the response headers Cloudflare reads, and it calls the Cloudflare purge API. Your Laravel cache driver is irrelevant to the edge cache.

Implementation Patterns

Route-scoped middleware with explicit TTLs. Don’t apply caching globally. Put the middleware on the routes that are genuinely public and static: landing pages, product listings, category indexes, sitemaps. TTLs of 300–3600s are typical; anything that changes faster should be purged rather than short-TTL’d.

Two headers, two purposes. Treat Cache-Control as the browser TTL and CDN-Cache-Control (or Cloudflare-CDN-Cache-Control) as the edge-only TTL. A common setup is short browser cache, long edge cache, and purge-driven invalidation at the edge:

$response->headers->set('Cache-Control', 'public, max-age=60');
$response->headers->set('CDN-Cache-Control', 'public, max-age=86400');

Tag on write, purge on update. Add a stable tag per entity, then purge the tag inside an observer or model event:

// Response
$response->headers->set('Cache-Tag', implode(',', ["product-{$product->id}", 'products']));

// Observer
class ProductObserver {
    public function saved(Product $product): void {
        CloudflareCache::purgeTags(["product-{$product->id}", 'products']);
    }
}

Keep a small allowlist of tags (products, home, sitemap) so purges stay predictable.

Purge on deploy, not on every request. Add a step to your deploy pipeline (Envoyer, Deployer, GitHub Actions) that purges the full zone or a set of high-level tags. This is the single highest-value use of the package day-to-day: stale edge content after a release is the most common complaint.

Bypass cache for authenticated or personalized requests. Either omit the middleware, or use a middleware ordering where your auth middleware forces private, no-store. Cloudflare will not cache responses with Set-Cookie or Cache-Control: private, but be explicit rather than relying on that.

Queue purges. Wrap purge calls in a job dispatched after commit (->afterCommit() or DB::afterCommit()), so you don’t purge for a transaction that rolls back, and so a purge-API timeout doesn’t fail the user request.

Use it with Laravel’s own cache, not instead of it. The edge cache does not replace Cache::remember() for origin-side load reduction. Layer: Laravel cache → Cloudflare edge → purge hooks.

Gotchas and Tips

  • Tag purge is plan-gated. The Cache-Tag response header is widely supported, but the purge-by-tag API is an Enterprise-plan feature in Cloudflare. On Free/Pro/Business, plan on single-URL purges (purgeUrls([...])) or purgeEverything(). Check your plan before building a tag-based architecture around this package.
  • Purge rate limits. Cloudflare’s purge endpoint is heavily rate-limited (URL purges are limited per day, and purgeEverything has cooldowns). Batch URLs into a single call rather than looping. A purgeEverything() in a tight loop will burn your quota and return 429s that the package may or may not surface clearly.
  • Purges are not instant or transactional. Propagation is typically sub-second but not guaranteed. Do not write tests or UX that assume a purge is visible on the very next request.
  • Vary and cookies silently kill caching. If your app sets a session cookie, CSRF cookie, or Vary: Cookie on a response you thought was cacheable, Cloudflare will bypass it and you’ll see cf-cache-status: DYNAMIC or BYPASS. Inspect response headers with curl -I on a fresh session.
  • Only GET and HEAD are cached. POST/PUT/DELETE go straight to origin. This matters for API routes that you might be tempted to wrap.
  • The package is header-shaped, not logic-shaped. If you need Cloudflare-specific behavior (cache rules, origin rules, workers), you still need to configure those in the Cloudflare dashboard. This package only sets headers and calls purge.
  • Environment safety. Default CLOUDFLARE_CACHE_ENABLED=false locally/CI so a saved() event in a test suite doesn’t purge your production zone. Guard with app()->environment('production') in the config or in the purge wrapper.
  • Debugging checklist: (1) curl -I and inspect cf-cache-status, age, cache-control, cdn-cache-control, cache-tag; (2) confirm the zone is not in Development Mode (which forces BYPASS); (3) confirm the DNS record is proxied (orange cloud) — an unproxied record means Cloudflare never sees the request; (4) confirm the URL has no cache-busting query string.
  • Extension points: the package typically exposes a facade and a config-driven middleware. If the middleware’s header logic doesn’t fit, subclass it or write a one-line middleware of your own that sets the same headers — you don’t need the package for header shaping, only for the purge client. For the purge client, wrap the facade in your own service class so you can swap it out or no-op it in tests.
  • Testing: fake the HTTP client (Http::fake()) rather than the facade, so you actually assert the purge request payload and URL. Assert that observers dispatch the purge job on commit, and assert the job does not dispatch when the model is unchanged (isDirty()).
  • Version pinning: the package is at a 2.x score and last released 2025-09-21; pin a minor version and read the changelog before upgrading, since purge API wrappers tend to change method signatures between majors.
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.
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource