Repository navigation
Cache Warming
After a full purge every page is cold, and the first visitor to each one pays for the render. A warm run rebuilds pages in the background from your sitemaps, most recently changed first, and records for every URL whether the origin actually stored a page.
Warming exists to fill GT Performance's own page store, so it does not run in optimize-only mode, where the host's cache stores pages instead. No warm run is queued after a full purge, the stale-page refresh is skipped, and Start warm run and wp gt-performance cache warm refuse with a message that nothing was queued.
A run starts:
- Automatically after a full purge, when the page cache and Warm cache after a full purge are both on. The run is queued 30 seconds later, and a burst of full purges (a theme switch plus a menu save) within a minute queues one run, not several.
- From Tools > Cache warming > Start warm run.
- From WP-CLI:
wp gt-performance cache warm
wp gt-performance queue run # process it now instead of waiting for cron
wp gt-performance cache warm-statusTo rebuild a single page, use Warm this URL in the admin bar. See Purging.
Starting a run while another is unfinished marks the earlier one superseded. Its already-queued preloads still run, but their results no longer count against it.
Page Cache > Cache warming > Sitemap sources (cache.preload_sitemaps) takes up to 10 sitemap URLs from this site, one per line. When it is empty, the run uses:
- the WordPress core sitemap,
/wp-sitemap.xml - every
Sitemap:line in/robots.txt, which is where SEO plugins announce their own sitemaps
Only sources on the same scheme, host and port as the home URL are used, and at most 10 in total. The home page is always warmed first.
Discovery rules:
- Each discovery job fetches at most two sitemaps, so a large site is read across many short background jobs instead of one long request.
- Sitemap indexes are followed up to five levels deep.
- Redirects are not followed automatically. Each hop is recorded as its own target, must stay on this site, and counts as a level.
- A sitemap response is read up to 2 MB,
robots.txtup to 64 KB. A truncated sitemap is reported. - Entries on other domains are skipped and reported.
- URLs the cache would bypass anyway (a bypass path, an unknown query parameter, and so on) are never queued.
- A run holds at most 50,000 targets, sitemaps and variants included.
Progress lives in the gtperf_warm_targets table, so a worker that dies mid-run resumes where it stopped. Every target is identified by its normalized URL, so a sitemap that lists itself, or a cycle of indexes, is fetched once.
Each sitemap entry is ranked by its <lastmod>:
| Priority | Targets |
|---|---|
| First | The home page |
| Next | Entries modified in the last 7 days |
| Then | Everything else, including entries without a <lastmod>
|
With Separate cache for mobile HTML on, every URL gets a second target for the mobile variant, requested with a phone user agent.
Warming dispatches URLs per warming batch (cache.preload_max_urls, default 200, maximum 2,000) preload jobs at a time, then waits for all of them to report before queuing the next batch. It checks once a minute. After 30 checks without a result, the missing ones are marked failed with no_result and the run carries a lost_results warning instead of waiting forever. Cancelled or lost preload jobs are the usual cause.
Set the batch size to 0, or turn Warm cache after a full purge off, to stop rebuilding without turning caching off. A run that reaches dispatch in that state skips its remaining URLs as preload_disabled and ends partial.
Warm jobs run at the same priority as ordinary preloads, ahead of unused-CSS generation. Pausing optional queue work pauses warming too. See Tools.
A run never warms more pages than the cache is allowed to keep. Each batch is capped by the room left under cache.entry_budget (default 5,000 entries, 0 for no limit), counting every URL target this run has already queued or reported. When the budget is used up, the remaining URLs are skipped as capacity_limited and the run ends in the capacity_limited state. Because the home page and recently changed pages go first, those are the ones a budget smaller than the site keeps.
The same budget drives eviction in the hourly garbage collector. See Page Cache for how to change it.
A 200 response is not proof of a warm cache. Each preload is classified from the evidence:
| Result | Detail | Meaning |
|---|---|---|
origin_ready |
rebuilt |
The origin stored a fresh page during this request |
origin_ready |
existing |
A fresh origin entry was already there |
edge_observed |
cf_hit, cf_stale, cf_updating, cf_revalidated
|
Cloudflare answered from its cache and no fresh origin entry exists |
requested |
no_artifact, or gt_<status>
|
HTTP 200, but nothing was stored. The detail carries the X-GT-Cache value when there was one |
skipped |
a bypass reason, or invalid_url
|
The URL is not cacheable, so it was never requested |
skipped |
redirect_<code> (for example redirect_301), http_<code> for a 4xx (for example http_404) |
The page answered with a redirect or a client error. That is the page's answer, not a hiccup, so it is not retried |
failed |
http_<code> for a 5xx or any other non-200 status, request_error, no_result
|
A server error, a transport error, or no report |
Preload requests carry a signed X-GT-Preload token (an HMAC derived from AUTH_KEY, valid for ten minutes), do not follow redirects, time out after 15 seconds, and identify themselves as GT-Performance-Preloader/<version>. The token is what lets a preload rebuild a stale page; the drop-in ignores the header without a valid one. A failed preload job (a server error or a transport failure) is retried with backoff, up to three attempts in total. A redirect or a 4xx is recorded as skipped at once: a sitemap that still lists a moved post gets the same 301 on every retry.
| State | Meaning |
|---|---|
discovering |
Reading sitemaps |
warming |
Dispatching preload batches |
complete |
Every sitemap read and every URL reported |
partial |
Finished with a sitemap failure or one of the warnings below |
capacity_limited |
Stopped at the cache size budget |
superseded |
Replaced by a newer run |
Warnings recorded on a run: sitemap_failed, sitemap_truncated, sitemap_skipped (a redirect left the site or went too deep), depth_limit, target_cap, foreign_entries, lost_results, preload_disabled. Any of sitemap_truncated, depth_limit, target_cap, sitemap_skipped or lost_results, or a failed sitemap, makes the run partial.
Tools > Cache warming shows the latest run: state, origin ready, edge only, waiting, failed, plus how many URLs answered without a stored page, how many were skipped, how many sitemaps were read or failed, when it last changed, and its warnings.
wp gt-performance cache warm-status prints the state, the sources (as paths), the warnings and every target count by kind and status.
The health report (Tools, Site Health, wp gt-performance health) includes the latest run and warns when it is partial, capacity_limited, has failed URLs, or has made no progress for 30 minutes while still running. See Diagnostics.
Target rows are kept for 7 days and the last 10 run summaries are kept in the gt_performance_warm_runs option.
| Label | Key | Default |
|---|---|---|
| Warm cache after a full purge | cache.preload |
On |
| URLs per warming batch | cache.preload_max_urls |
200 (0 to 2,000) |
| Sitemap sources | cache.preload_sitemaps |
Empty (WordPress sitemap plus robots.txt) |
| Cache size budget (no admin field) | cache.entry_budget |
5,000 |
Warm cache after a full purge also gates the stale-revalidation sweep described on Page Cache. Preloads queued after a publish or update run regardless of it.
AI assistants with operate permission can queue preloads for up to 20 URLs through the gt-performance/preload-urls ability. See AI and MCP.
Page Cache · Purging · Tools · WP-CLI · Settings Reference
GT Performance on WordPress.org · Changelog · Roadmap · GPL-2.0-or-later · Issues and pull requests welcome
Getting started
Caching
Optimization
Delivery
Operations
Reference
Links