# How to Measure Your Cache Hit Ratio (And What to Do When It's Low) — CacheBoost Blog

> Cache hit ratio is the single most useful metric for understanding your cache's health. Here's how to measure it across different stacks and what numbers to aim for.

Source: https://www.cache-boost.com/blog/how-to-measure-cache-hit-ratio.md
Language: en

---


Tags: tutorial, performance
Published: 2026-06-24
Author: Nicolas Hodin
Reading time: 4 min

---

**In summary:**
- Defines cache hit ratio and why it's the key cache-health metric
- Shows how to measure it on Cloudflare, Varnish, Nginx, and WordPress plugins
- Gives target numbers to aim for by site type
- Covers common causes of a low ratio and how to build a health dashboard
## What is cache hit ratio?

> **Cache hit ratio** is the percentage of HTTP requests served directly from cache, without hitting your origin server. A hit ratio of 90% means 9 out of 10 requests never reach your PHP application or database.

```
Hit ratio = (cache hits) / (cache hits + cache misses) × 100
```

A high hit ratio means:
- Faster responses for users (cached responses are 10–100× faster than origin responses)
- Lower server load (fewer requests reach PHP/database)
- Better Core Web Vitals scores (TTFB is dominated by origin response time on misses)

## Measuring cache hit ratio by stack

### Cloudflare

The easiest place to start. In the Cloudflare dashboard:

1. Go to **Caching → Cache Analytics** (or **Analytics & Logs → Traffic**)
2. Look at the **cache status** breakdown (hit / miss / expired …)

Cloudflare shows requests split into: `hit`, `miss`, `expired`, `bypass`, `revalidated`, `dynamic`.

For a rough hit ratio: `hit / (hit + miss + expired)`. Ignore `bypass` (requests configured to skip cache) and `dynamic` (Cloudflare Workers or API responses).

Via the API (the legacy Zone Analytics endpoint was sunset in 2020, use the GraphQL Analytics API):
```bash
SINCE=$(date -u -d '7 days ago' +%Y-%m-%d)   # macOS: date -u -v-7d +%Y-%m-%d
curl https://api.cloudflare.com/client/v4/graphql \
  -H "Authorization: Bearer $CF_TOKEN" -H "Content-Type: application/json" \
  -d '{"query":"query($zone:String!){viewer{zones(filter:{zoneTag:$zone}){httpRequests1dGroups(limit:7,filter:{date_geq:\"'"$SINCE"'\"}){sum{requests cachedRequests}}}}}","variables":{"zone":"'"$ZONE_ID"'"}}' \
  | jq '[.data.viewer.zones[0].httpRequests1dGroups[].sum]
        | {cached:(map(.cachedRequests)|add), total:(map(.requests)|add)}
        | {cached, total, ratio:(.cached/.total*100)}'
```

### Varnish

```bash
# Live stats (refreshed every second)
varnishstat -f MAIN.cache_hit -f MAIN.cache_miss

# One-time snapshot with computed ratio
varnishstat -1 -f MAIN.cache_hit -f MAIN.cache_miss | awk '
  /cache_hit/  { hit  = $2 }
  /cache_miss/ { miss = $2 }
  END { printf "Hit: %d  Miss: %d  Ratio: %.1f%%\n", hit, miss, hit/(hit+miss)*100 }
'
```

For time-windowed stats, use `varnishlog` with `vsl-query` or set up a Prometheus + Varnish exporter.

### Nginx (proxy_cache)

Add these variables to your access log format:

```nginx
log_format cache_status '$remote_addr - $upstream_cache_status '
                        '"$request" $status $body_bytes_sent';
access_log /var/log/nginx/cache.log cache_status;
```

`$upstream_cache_status` returns `HIT`, `MISS`, `EXPIRED`, `BYPASS`, `STALE`, or `UPDATING`.

```bash
# Tally cache statuses across the log
awk '{print $3}' /var/log/nginx/cache.log | sort | uniq -c | sort -rn
# Then: HIT / (HIT + MISS + EXPIRED) × 100
```

### WP Rocket / LiteSpeed Cache

You can detect these plugins per response:

- WP Rocket: no cache header by default, check the end of the HTML source for the footprint comment `<!-- This website is like a Rocket, isn't it? Performance optimized by WP Rocket... - Debug: cached@<timestamp> -->`
- LiteSpeed: `X-LiteSpeed-Cache: hit`

For aggregate stats, you need either server-level logging (Nginx/Apache) or a plugin like Query Monitor that shows cache status per request.

### Google Search Console (for SEO impact)

Search Console doesn't show cache hit ratios, but it shows the consequences of a low one. In **Core Web Vitals**:

- A high TTFB (Time to First Byte) for crawled pages usually indicates cold cache hits
- Pages with poor LCP scores often correlate with uncached origin responses

Cross-reference your cache flush events (deploys, plugin updates) with dips in Core Web Vitals scores in Search Console to see the SEO impact of cold cache periods.

## What numbers to aim for

| Hit ratio | Assessment |
|-----------|------------|
| > 90% | Healthy: most visitors get cached responses |
| 70–90% | Acceptable: room for improvement, especially for high-traffic pages |
| 50–70% | Low: investigate TTL settings and purge frequency |
| < 50% | Critical: significant performance and cost impact |

## Common causes of low hit ratio

**1. Too-short TTL**: If your `Cache-Control: max-age` is 60 seconds, pages expire before they're warmed by organic traffic. Extend TTL to 3600–86400 seconds for mostly-static content.

**2. Bypass rules that are too broad**: `Cache-Control: no-cache` or `no-store` on pages that don't need it. Check if your CMS or framework adds these headers by default.

**3. Excessive purging**: WooCommerce stores that purge on every stock change can flush their cache hundreds of times per day. Throttle or debounce purge events.

**4. Query string variations**: `example.com/page?ref=newsletter` and `example.com/page?ref=twitter` are treated as separate URLs by most caches. Normalize query strings in your cache configuration to avoid cache fragmentation.

**5. No warming**: Even a perfect cache configuration produces a low hit ratio immediately after a flush. Without warming, hit ratio recovers slowly as organic traffic refills the cache. With warming, it recovers in minutes.

## Building a cache health dashboard

For production sites, track hit ratio over time rather than spot-checking:

- **Cloudflare Analytics** (built-in)
- **Varnish + Prometheus exporter + Grafana** (self-hosted)
- **CacheBoost run history** (shows warming completion, which correlates with hit ratio recovery)

Set an alert when hit ratio drops below 70%: this usually indicates an unplanned cache flush or a configuration issue that needs investigation.

<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "FAQPage",
  "mainEntity": [
    {
      "@type": "Question",
      "name": "What is cache hit ratio?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "Cache hit ratio is the percentage of HTTP requests served directly from cache, without hitting your origin server. A hit ratio of 90% means 9 out of 10 requests never reach your application or database."
      }
    }
  ]
}
</script>


---

**CacheBoost** — Automatic cache warming for faster websites.

- Website: https://www.cache-boost.com
- Full content (all pages): https://www.cache-boost.com/llms-full.txt
- LLM index: https://www.cache-boost.com/llms.txt
- Documentation: https://www.cache-boost.com/support/getting-started/introduction
- Start free: https://www.cache-boost.com/try
