Skip to content
Gaurav TiwariDocs

Exclude Pages, Cookies and Parameters

Version 1.3.6 Updated October 26, 2026

Everything that keeps a page out of the cache, and how to add your own. Configured under Exceptions > Cache exceptions, one entry per line (commas also work).

The five lists

LabelSettingMatchingUse for
Paths that must stay dynamiccache.bypass_pathsSegment boundariesWhole sections: /account/, /members/
Cookie prefixes that bypass cachecache.bypass_cookiesPrefix of the cookie nameAny visitor with state
Never cache query parameterscache.bypass_query_paramsExact parameter nameActions: add-to-cart, preview
Cache each value separatelycache.vary_query_paramsExact parameter nameParameters whose every value is its own page for everyone: orderby, a product filter, lang
Parameters that do not change contentcache.ignored_query_paramsExact parameter nameTracking noise you want stripped while the page stays cached

The defaults are listed on Page Cache. Core WordPress paths, the login cookie and the preview parameters are already in them.

Path matching

Matching is on segment boundaries, so /checkout/ protects /checkout, /checkout/, and everything below it, but not /checkout-summary. That is the reason a naive prefix match is wrong: /cart should not accidentally bypass /cartography. Paths are compared case-sensitively and without the query string.

A bypass path of / protects the site root only, not the whole site.

The unknown_query rule

Any query parameter that is not on the ignored list or the cache-separately list causes a bypass. This is the behavior that surprises people most, and it is deliberate.

  • Nobody can fill your disk by requesting ?a=1, ?a=2, ?a=3.
  • A parameter your site genuinely uses can never be silently dropped into a wrong cache hit.

The cost is that a legitimate parameter you forgot to declare bypasses instead of caching. That is the right direction to fail in.

To cache across a parameter, add it to Parameters that do not change content: the page is cached once and the parameter is stripped from the key. Only list parameters that never change the page. Ignoring one that changes price, language or content serves the wrong HTML.

When each value really is a different page, and the same for every visitor who sends it, add the parameter to Cache each value separately instead: every value gets its own stored copy. Values over 100 characters are not cached, and a page keeps at most 100 variants; beyond that, new values are served uncached. Purging the page removes all of its variants. See Page Cache.

To name a parameter that must always bypass, add it to Never cache query parameters. Parameter names match case-insensitively.

A parameter sent as an array (?name[]=value) always bypasses the cache (query_array:<name>) and is sent no-store, whichever list it is on.

Cookies

Prefix match on the cookie name, so wordpress_logged_in_ covers every hashed variant. Enter names or stable prefixes, never values. This is why a signed-in administrator never sees a cache hit, and why testing must be done logged out.

The managed Cloudflare rule excludes a request when the cookie text appears anywhere in its Cookie header, which is at least as broad as the origin’s prefix match. See Cloudflare.

What is excluded automatically

  • Everything the active commerce adapters declare. See Commerce Safety.
  • Jetpack’s visitor-state cookies, while Jetpack is active and Jetpack compatibility is on. See Integrations.
  • Requests with an Authorization header, requests that are not GET or HEAD, and requests for a host this site does not answer to.
  • Admin, AJAX and cron requests, feeds and robots.txt.
  • Any response that sets a cookie, is not HTTP 200, is not text/html, or carries no-store or private in Cache-Control.
  • Any response rendered while DONOTCACHEPAGE is true.
  • Any post with Don’t cache this page ticked in the editor’s GT Performance box (reason page-option). See Page Cache.

The lists also reach beyond the page cache. Bypass paths are removed from WordPress speculative loading (WordPress 6.8+), so the browser never prefetches a cart or account page. A front-end request that matches a bypass path, cookie or query rule from these lists or a commerce adapter, or that sends a parameter as an array, is sent with no-store headers and X-GT-Commerce-Cache: BYPASS, even while the origin page cache is off or in optimize-only mode.

Per-response exclusion from code

// Anywhere before the response is captured
if ( my_page_is_personalised() ) {
    define( 'DONOTCACHEPAGE', true );
}

The page renders normally and is not stored. Setting a cookie in the response has the same effect.

Adding rules from code

Two filters carry the cache policy, and a rule added in code needs both:

  • gt_performance_cache_policy is read while WordPress runs: the capture decision, cache explain, warming, speculative-loading exclusions, and the Cloudflare rule when it is synced.
  • gt_performance_compiled_config shapes the encrypted config the drop-in reads before WordPress loads. Its cache key is the same policy.

The commerce adapters hook both. Do the same:

function my_site_cache_rules( array $policy ): array {
    $policy['bypass_paths'][]   = '/members/';
    $policy['bypass_cookies'][] = 'my_session_';
    return $policy;
}
add_filter( 'gt_performance_cache_policy', 'my_site_cache_rules' );
add_filter( 'gt_performance_compiled_config', function ( array $config ): array {
    $config['cache'] = my_site_cache_rules( (array) $config['cache'] );
    return $config;
} );

The compiled config is rewritten on every settings save, on plugin activation and deactivation, and when the active commerce policy changes. After adding the code, save settings once so the runtime file is rewritten, then purge the page cache (Purge GT cache or wp gt-performance cache purge) so no old copy of /members/ survives. A save that changes no setting does not purge by itself. Then run Connect/sync Cloudflare or wp gt-performance cloudflare sync so the edge rule includes the new paths.

With only the first filter, WordPress never stores the page, but a copy stored before you added the rule is still served by the drop-in until it expires or is purged. Adding a path only in the Cloudflare dashboard leaves your origin caching it.

Checking your work

wp gt-performance cache explain --page-url=https://example.com/members/

prints the decision as JSON: cacheable, the deciding reason (for example path:/members/), the cache key, the state of the origin entry, and whether the managed Cloudflare rule agrees with the origin for that URL. The URL must be on this site’s home host.

Tools > Explain this page shows the same decision in wp-admin.

To see the decision on a live response, turn on Diagnostic logging (Optimization tab, Diagnostics) and look for X-GT-Cache: BYPASS and X-GT-Cache-Reason, which carries the reason as written, for example cookie:my_session_. A bypass cookie is easiest to test with curl:

curl -sI -H 'Cookie: my_session_abc=1' https://example.com/ | grep -i '^x-gt'

Turn Diagnostic logging off again when you are done.

Share

Was this helpful?

Still stuck? Open a support ticket