Rule Examples
Practical MilliCache rule examples for common scenarios. All examples use the MilliRules fluent API.
Setup#Copied!
Every example below starts from $rules, which you get from MilliCache:
1$rules = millicache()->rules();
Where to put the code#Copied!
Anywhere in a plugin or a theme. MilliCache is loaded by then, so the rule registers right away and no wrapper is needed.
The exception is a file that runs before MilliCache, a must-use plugin being the
usual case. On a live request that is still fine, because the page cache loads at the
very top of WordPress. Under WP-CLI it is not: the CLI skips the page cache, so
millicache() does not exist yet and the file ends in a fatal error. Register on a
hook there, which also keeps the rules visible to anything reading them outside a
cached request: the abilities API, and wp millicache rules list in
MilliCache Pro.
1add_action( 'plugins_loaded', function () {
2 millicache()->rules()->create( 'mysite:example' )
3 // …
4 ->register();
5} );
Which phase a rule runs in#Copied!
You do not have to say. MilliCache reads the conditions and actions you used and picks the phase that can run them, and a rule that could only have run in the bootstrap phase is moved to the WordPress phase, because by the time your code runs that phase is already over. There it still sets the lifetime, adds flags, and decides whether the generated page is stored.
Use ->on() only to move a rule to a specific WordPress action.
The bootstrap phase cannot be reached from code. It runs inside
advanced-cache.php, before any plugin, theme or must-use plugin exists, and it runs once. Bootstrap rules have to live in the settings, which the drop-in reads from the database. Writing them takes MilliCache Pro: either its Rules Builder orwp millicache rules import.The difference is speed, not capability: a bootstrap rule can turn a request away before WordPress loads, while a WordPress-phase rule decides once the page has been built.
Example 1: Different TTL by Content Type#Copied!
Cache news for 15 minutes, documentation for 1 week:
1// Short TTL for news
2$rules->create( 'mysite:news-ttl' )
3 ->order( 10 )
4 ->when()
5 ->request_url( '/news/*' )
6 ->then()
7 ->set_ttl( 900 ) // 15 minutes
8 ->register();
9
10// Long TTL for documentation
11$rules->create( 'mysite:docs-ttl' )
12 ->order( 10 )
13 ->when()
14 ->request_url( '/docs/*' )
15 ->then()
16 ->set_ttl( 604800 ) // 1 week
17 ->register();
Example 2: WooCommerce Cart/Checkout Bypass#Copied!
Never cache cart, checkout, or account pages:
1// Runs early in the WordPress phase, before the page is stored
2$rules->create( 'mysite:woo-no-cache' )
3 ->order( 1 )
4 ->when_any()
5 ->request_url( '*/cart/*' )
6 ->request_url( '*/checkout/*' )
7 ->request_url( '*/my-account/*' )
8 ->cookie( 'woocommerce_*' )
9 ->then()
10 ->do_cache( false, 'WooCommerce dynamic page' )
11 ->register();
Example 3: Membership Site Caching#Copied!
Cache for guests, bypass for active members:
1// WordPress phase - needs user context
2$rules->create( 'mysite:members-no-cache' )
3 ->on( 'template_redirect', 25 )
4 ->order( 10 )
5 ->when()
6 ->is_user_logged_in()
7 ->custom( 'is-active-member', function() {
8 // Check your membership plugin
9 return function_exists( 'hasMembershipLevel' )
10 && hasMembershipLevel();
11 } )
12 ->then()
13 ->do_cache( false, 'Active member' )
14 ->register();
Example 4: A/B Testing Support#Copied!
Different cache entries for A/B test variants:
1// Add a test variant as a flag
2$rules->create( 'mysite:ab-test-flag' )
3 ->on( 'template_redirect', 25 )
4 ->order( 10 )
5 ->when()
6 ->cookie( 'ab_variant' )
7 ->then()
8 ->add_flag( 'ab:' . ( $_COOKIE['ab_variant'] ?? 'control' ) )
9 ->register();
Example 5: Preview and Draft Bypass#Copied!
Never cache previews or drafts:
1$rules->create( 'mysite:no-preview' )
2 ->order( 1 )
3 ->when_any()
4 ->request_param( 'preview', 'true' )
5 ->request_param( 'draft', '1' )
6 ->request_param( 'p' ) // Post preview by ID
7 ->then()
8 ->do_cache( false, 'Preview/draft mode' )
9 ->register();
Example 6: Block-Based Rules#Copied!
Use has_block() to target pages containing specific Gutenberg blocks:
1// Flag pages with a pricing table block
2$rules->create( 'mysite:pricing-flag' )
3 ->on( 'template_redirect', 25 )
4 ->order( 10 )
5 ->when()
6 ->is_singular()
7 ->has_block( 'acme/pricing-table' )
8 ->then()
9 ->add_flag( 'block:pricing' )
10 ->register();
11
12// Short TTL for pages with live data blocks
13$rules->create( 'mysite:live-data-ttl' )
14 ->on( 'template_redirect', 25 )
15 ->order( 10 )
16 ->when()
17 ->is_singular()
18 ->has_block( 'acme/live-stock-ticker' )
19 ->then()
20 ->set_ttl( 60 ) // 1 minute for live data
21 ->add_flag( 'live-data' )
22 ->register();
Example 7: Term-Based Rules#Copied!
Use has_term() for taxonomy-based caching decisions:
1// Flag seasonal products
2$rules->create( 'mysite:seasonal-flag' )
3 ->on( 'template_redirect', 25 )
4 ->order( 10 )
5 ->when()
6 ->is_singular( 'product' )
7 ->has_term( 'seasonal', 'product_cat' )
8 ->then()
9 ->add_flag( 'promo:seasonal' )
10 ->register();
11
12// Short TTL for featured content
13$rules->create( 'mysite:featured-ttl' )
14 ->on( 'template_redirect', 25 )
15 ->order( 10 )
16 ->when()
17 ->is_singular( 'post' )
18 ->has_term( 'featured', 'post_tag' )
19 ->then()
20 ->set_ttl( 1800 ) // 30 minutes
21 ->add_flag( 'featured' )
22 ->register();
Example 8: Conditional TTL by Post Meta#Copied!
Short TTL for "breaking news" posts:
1$rules->create( 'mysite:breaking-news-ttl' )
2 ->on( 'template_redirect', 25 )
3 ->order( 10 )
4 ->when()
5 ->is_singular( 'post' )
6 ->custom( 'is-breaking', function() {
7 return get_post_meta( get_the_ID(), 'breaking_news', true );
8 } )
9 ->then()
10 ->set_ttl( 300 ) // 5 minutes for breaking news
11 ->add_flag( 'breaking' )
12 ->register();
Example 9: Geolocation-Based Caching#Copied!
Tag cache entries by country for geo-targeted content:
1$rules->create( 'mysite:geo-flag' )
2 ->order( 10 )
3 ->when()
4 ->request_header( 'CF-IPCountry' ) // Cloudflare header
5 ->then()
6 ->custom( 'add-geo-flag', function() {
7 $country = $_SERVER['HTTP_CF_IPCOUNTRY'] ?? 'XX';
8 millicache_add_flag( 'geo:' . strtolower( $country ) );
9 } )
10 ->register();
Example 10: API Rate Limiting Support#Copied!
Short TTL for API responses:
1$rules->create( 'mysite:api-ttl' )
2 ->order( 10 )
3 ->when()
4 ->request_url( '/api/*' )
5 ->request_method( 'GET' )
6 ->then()
7 ->set_ttl( 60 ) // 1 minute
8 ->set_grace( 300 ) // 5 minute grace
9 ->register();
Example 11: Mobile vs Desktop Caching#Copied!
Separate cache entries for mobile and desktop:
1$rules->create( 'mysite:mobile-flag' )
2 ->order( 10 )
3 ->when()
4 ->custom( 'is-mobile', function() {
5 $ua = $_SERVER['HTTP_USER_AGENT'] ?? '';
6 return preg_match( '/Mobile|Android|iPhone/i', $ua );
7 } )
8 ->then()
9 ->custom( 'add-mobile-flag', function() {
10 millicache_add_flag( 'device:mobile' );
11 } )
12 ->register();
13
14$rules->create( 'mysite:desktop-flag' )
15 ->order( 10 )
16 ->when()
17 ->custom( 'is-desktop', function() {
18 $ua = $_SERVER['HTTP_USER_AGENT'] ?? '';
19 return ! preg_match( '/Mobile|Android|iPhone/i', $ua );
20 } )
21 ->then()
22 ->custom( 'add-desktop-flag', function() {
23 millicache_add_flag( 'device:desktop' );
24 } )
25 ->register();
Example 12: Clear Cache on External Event#Copied!
Clear product cache when inventory system updates:
1$rules->create( 'mysite:inventory-clear' )
2 ->on( 'my_inventory_updated', 10 ) // Your custom hook
3 ->when()
4 ->custom( 'always', fn() => true )
5 ->then()
6 ->clear_cache( [ 'product:*', 'woo:sale' ] )
7 ->register();
Compound Conditions#Copied!
Combine conditions with AND, OR, and NOT logic:
1// AND (default) - all conditions must match
2$rules->create( 'mysite:all-match' )
3 ->when()
4 ->request_method( 'GET' )
5 ->request_url( '/shop/*' )
6 ->cookie( 'currency', 'EUR' )
7 ->then()
8 // Only runs if ALL conditions match
9 ->add_flag( 'shop:eur' )
10 ->register();
11
12// OR - any condition can match
13$rules->create( 'mysite:any-match' )
14 ->when_any()
15 ->request_url( '*/cart/*' )
16 ->request_url( '*/checkout/*' )
17 ->request_url( '*/account/*' )
18 ->then()
19 // Runs if ANY condition matches
20 ->do_cache( false, 'Dynamic page' )
21 ->register();
22
23// NOT - none of the conditions should match
24$rules->create( 'mysite:none-match' )
25 ->when_none()
26 ->request_method( 'GET' )
27 ->request_method( 'HEAD' )
28 ->then()
29 // Runs if NEITHER GET nor HEAD
30 ->do_cache( false, 'Non-cacheable method' )
31 ->register();
Tips#Copied!
Use Descriptive Rule IDs#Copied!
1// Good - namespace:purpose
2$rules->create( 'mysite:woo-cart-bypass' )
3
4// Avoid - generic
5$rules->create( 'rule1' )
Choose the Right Phase#Copied!
Use Bootstrap (php) when... |
Use WordPress (wp) when... |
|---|---|
| Checking URL patterns | Checking user roles |
| Checking cookies/headers | Checking post meta |
| Setting TTL by path | Checking template |
| Early bypass decisions | Adding content-based flags |
Keep Bootstrap Rules Simple#Copied!
Bootstrap rules run before WordPress, so keep them fast:
1// Good - simple string match
2->when()->request_url( '/api/*' )
3
4// Avoid in bootstrap - complex logic
5->when()->custom( 'complex', function() {
6 // Loading files, database, etc. defeats the purpose
7} )
Learn More#Copied!
- MilliRules Documentation — Complete rules engine reference
- Conditions Reference
- Actions Reference
- Cache Flags — Partner feature to rules
- Visual Rules Builder — Build rules like these without code, in MilliCache Pro
Last updated:
Release updates
Get notified when new versions and guides ship. No spam.
By subscribing you agree to our Privacy Policy. Double opt-in, no spam, unsubscribe anytime.