Purge CDN and Proxy Caches in PagibleAI

A CDN or reverse proxy in front of PagibleAI serves cached pages without hitting your Laravel application. The aimeos/pagible-cdn package keeps those caches fresh: when a page is published, moved or restricted, or a public file is removed, it purges the affected URLs from Cloudflare, Fastly or Varnish using FOSHttpCache.

Drafts and editor previews never trigger a purge. Content changes reach visitors when they are published, so the CDN can cache public pages much longer than the page's own cache time.

When to use it

Install the package if a shared cache stores your HTML pages, for example:

  • Cloudflare or Fastly in front of the site
  • one or more Varnish servers as reverse proxy
  • Varnish behind a CDN (stacked caches)

Page URLs are generated from the cms.page route of the theme package (aimeos/pagible-theme). In headless setups without the theme package, only file URLs are purged.

Install the package

composer require aimeos/pagible-cdn
php artisan vendor:publish --provider="Aimeos\Cms\CdnServiceProvider"

The second command copies the configuration to config/cms/cdn.php. Purge requests are sent by queued jobs on the CMS queue, configured by CMS_QUEUE_CONNECTION and CMS_QUEUE, so a queue worker must run:

php artisan queue:work

If the CMS queue uses its own connection or queue name, pass them to the worker, e.g. php artisan queue:work redis --queue=cms. Clear the configuration cache and restart workers after changing environment values.

Configure the CDN clients

Each client is enabled as soon as its token or servers are set; all others are skipped. You can enable several clients at once, and every change is purged from all of them.

Cloudflare

Create an API token with the Zone.Cache Purge permission for the zone and copy the zone ID from the zone overview:

CMS_CDN_CLOUDFLARE_TOKEN="..."
CMS_CDN_CLOUDFLARE_ZONE="..."

Fastly

Set an API token allowed to purge the service and the service ID. CMS_CDN_FASTLY_SOFT defaults to true, which marks content as stale instead of removing it:

CMS_CDN_FASTLY_TOKEN="..."
CMS_CDN_FASTLY_SERVICE="..."
CMS_CDN_FASTLY_SOFT=true

Varnish

List the Varnish servers, comma separated. PagibleAI sends PURGE requests to each of them:

CMS_CDN_VARNISH_SERVERS="10.0.0.1:6081,10.0.0.2:6081"

Varnish must accept PURGE requests from your application servers. Add the purge handling and an ACL to your VCL as described in the FOSHttpCache proxy configuration.

General settings

KeyEnvironment variableDefaultDescription
urlCMS_CDN_URLAPP_URLScheme and host the CDN serves pages and files from
delayCMS_CDN_DELAY0Seconds to wait before purging, e.g. until database replicas are up to date
limitCMS_CDN_LIMIT500Above this number of URLs, Cloudflare and Fastly remove all content with one request; 0 disables it
maxageCMS_CDN_MAXAGEpage settingSeconds the CDN caches public pages
staleCMS_CDN_STALE0Seconds the CDN serves outdated pages while refreshing or if your server fails
timeoutCMS_CDN_TIMEOUT10Seconds to wait for the CDN API or proxy response

Set the CDN cache lifetime

Public pages are sent with Cache-Control: public, s-maxage=... using the cache time of each page. Pages with access rules and pages viewed by editors are private and never cached by the CDN.

Because changed pages are purged, the CDN can keep pages longer than the page cache time. CMS_CDN_MAXAGE replaces s-maxage and removes the Expires header so the CDN can't fall back to the shorter page lifetime. CMS_CDN_STALE adds stale-while-revalidate and stale-if-error:

CMS_CDN_MAXAGE=86400
CMS_CDN_STALE=60

What gets purged

  • Pages: URLs of pages whose published content, route, access rules or files changed, i.e. the same pages the theme's page cache invalidates. A moved page is purged at its old and new URL.
  • Files: public URLs of files and previews deleted from storage or moved to the private disk. New uploads always get new file names, so they never serve outdated content.
  • Shared content: all pages using a shared element or file that is published, deleted or restored. These pages are found by queued jobs on the CMS queue.

Purging is URL-based; cache tags or surrogate keys are not used. URLs are built from the cms.page route and CMS_CDN_URL or APP_URL, or from the page domain if CMS_MULTIDOMAIN is enabled.

