Documentation

Developer Reference

Everything you need to configure, extend, and control Frosty Cache through code.

Section Reference
OverviewHow Frosty Cache works, cache storage, skip parameter
ConstantsFROSTYCACHE_AGE, FROSTYCACHE_REST_PREFIX, FROSTYCACHE_FOLDER
Filters15 filters for cache duration, skip logic, keys, headers, ETags, and more
Actions6 actions for cache save, delete, and clear lifecycle events
WP-CLIflush, clear-post, clear-route, clear-index, status, cleanup, warm, repair, license, rollback

Overview

Frosty Cache caches WordPress REST API GET responses as static JSON files and serves them before the database loads. Configuration is done entirely through PHP filters, constants, and WP-CLI commands.

Cache files are stored in wp-content/uploads/frostycache_cache/. Each cached response includes the JSON data, response headers, an ETag, and an absolute expiry timestamp written at save time. Logged-in users and nonce-bearing requests bypass the cache by default.

Add ?skip_cache=1 to any REST API request to force a fresh response.

Constants

Define these in wp-config.php to override defaults.

FROSTYCACHE_AGE

Cache expiration in seconds. Default: 86400 (1 day). Sets the global default; can be overridden per request with the frostycache_cache_age filter. The duration in effect is stored in each cache file as an absolute expiry at save time, so changing this value only affects newly cached responses — flush the cache to apply it to existing files.

Define in wp-config.php before Frosty Cache loads.

FROSTYCACHE_REST_PREFIX

REST API URL prefix. Default: /wp-json. Override if you use a custom REST prefix.

FROSTYCACHE_FOLDER

Cache directory path. Default: wp-content/uploads/frostycache_cache/. Automatically uses per-site directories on multisite.

Filters

frostycache_cache_age

Filter

Control cache duration per request. Return an array with length and period keys. The default is FROSTYCACHE_AGE if defined, otherwise 1 day. The resulting duration is stored in the cache file as an absolute expiry at save time, so per-route durations are honored by every layer — including the early-serve drop-in / MU plugin and the expired-file cleanup cron. Changing a duration only affects newly cached responses; flush the cache to apply it to existing files.

Example: 1-hour cache for product endpoints
add_filter( ‘frostycache_cache_age’, function( $cache_age, $request ) { if ( str_contains( $request->get_route(), ‘/products’ ) ) { return [ ‘length’ => 1, ‘period’ => HOUR_IN_SECONDS ]; } return $cache_age; }, 10, 2 );

frostycache_skip_cache

Filter

Decide whether a request should skip caching entirely. Return true to bypass.

Example: Skip caching for a private namespace
add_filter( ‘frostycache_skip_cache’, function( $skip, $request ) { if ( str_contains( $request->get_route(), ‘/myapp/v1/private’ ) ) { return true; } return $skip; }, 10, 2 );

frostycache_skip_cache_nonce

Filter

Control whether requests containing a nonce header bypass caching. Default: true (skip).

Example: Allow caching even with nonces
add_filter( ‘frostycache_skip_cache_nonce’, ‘__return_false’ );

frostycache_dangerous_routes

Filter

Define REST API routes that should never be cached.

Example: Add a custom route to the blocklist
add_filter( ‘frostycache_dangerous_routes’, function( $routes ) { $routes[] = ‘/myapp/v1/webhook’; return $routes; } );

frostycache_generate_cache_key

Filter

Customize the cache key. Default is an MD5 hash of the serialized request details.

Example: Include user role in cache key
add_filter( ‘frostycache_generate_cache_key’, function( $key, $data, $request ) { $user = wp_get_current_user(); if ( $user->ID ) { return md5( $data . implode( ‘,’, $user->roles ) ); } return $key; }, 10, 3 );

frostycache_cache_dir

Filter

Change the directory where cached JSON files are stored.

Example: Store cache on a separate disk
add_filter( ‘frostycache_cache_dir’, function() { return ‘/mnt/ssd/frostycache_cache/’; } );

