Pre-warms Cloudflare image transform caches,
Crawls any site for /cdn-cgi/image/ variants and heats AVIF + WebP at the edge before visitors arrive.
Cloudflare generates each /cdn-cgi/image/... variant on first request (cold), so whichever real visitor hits it first pays the generation latency. srcset makes this worse: a browser only ever fetches the ONE variant matching its viewport, so simply loading pages never warms them all. This tool crawls the deployed HTML/CSS for every declared variant and requests each in both AVIF and WebP ahead of time, moving the cold cost off the visitor path.
Billing note: Cloudflare counts a unique transformation once per billing period; cache hits after that are free, so re-warming an already-counted variant costs nothing.
Requires Go 1.21+. Produces a fully static binary with zero runtime dependencies (no Go needed on the target host):
CGO_ENABLED=0 go build -o cache-warmer ../cache-warmer # use defaults below
DOMAIN=https://example.com WEBROOT=/var/www/html ./cache-warmer| Env var | Default | Purpose |
|---|---|---|
DOMAIN |
https://example.com |
Public origin to warm through |
WEBROOT |
/var/www/html |
Deployed site directory to crawl |
UA |
CF-Transforms-Cache-Warmer/1.0 |
User-Agent (easy to allowlist in CF WAF) |
MAX_TIME |
20 |
Per-request timeout (seconds) |
Exit code is non-zero if anything failed, so cron mail/logs flag it. Output includes the CF-Cache-Status of every request (HIT / MISS / EXPIRED / REVALIDATED) and a summary.
install.sh installs the binary to /usr/local/bin and registers a cron entry. Every config value resolves in this order: environment variable (used silently) → interactive prompt → built-in default.
sudo ./install.sh # interactive
sudo DOMAIN=https://example.com WEBROOT=/var/www/html ./install.sh # non-interactive
./install.sh --dry-run # print the plan, change nothingDefault schedule: 6am on the 1st of each month, logging to /var/log/warm-cache.log. The entry carries its config inline and ends with a # cache-warmer marker, so re-running install.sh replaces the old entry instead of duplicating it.
- Walks
WEBROOTfor*.html/*.cssfiles and extracts every unique/cdn-cgi/image/...path (same regex asgrep -rhoE). - For each path, issues two requests against
DOMAIN: one with an AVIF-preferringAcceptheader, one WebP-only — becauseformat=autokeys the Cloudflare cache on that header. - Reports status code,
CF-Cache-Status, and elapsed time per request; exits non-zero on any failure.