Not purged are:

  • URLs with query strings, e.g. search or pagination parameters. Configure the CDN to ignore query strings for pages that don't use them, or keep their cache lifetime short.
  • Sitemaps, which update when their CDN cache expires.
  • Other pages that show the changed page, e.g. in the navigation. They update when their CDN cache expires, so keep the lifetime of HTML pages reasonable.

Users with the cache:clear permission can also purge a page and all its subpages with the Clear cache action in the admin panel. The default admin, publisher and editor roles include it. For the root page, this purges the whole site.

Delay, batching and limits

Each client gets its own jobs with up to 250 URLs per job, so a failing CDN doesn't delay the others. Identical purges that are still queued aren't queued again, so a large change of shared content removes all content only once.

If more URLs than limit are purged at once, Cloudflare and Fastly remove all content with a single request instead. This also removes cached images, CSS and JavaScript, which are then fetched from your server again. If shared elements like the footer are used by more pages than the limit, every change does this, so raise the limit or set it to 0 for large sites with frequent shared-content changes. Varnish always purges URL by URL.

Failed purges are retried after 10 seconds, 1, 5 and 15 minutes, including when the CDN API rate-limits requests. After the last retry, Laravel stores them as failed jobs. Jobs contain only the client name and URLs, and credentials are read when the job runs, so changed credentials apply to queued purges too.

With the sync queue, URLs are purged after the response is sent, without delay or retries. Use a real queue in production.

Purge from the command line

php artisan cms:cdn:purge https://example.com/a /b # purge URLs, paths are relative to CMS_CDN_URL or APP_URL
php artisan cms:cdn:purge --all                    # remove all content, e.g. after theme changes
php artisan cms:cdn:purge --all --client=fastly    # limit to one or more clients

The command purges directly without the queue and prints the result per client, so it's also a quick way to test credentials. Removing all content is only supported by Cloudflare and Fastly. Clients are purged in the configured order.

Stacked caches

If a CDN caches the responses of Varnish, the inner cache must be purged first. Otherwise, the CDN fetches the outdated page from Varnish again. Give the CDN client a longer delay, e.g. 'delay' => 10, and list the inner cache first in clients so cms:cdn:purge handles it first. If the inner purge fails, the CDN may cache the outdated page until its lifetime expires.

Multiple domains and zones

Add one client per zone or service in config/cms/cdn.php and limit it to its host names with hosts, e.g. one Cloudflare zone per domain in a multi-domain setup:

'clients' => [
    'shop' => [
        'driver' => 'cloudflare',
        'token' => env( 'CMS_CDN_SHOP_TOKEN' ),
        'zone' => env( 'CMS_CDN_SHOP_ZONE' ),
        'hosts' => ['shop.example.com'],
    ],
    'blog' => [
        'driver' => 'cloudflare',
        'token' => env( 'CMS_CDN_BLOG_TOKEN' ),
        'zone' => env( 'CMS_CDN_BLOG_ZONE' ),
        'hosts' => ['blog.example.com'],
    ],
],

Each client can also set its own delay. If files are served from another host, e.g. cdn.example.com, add that host to hosts as well, otherwise removed files are not purged.

Protected content

Pages that get access rules and files moved to the private disk are only removed from the CDN by their purge. Until it succeeds, the CDN still serves the public copy:

  • during the delay of the client
  • until maxage expires if the purge fails after all retries or with the sync queue, plus stale seconds while your server fails
  • if the client's hosts don't contain the host of the file URLs

For sites with protected content, use a real queue, keep maxage moderate and add the public file host to hosts.

Troubleshoot CDN purging

  • Nothing is purged: check that a queue worker processes the CMS queue (CMS_QUEUE_CONNECTION, CMS_QUEUE) and that the client's token or servers are set. Run php artisan config:clear and restart workers after changing values.
  • Cloudflare returns an authentication error: the API token needs the Zone.Cache Purge permission for the zone in CMS_CDN_CLOUDFLARE_ZONE.
  • Fastly rejects the purge: check that the token may purge the service in CMS_CDN_FASTLY_SERVICE.
  • Varnish rejects PURGE requests: the VCL doesn't handle PURGE or the application server isn't allowed by its purge ACL.
  • Pages are purged but stay outdated: check that CMS_CDN_URL matches the scheme and host the CDN serves, and with stacked caches that the inner cache is purged first.
  • Page URLs are never purged: the theme package isn't installed, so there is no cms.page route.
  • --all fails for Varnish: removing all content is only supported by Cloudflare and Fastly.
  • Purges keep failing: run php artisan cms:cdn:purge / to see the error per client and inspect failed jobs with php artisan queue:failed.