frostycache_etag

Filter

Customize the ETag value used for conditional requests (HTTP 304).

Example: Use SHA-256 instead of MD5
add_filter( ‘frostycache_etag’, function( $etag, $result ) { return ‘”‘ . hash( ‘sha256’, wp_json_encode( $result ) ) . ‘”‘; }, 10, 2 );

frostycache_cache_data

Filter

Modify the complete cache data structure (data, headers, ETag, expiry timestamp) before saving to disk.

Example: Add a timestamp to cached data
add_filter( ‘frostycache_cache_data’, function( $data ) { $data[‘cached_at’] = time(); return $data; } );

frostycache_cache_headers

Filter

Modify all response headers before they are cached.

Example: Add a custom header to cached responses
add_filter( ‘frostycache_cache_headers’, function( $headers ) { $headers[‘X-Powered-By’] = ‘Frosty Cache’; return $headers; } );

frostycache_cache_header

Filter

Modify an individual response header value before caching.

Example: Override Cache-Control for all cached responses
add_filter( ‘frostycache_cache_header’, function( $value, $key ) { if ( $key === ‘Cache-Control’ ) { return ‘public, max-age=3600, s-maxage=86400’; } return $value; }, 10, 2 );

frostycache_load_headers

Filter

Add extra headers when serving a cached response.

Example: Add CORS headers to cached responses
add_filter( ‘frostycache_load_headers’, function( $headers ) { $headers[‘Access-Control-Allow-Origin’] = ‘*’; return $headers; } );

frostycache_304_response

Filter

Filter the 304 Not Modified response before sending it to the client.

Example: Add a tracking header to 304 responses
add_filter( ‘frostycache_304_response’, function( $response ) { $response->header( ‘X-304-Served’, ‘true’ ); return $response; } );

frostycache_cache_request_details

Filter

Add custom properties to the cached request details (used in cache key generation).

Example: Vary cache by a custom header
add_filter( ‘frostycache_cache_request_details’, function( $args, $request ) { $args[‘currency’] = $request->get_header( ‘X-Currency’ ); return $args; }, 10, 2 );

frostycache_respect_response_cache_control

Filter

Control whether Frosty Cache honors the endpoint’s own Cache-Control header. When respected (the default), a response marked no-store, no-cache, or private is never written to the cache, even if it otherwise passed every other check.

Example: Ignore Cache-Control for a trusted endpoint
add_filter( ‘frostycache_respect_response_cache_control’, function( $respect, $cache_control, $request ) { if ( str_contains( $request->get_route(), ‘/myapp/v1/’ ) ) { return false; } return $respect; }, 10, 3 );

frostycache_associate_list_items

Filter

Control whether items in list endpoint responses are associated with the cache key for targeted invalidation.

Example: Disable item association for a custom endpoint
add_filter( ‘frostycache_associate_list_items’, function( $associate, $result, $request ) { if ( str_contains( $request->get_route(), ‘/myapp/v1/feed’ ) ) { return false; } return $associate; }, 10, 3 );

Actions

frostycache_before_cache_save

Action

Fires before a cache file is written to disk. Use for logging or validation.

Example: Log cache writes
add_action( ‘frostycache_before_cache_save’, function( $file, $data, $key ) { error_log( ‘Frosty Cache: caching ‘ . $key . ‘ to ‘ . $file ); }, 10, 3 );

frostycache_after_cache_save

Action

Fires after a cache file is successfully written to disk.

Example: Purge CDN after cache write
add_action( ‘frostycache_after_cache_save’, function( $file, $data, $key ) { my_cdn_purge( $key ); }, 10, 3 );

frostycache_save_cache_key

Action

Allows custom REST endpoints to save their cache keys for targeted invalidation.

Example: Track cache keys in a custom table
add_action( ‘frostycache_save_cache_key’, function( $request, $uri, $key ) { my_save_cache_key_mapping( $uri, $key ); }, 10, 3 );

