OCMLABS/MODULE-CLOUDFLARE · ARCHITECTURE

How the Cloudflare module is built - edge header plugins, purge queue, health runner, admin surface and cron

  • Magento
  • Adobe Commerce
  • Hyvä

Module identity

OCMLabs_Cloudflare, namespace OCMLabs\Cloudflare, Composer package ocmlabs/module-cloudflare, licence OSL-3.0. PHP 8.1 or later with declare(strict_types=1) on every file. Depends on magento/framework 103.0.x (Magento 2.4.4 and later), the page cache, store, config, catalog, CMS, URL rewrite, message queue and cron modules, and Guzzle 7. All Cloudflare calls go through one API client; all configuration reads go through one Config model.

Edge headers

Three frontend plugins decide what Cloudflare may cache:

  1. Kernel marker (before plugin on Magento\Framework\App\PageCache\Kernel::process). The built-in full page cache strips headers and saves the remaining getHeaders()->toArray() with the page. The plugin runs first and adds two markers, an internal edge TTL header (X-OCMLabs-Edge-TTL) and Cache-Tag, so they are stored with the page and survive on cache hits.
  2. Result marker (after plugin on ResultInterface::renderResult). The Varnish-mode equivalent: in Varnish mode the kernel is not involved, so the markers are added when the result renders.
  3. Response send (plugin on Magento\Framework\App\Response\Http::sendResponse). The header policy runs last. In built-in mode it turns the marker into Cache-Control: public, max-age=0, s-maxage=<ttl> plus CDN-Cache-Control, strips the session cookies (Laminas and native header() cookies both), re-checks the bypass matcher, and sends private, no-store on everything else, including responses that set X-Magento-Vary (unless vary handling is cache_key, where a re-asserted default value is allowed). In Varnish mode it leaves headers and cookies to Varnish and only keeps the marker and Cache-Tag for the VCL.

With the module disabled, or edge caching off, the policy sends private, no-store on every storefront response.

Purge pipeline

  1. Observers. clean_cache_by_tags and clean_cache_after_reindex feed the tag observer; adminhtml_cache_flush_all and adminhtml_cache_flush_system feed the flush observer. A plugin on TypeListInterface catches a refresh of the full_page cache type, which does not fire an event. All observers are global, because bin/magento cache:clean dispatches the adminhtml events outside the adminhtml area.
  2. Dispatcher. Normalises the request (tags lowercased, store and website tags dropped), resolves the websites and zones involved, and either publishes to the queue (async) or runs the strategy immediately (sync, CLI and dashboard quick purge).
  3. Queue. Topic ocmlabs.cloudflare.purge on the db connection, consumer ocmlabsCloudflarePurge using Magento’s BatchConsumer with a custom merger that folds every waiting message into one merged message grouped by zone.
  4. Consumer. Per group, in a try/catch so one zone’s failure does not stop another: chunk to 100 items, take one rate limiter token per chunk, send, log. On an empty bucket or a 429 it blocks the bucket for Retry-After, re-queues the remainder and returns. More than 20 waiting messages for a zone collapse into a purge-everything.
  5. Strategies. tag sends raw tags; url resolves tags to URLs through the URL finder (store-scoped, configurable parents, paginated categories, 500 per tag, 5000 aggregate); prefix and everything map directly to the Cloudflare endpoints.
  6. Rate limiter. A token bucket per Cloudflare account stored in the module cache type, sized by plan: Free 5 per minute (burst 25), Pro 5 per second (25), Business 10 per second (50), Enterprise 50 per second (500).

API client

One Guzzle-based client for https://api.cloudflare.com/client/v4/ with retries on transient errors, 100-item chunking, typed exceptions (authentication, permission, rate limit, API error) and token redaction in every logged or stored message. Debug logging writes request and response summaries to var/log/ocmlabs_cloudflare.log.

Cache rules are managed through the Rulesets API (http_request_cache_settings phase): the module reads the zone’s entrypoint ruleset, computes a diff against the rules it would write for the website, and replaces only the [OCMLabs] ... (<website code>) rules, keeping merchant rules in their positions. Analytics use the GraphQL Analytics API with httpRequests1mGroups, httpRequests1dGroups and httpRequestsAdaptiveGroups datasets.

Health runner

Twenty-five checks implement one interface (code, title, group, run) and are registered in di.xml as an ordered pool, so a custom check can be added with a di.xml entry. The runner executes them for a website, catches exceptions per check so one API failure does not blank the page, and stores the results in the module cache type with a timestamp. Live probes go through a prober with an SSRF guard that only fetches URLs resolving to the website’s configured hosts, and require an HTTP 200.

Real IP

A plugin on Magento\Framework\HTTP\PhpEnvironment\RemoteAddress. It returns CF-Connecting-IP only when the raw REMOTE_ADDR is inside Cloudflare’s ranges (IPv4 and IPv6, including IPv4-mapped IPv6). Ranges come from the module cache type, refreshed daily, with a bundled copy as the fallback.

Database

One table, ocmlabs_cloudflare_purge_log: zone ID, website ID, mode, item count, items (JSON, capped at 64 KB), trigger, status (success, failed, queued), Cloudflare request ID, error, duration in milliseconds, created at. Indexed on created_at and status.

Cache type

ocmlabs_cloudflare (“OCM Labs Cloudflare” under System > Cache Management) holds zone details, analytics responses, health results, the rate limiter buckets and the Cloudflare IP ranges. Flushing it is safe; everything is refetched.

Cron jobs

JobSchedulePurpose
ocmlabs_cloudflare_health15 minutes past every hourRun the health checks for every website with a zone
ocmlabs_cloudflare_analytics_warmevery 15 minutesWarm the analytics cache for each website’s default range
ocmlabs_cloudflare_ip_ranges04:30 dailyRefresh Cloudflare’s published IP ranges
ocmlabs_cloudflare_purge_log_rotate03:00 dailyDelete purge log rows older than 30 days

All module cron work is skipped while Enable Cloudflare Integration is off.

Admin surface

Route frontName ocmlabs_cloudflare, menu System > Cloudflare with Dashboard and Purge Log. The dashboard is one page with Overview, Analytics and Health tabs backed by AJAX controllers; the purge log is a UI component grid with a detail view. Config section ocmlabs_cloudflare with Test Connection and Detect Zone buttons. ACL: OCMLabs_Cloudflare::dashboard with children ::purge, ::settings_write and ::purge_log, plus ::config under Stores > Configuration. The admin footer shows the module version with links to the docs, the issue tracker and the OCM Labs catalogue; the module makes no other outbound requests than Cloudflare’s API and your own storefront.

Console commands

Six commands under ocmlabs:cloudflare: (purge, status, dev-mode, health, rules:apply, vcl:export). The command list is built on every bin/magento call, so the heavy services are injected as proxies. See the CLI Reference.

Tests

409 PHPUnit unit tests run against a plain Magento checkout in CI (PHP 8.1 and 8.2 lint with phpcs on the Magento coding standard, unit tests on Magento 2.4.8).

See also

In this module