Hooks and Filters
Every action and filter the plugin fires. All are prefixed gt_performance_.
Two things to know before you hook anything:
- Nothing here runs on a cache hit. When
advanced-cache.phpserves a stored page, WordPress has not loaded, so no hook fires. The HTML filters run when a page is rendered and about to be stored. - The plugin boots on
plugins_loadedat priority 1. Add your callbacks from a plugin or must-use plugin. A theme’sfunctions.phploads later, which is fine for the filters below but too late forgt_performance_loaded.
HTML pipeline
gt_performance_html
apply_filters( 'gt_performance_html', string $html, \GTPerformance\Cache\RequestContext $request ): string
The full response body of a cacheable page, after it passes response validation and before it is stored. Every built-in optimizer hooks here: the optimization pipeline at priority 10, CDN URL rewriting at 60. Return a non-empty string; anything else is discarded and the original body is stored.
It also runs, without storing anything, for an administrator’s CSS preview, for the unused-CSS generator’s own background request, and in optimize-only mode for every response that passes validation, on its way to the host’s cache. It does not run for a post with Don’t cache this page ticked.
$request is read-only. It carries method, scheme, host, path, query, cookies, headers, userAgent and arrayQuery (the names of parameters sent as arrays, which the request is bypassed for), with control characters stripped and lengths bounded.
gt_performance_optimize_stage
apply_filters( 'gt_performance_optimize_stage', bool $enabled, string $stage ): bool
Asked once per stage, per page. $stage is css, javascript, media, images, fonts or embeds, in that order. Return false to skip a stage. Returning false for css also stops background CSS regeneration. Safe mode skips every stage without asking.
add_filter( 'gt_performance_optimize_stage', function ( bool $enabled, string $stage ) {
return 'javascript' === $stage && is_page( 'calculator' ) ? false : $enabled;
}, 10, 2 );
The built-in Perfmatters integration uses this filter to hand front-end optimization to Perfmatters when you choose that in Integrations.
gt_performance_optimized_html
apply_filters( 'gt_performance_optimized_html', string $html ): string
The body after all six stages, still inside the priority-10 callback of gt_performance_html, so CDN rewriting has not run yet.
Cache policy
gt_performance_cache_policy
apply_filters( 'gt_performance_cache_policy', array $policy ): array
The cache section of the settings (mode, bypass_paths, bypass_cookies, bypass_query_params, vary_query_params, ignored_query_params, the lifetimes and the rest). Some callers add generation and hosts before filtering. WordPress-side code reads it wherever it decides eligibility: capturing a page, cache explain, preloading, warm runs, CSS generation, speculative-loading exclusions, and compiling the managed Cloudflare rule on sync.
It is not what the drop-in reads. advanced-cache.php reads the compiled runtime configuration, which goes through gt_performance_compiled_config. A bypass rule that must hold at the origin, in the drop-in and at the edge needs both filters, which is how the built-in commerce and Jetpack protections do it:
function my_bypass( array $cache ): array {
$cache['bypass_paths'][] = '/members/';
return $cache;
}
add_filter( 'gt_performance_cache_policy', 'my_bypass' );
add_filter( 'gt_performance_compiled_config', function ( array $config ) {
$config['cache'] = my_bypass( (array) $config['cache'] );
return $config;
} );
Then save the settings once so the runtime file is rewritten, and run Connect/sync Cloudflare if Cloudflare is connected. For most sites the Exceptions tab is simpler. See Cache Exclusions.
gt_performance_compiled_config
apply_filters( 'gt_performance_compiled_config', array $config, array $settings ): array
The runtime configuration the drop-in reads on every request. $config has generation, cache (the cache settings plus hosts), debug and plugin_dir. $settings is the full sanitized settings array. In optimize-only mode cache.enabled is compiled as false, so an installed drop-in never serves.
It runs only when the configuration is compiled: when settings are saved, when the page-cache or Redis drop-in is installed or republished after a plugin update, on activation, when any plugin is activated or deactivated, and when the set of active commerce protections changes. The result is written to wp-content/cache/gt-performance/config.json as authenticated encrypted JSON.
gt_performance_canonical_hosts
apply_filters( 'gt_performance_canonical_hosts', array $hosts ): array
Hostnames this install answers to. Defaults to the hosts of home_url() and site_url(). Add a staging alias or a second mapped domain here. A request for any other host is not cached, and the list is also used to accept sitemap sources and same-site URLs in the queue, to check --page-url in WP-CLI, and as the hostnames a full Cloudflare purge clears. Values are lowercased; do not include a port. Because the list is compiled into the runtime file, save the settings after changing it.
gt_performance_cache_stored
do_action( 'gt_performance_cache_stored', \GTPerformance\Cache\RequestContext $request, string $hash )
After a page is written to the origin cache. The plugin records the page’s content dependencies here for dependency-aware purging.
Purging and the edge
gt_performance_purged_urls
do_action( 'gt_performance_purged_urls', array $urls, string $scope, array $primary )
After origin entries for these URLs were deleted. $scope is origin. $urls includes the full URLs of any recorded query variants of the purged pages. $primary (1.3.6 and later) holds the URLs the change names directly, such as a post’s permalink, home and archives, with their variants. It equals $urls unless a post change marked them. The Cloudflare integration collects the URLs and sends one purge at shutdown; the xCloud integration purges its host cache once per request; the xCloud Enterprise purge sends the batch to xCloud’s purge plugin, or only $primary when the batch is over 300 URLs. The first time either purge action fires in a request, the plugin loads the Cloudflare, xCloud and Redis modules if the request had not loaded them, so the edge hears about purges from anonymous requests too.
gt_performance_xcloud_enterprise_purge
apply_filters( 'gt_performance_xcloud_enterprise_purge', bool $enabled ): bool
Whether GT Performance purges xCloud Cloudflare Enterprise through xCloud’s purge plugin when that plugin is installed. Default true. Return false to leave Enterprise purging to xCloud’s plugin alone. With false, direct Cloudflare is no longer paused. Since 1.3.6. See xCloud.
gt_performance_purge_paginated_listings
apply_filters( 'gt_performance_purge_paginated_listings', bool $purge ): bool
Whether a post change also purges page two and later of the listings it appears in. Default false: only page one is purged and later pages refresh when their copies expire. Since 1.3.6. See Purging.
gt_performance_purged_all
do_action( 'gt_performance_purged_all', string $scope )
After the whole origin cache was deleted. $scope is origin. Cloudflare purges this site’s canonical hostnames (not the whole zone), xCloud purges its host cache, and the queue schedules a warm run when warming is on and the site is in store mode.
gt_performance_flush_edge_purges
apply_filters( 'gt_performance_flush_edge_purges', bool|\WP_Error $result ): bool|\WP_Error
Asked when a caller needs deferred edge purges finished before it reports success: the admin and admin-bar purge actions, wp gt-performance cache purge, purge verification, and assistant purge operations. The Cloudflare integration sends its pending purge here. Return a WP_Error to report an edge failure; keep an error you receive. Ordinary post-save purges are not flushed early.
See Purging.
Queue
gt_performance_enqueue_purge
do_action( 'gt_performance_enqueue_purge', array $urls )
Queues one purge_url job per URL. Purge jobs keep running while optional queue work is paused. The commerce integrations use it for the listings a price or stock change reaches through recorded dependencies; the product itself and its store pages are purged within the request.
gt_performance_enqueue_preload
do_action( 'gt_performance_enqueue_preload', array $urls )
Queues one preload_url job per URL, public variant only. Jobs for URLs outside the canonical hosts fail rather than make the request.
do_action( 'gt_performance_enqueue_purge', array( home_url( '/pricing/' ) ) );
do_action( 'gt_performance_enqueue_preload', array( home_url( '/pricing/' ) ) );
gt_performance_revalidate_batch
apply_filters( 'gt_performance_revalidate_batch', int $batch ): int
How many stale pages each queue run may queue for rebuild. Default 5, clamped to 0 to 200. Return 0 to stop background revalidation of stale entries. The sweep runs only when the origin cache and warming are both on.
gt_performance_enqueue_css, gt_performance_enqueue_font_localization, gt_performance_enqueue_image_variants
do_action( 'gt_performance_enqueue_css', string $url )
do_action( 'gt_performance_enqueue_font_localization', string $href )
do_action( 'gt_performance_enqueue_image_variants', int $attachmentId, string $variantKey )
The optimizers hand background work to the queue through these: unused-CSS generation for a same-site URL, copying a Google Fonts stylesheet and its files, and generating a WebP or AVIF variant for one image size (full or a registered size name).
gt_performance_job_{type}
do_action( "gt_performance_job_{$type}", array $payload )
How the queue runs job types it does not handle itself. The built-in types that go through it are generate_css, regenerate_css_batch, localize_fonts, generate_image_variants, database_cleanup and agent_operation. Throw an exception to fail the job; it is retried up to three attempts in total. preload_url, purge_url and the warm-run steps are handled internally and never fire this hook. Only plugin code creates jobs, so this is not a way to add your own job types.
CSS
gt_performance_css_safelist
apply_filters( 'gt_performance_css_safelist', array $safelist, string $url ): array
Selector patterns that survive pruning on the page at $url. A plain string is a partial match; a delimited regular expression such as /^\.modal(?:--|\b)/i is matched as a pattern. Starts from the Selector safelist setting plus the fetched important classes.
add_filter( 'gt_performance_css_safelist', function ( array $safelist ) {
$safelist[] = '.js-only-modal';
return $safelist;
} );
gt_performance_css_stylesheet_exclusions
apply_filters( 'gt_performance_css_stylesheet_exclusions', array $exclusions, string $url ): array
Fragments that leave a stylesheet untouched and loaded normally. Matched against external stylesheet URLs and inline <style> IDs. Starts from the Excluded stylesheets setting. The built-in page-builder and commerce protections add to it.
gt_performance_css_allowed_hosts
apply_filters( 'gt_performance_css_allowed_hosts', array $hosts, string $siteHost ): array
Hosts the unused-CSS engine may read stylesheets from. Defaults to the site host plus the CDN host when a CDN URL is set. Stylesheets on other hosts are left as they are.
See CSS Optimization.
JavaScript
gt_performance_javascript_exclusions
apply_filters( 'gt_performance_javascript_exclusions', array $fragments ): array
Substrings matched against script URLs. A matching script is not minified, deferred or delayed. Starts from the Never optimize scripts setting. Cart, checkout and payment scripts are protected separately and cannot be removed from protection here.
add_filter( 'gt_performance_javascript_exclusions', function ( array $fragments ) {
$fragments[] = '/plugins/my-checkout/';
return $fragments;
} );
Media
gt_performance_media_lazy_load, gt_performance_media_add_dimensions
apply_filters( 'gt_performance_media_lazy_load', bool $enabled ): bool
apply_filters( 'gt_performance_media_add_dimensions', bool $enabled ): bool
Whether lazy-loading, and adding missing width and height, run on the page being optimized. Each starts from its setting and applies to the whole page, not to one image. The built-in EWWW integration turns lazy-loading off here when EWWW already lazy-loads.
gt_performance_generate_image_variants
apply_filters( 'gt_performance_generate_image_variants', bool $generate, int $attachmentId, array $metadata ): bool
Whether to queue and generate WebP or AVIF variants for an attachment. Asked when the attachment metadata is generated and again when the queued job runs.
gt_performance_rewrite_image_variants
apply_filters( 'gt_performance_rewrite_image_variants', bool $rewrite ): bool
Whether image URLs on the page being optimized are rewritten to generated variants. The built-in EWWW integration returns false for both variant filters when EWWW delivers next-generation formats itself.
Compatibility
gt_performance_core_forms_poll_detected
apply_filters( 'gt_performance_core_forms_poll_detected', bool $found, array $posts ): bool
When the origin cache is on, the plugin strips Core Forms’ cf_poll_voter cookie from anonymous GET responses that contain no poll, so they stay cacheable. $found says whether a core-forms/poll block or [cf_poll] shortcode was found in $posts (the queried WP_Post objects). Return true to keep the cookie on a page whose poll is rendered some other way.
Admin screen
These let a separate package add its own tab to GT Performance without changing shared code. They run only in wp-admin.
| Hook | Type | Arguments | Use |
|---|---|---|---|
gt_performance_admin_tabs | filter | list<string> $slugs | Add a tab slug. Built-in tabs are always kept |
gt_performance_admin_tab_labels | filter | array<string,string> $labels | Label for your slug. Labels for unknown slugs are dropped |
gt_performance_render_tab_{slug} | action | array $settings | Echo your tab’s content |
gt_performance_admin_notices | filter | array<string, array{0:string,1:string}> $notices | Message and type (success, info, warning or error) for a notice code passed in gtperf_notice |
Lifecycle
gt_performance_loaded
do_action( 'gt_performance_loaded', \GTPerformance\Core\Plugin $plugin )
Fires on plugins_loaded priority 1, after the modules for this request have registered their hooks. Which modules exist depends on the request: the Cloudflare, xCloud and Redis modules load for admin, AJAX, cron, WP-CLI, REST and signed-in requests, not for anonymous page views. They are loaded later in any request that fires a purge.
Scheduled events
| Hook | Schedule | What it does |
|---|---|---|
gt_performance_run_queue | gtperf_every_minute | Runs up to 25 queue jobs within a 20-second budget, queues stale pages for rebuild, and prunes old job, warm-run, dependency and operation records |
gt_performance_collect_garbage | hourly | Examines up to 4,000 page entries: deletes those from older settings generations and those past their stale window, then evicts the oldest above the cache entry budget. Also removes files in the cache’s assets directory not read or written for 14 days |
gt_performance_database_cleanup | daily, gtperf_weekly or gtperf_monthly | Starts a background cleanup of the saved tasks, keeping the configured number of revisions. Scheduled only while scheduled cleanup is on; the first run is one hour after it is scheduled |
gt_performance_cloudflare_retry | Single event | Retries a Cloudflare purge that failed with a transport error, HTTP 429 or a 5xx, up to three retries with increasing delays that honor Retry-After |
Custom schedules registered by the plugin: gtperf_every_minute (60 seconds), gtperf_weekly (7 days), gtperf_monthly (30 days).
If the queue or garbage-collection event goes missing, the plugin schedules it again on the next init.