frostycache_before_cache_delete

Action

Fires before cache files are deleted. The arguments differ by call site: a full cache flush fires it with just (array $files); a targeted delete for a specific post fires it with (array $cache_keys, int $post_id, string $post_type). Declare only $files (accepted args = 1) if you want a handler that works safely for both.

Example: Log cache purges for both call sites
add_action( ‘frostycache_before_cache_delete’, function( $files ) { error_log( ‘Frosty Cache: purging ‘ . count( $files ) . ‘ cache file(s)’ ); } );

frostycache_cache_cleared

Action

Fires after the entire cache has been cleared. No parameters.

Example: Notify after full cache flush
add_action( ‘frostycache_cache_cleared’, function() { do_action( ‘my_slack_notify’, ‘Frosty Cache cache was flushed’ ); } );

frostycache_after_cache_delete

Action

Fires after cache files have been deleted for a specific post.

Example: Warm cache after invalidation
add_action( ‘frostycache_after_cache_delete’, function( $post_id ) { wp_remote_get( rest_url( ‘wp/v2/posts/’ . $post_id ) ); } );

WP-CLI Commands

wp frostycache flush

Clear the entire Frosty Cache cache. All cached JSON files are deleted.

wp frostycache clear-post <id>

Clear cache files associated with a specific post ID and all related endpoints.

<id> (required) — The post ID to clear

wp frostycache clear-route <route> [–query=<query>]

Clear the cached response for one exact route — useful for custom REST endpoints with no post-type/meta tracking, or stale caches written before a frostycache_skip_cache rule existed. Works even if the route never registered a cache key association.

<route> (required) — The REST route as in WP_REST_Request::get_route(), e.g. /cocart/v2/checkout (no /wp-json prefix, no query string)
[--query=<query>] (optional) — The query string exactly as sent with the request, e.g. "category=22". Omit for routes requested with no query parameters

wp frostycache clear-index <type>

Clear index/list caches for a custom endpoint type that is not tied to a post type — caches saved via frostycache_index_cache_keys_{type} options, e.g. custom routes hooked into frostycache_save_cache_key.

<type> (required) — The index type, i.e. the suffix in the frostycache_index_cache_keys_{type} option name (e.g. wc_products, taxonomies)

wp frostycache status

Show cache statistics: file count, total size, and cache directory path.

wp frostycache cleanup

Remove expired cache files. Runs automatically via WP-Cron (twicedaily).

wp frostycache warm [–post_type=<type>] [–limit=<number>] [–per-page=<number>] [–delay=<ms>] [–dry-run]

Pre-populate the cache for published posts and pages by issuing real loopback HTTP requests to their REST API endpoints — the same request path a real visitor would trigger, sent unauthenticated with no cookies so the cache is actually written. Reports a progress bar while running and a summary of successes/failures when done.

[--post_type=<type>] (optional) — Comma-separated list of post types to warm. Default: post,page
[--limit=<number>] (optional) — Maximum number of items to warm. Default: all matching items
[--per-page=<number>] (optional) — Number of posts to query per batch. Default: 100
[--delay=<ms>] (optional) — Milliseconds to sleep between requests. Default: 0
[--dry-run] (optional) — List the URLs that would be warmed without making any requests

wp frostycache repair [–force]

Repair the cache layer: reinstalls the MU plugin fallback and the advanced-cache.php drop-in. If another cache plugin owns the drop-in it is left in place (REST API caching stays active via the MU plugin) unless --force is passed. Prints the cache layer status when done.

[--force] (optional) — Overwrite advanced-cache.php even if owned by another plugin

wp frostycache license <action> [<key>]

Manage the plugin license. Actions: activate, deactivate, status.

<action> (required) — activate, deactivate, or status
[<key>] (optional) — License key (required for activate)

wp frostycache rollback [–version=<version>]

Rollback to a previous plugin version. Requires a valid license. Omit --version to list available versions.