What gets cached
Edge Caching caches anonymous GET and HEAD requests for successful pages
and redirects, for one hour. Everything personal or state-changing goes
straight to the origin. This page describes the rules in the order the site’s
VCL applies them, and how to diagnose a page that is never cached.
How a request is evaluated
Section titled “How a request is evaluated”Each request reaching the site’s Varnish instance goes through these steps, in this order. As soon as a step passes or blocks the request, evaluation stops there.
- Development Mode. When enabled, every request is passed to the origin and nothing is cached.
- Routing. If the path matches one of your extra routes, the request is sent to that route’s backend instead of the main origin. All the following rules still apply.
Authorizationheader. Requests carrying HTTP authentication are passed.- Excluded visitor IPs are passed.
- Geo blocking. Requests from a blocked country receive a
403. - Excluded cookies, URL paths and user agents are passed.
- XML-RPC and trackbacks (
/xmlrpc.php,/wp-trackback.php) are blocked with a403when XML-RPC blocking is enabled, and passed otherwise. - Firewall rules return a
403: AI crawlers, vulnerability scanners, blocked user agents, empty user agents, sensitive files, PHP files in uploads and author enumeration, each when enabled. wordpress_no_cachecookie. With Exclude DONOTCACHEPAGE Constant enabled, requests carrying this cookie are passed.- Cookies are removed from the request.
- Query string cleaning strips tracking parameters, or the whole query string if configured.
- Method check. Anything other than
GETorHEADis passed. - Cache lookup. A hit is answered from memory; a miss is fetched from the origin.
Default bypass rules
Section titled “Default bypass rules”These defaults are tuned for WordPress and WooCommerce. You can add your own under Cache Rules in the admin.
| Rule | Default values |
|---|---|
| Excluded cookies | wordpress_logged_in, wordpress_sec_*, wp-postpass, woocommerce_cart_hash, woocommerce_items_in_cart, woocommerce_cart, wp_woocommerce_session |
| Excluded URL paths | /wp-admin, /wp-login.php, /wp-json/, /feed, sitemaps (sitemap*.xml), /login, /cart, /checkout, /my-account |
| Stripped query parameters | utm_source, utm_medium, utm_campaign, utm_content, utm_term, fbclid, gclid, msclkid |
Paths are matched as regular expressions against the URL. A path rule
starting with ^ matches only at the beginning of the URL, so ^/cart
covers /cart/ and /cart/?step=2 but not /product/cart-cover/.
Cookies
Section titled “Cookies”After the bypass checks, all remaining cookies are removed from the request before the cache lookup. A cached page therefore never varies by cookie, and a cookie never causes a page to bypass the cache unless it is listed in Exclude Cookies.
If your theme or a plugin reads a cookie to change the page for anonymous visitors, such as a currency switcher, a language selector or an A/B test, add that cookie to Exclude Cookies. Otherwise every visitor receives whichever version was cached first.
Query strings
Section titled “Query strings”Tracking parameters are removed before the lookup, so
/pricing/?utm_source=newsletter and /pricing/ share one cache entry, and
the origin receives the URL without them. Browser-based analytics still see
them; see Analytics and tracking tools
for the plugins that read them on the server.
Any other parameter creates a separate cache entry: /shop/?color=red and
/shop/?color=blue are cached independently. If your site never uses query
strings to change content, Strip all query strings collapses every variant
into one entry. Do not enable it on sites where query strings matter, such as
search results or filtered product listings.
Which responses are stored
Section titled “Which responses are stored”| Origin response | Behavior |
|---|---|
200, 301, 302 |
Cached for 1 hour, regardless of max-age, plus the grace period when Always Online is enabled |
Cache-Control: no-cache, no-store or private |
Never cached, whatever the status |
404 |
Not cached |
5xx |
Not cached. Visitors get the maintenance page, or a stale copy when Always Online has one |
Other statuses (307, 410, …) |
Varnish defaults: the response’s own Cache-Control or Expires, otherwise two minutes |
The one-hour lifetime is set by the platform: a max-age or s-maxage from
the origin neither shortens nor extends it. To keep a page out of the cache,
send Cache-Control: private or no-store, or add its path to Exclude URL
Paths. To refresh a page after a change, purge it (see
How the cache is refreshed).
Set-Cookie headers are removed from cached responses, so one visitor’s
session cookie is never served to another. A consequence is that a cached
page cannot set cookies. Responses that are passed, or marked private or
no-store, keep their Set-Cookie headers.
Response headers
Section titled “Response headers”| Header | Meaning |
|---|---|
X-Cache: HIT |
Served from cache. X-Cache-Hits shows how many times this cached copy has been served. |
X-Cache: MISS |
Fetched from the origin, either a cache miss or a passed request. |
X-Cache: BLOCKED |
Rejected by a site firewall rule. |
X-Served-By |
Identifies responses served by the site’s Varnish instance. |
Inspect them with:
curl -sI https://example.com/ | grep -i '^x-'Why is a page never cached?
Section titled “Why is a page never cached?”If a page returns X-Cache: MISS on every request, check these causes in
order:
- Development Mode is enabled for the site.
- You are logged in. Your browser sends
wordpress_logged_in, which is excluded. Test from a private window or withcurl. - The path matches an excluded path, including the defaults above.
- The origin sends
Cache-Control: private,no-cacheorno-store. Some plugins and WooCommerce session handling do this on every page. Check withcurl -sIagainst the origin. - The request is not a
GETorHEAD, or carries anAuthorizationheader. - The URL varies. Different query strings are different cache entries; a unique parameter on every request means every request is a miss.
- The response is a
404or5xx, which are never cached.