The Watchbillby LatticeDDI

Monitoring a site behind a CDN, firewall or proxy

Published . Updated .

This guide explains how to monitor a site that sits behind a CDN, a firewall, or a reverse proxy, and how to tell a cached answer from the server itself.

If the site sits behind a CDN, firewall, or proxy, an Up result means that service answered. It may be serving a cached copy even when your server is down. To check the server itself, point a check at a health URL that is never cached, and turn on must not be served from cache.

The public homepage and the server behind it are two different questions. Visitors care that the homepage loads. You may also have promised to notice when the server itself stops. A CDN, a web application firewall, or a reverse proxy can answer the first question with a stored page while the second question is already no. Cloudflare, Akamai, Fastly, CloudFront, Azure Front Door, Sucuri, and Imperva are examples. The same thing happens on a small Varnish or nginx proxy you run yourself. The brand on the dashboard does not change the measurement.

Why does a cached page stay green?

A normal HTTPS check requests the URL you saved and reads the status. A 200 is up. If a cache in front of the server still has a 200 from this morning, the check receives that 200. The server can be off, the process can be crashed, or the database can be refusing connections, and the status code the checker sees is still success. The outage you meant to catch is hidden behind a copy.

That is what the cache is for. The mistake is to report it as "the server is up" when you only saw the cache. If the agreement is about the public URL, a cached 200 is the right answer, because visitors got a page. If the agreement is about the server, you need a request the cache will not satisfy from storage. A firewall or reverse proxy can do the same with a stale copy or its own block page. How to read the status code is in which HTTP codes count as downtime.

What should you do?

  1. Decide, in writing, whether this check watches the public URL or the server behind it. One check should not pretend to be both.
  2. If you need the server, add a health URL that returns a small, uncacheable response, such as a 200 with a short body, and point the check at that URL.
  3. Tell the CDN, firewall, or proxy not to store that URL. Do this for the one path, not for the whole site. A unique query string on every request is a poor substitute: it fills the cache with one copy per check.
  4. Turn on "must not be served from cache" for that check, then request the URL and read the cache header. A stored copy should not count as up.
  5. Keep a separate check on the public homepage when the client also cares that visitors can load the cached page. Say on the status page which check is which.

The health URL has to be one the cache is allowed to skip. A homepage the CDN is configured to store will keep answering from storage. A path that sets Cache-Control: no-store, or that a cache rule skips, fails when the server fails. Do not turn caching off for the whole site. The bypass belongs on the health URL only. A meta tag in the HTML does not do this. The cache decides before a browser reads the page.

Which headers say a proxy answered?

You do not need a vendor menu to see that a proxy was in the path. The response carries headers. Read them on one request made the way the checker does, with the checker's user agent, and write down what you see.

Cloudflare documents CF-Cache-Status. HIT means the edge served a stored copy. STALE and UPDATING also serve a stored copy. DYNAMIC means the request was not eligible for cache. BYPASS means the request was eligible and the response was not cacheable. Cloudflare also sets Age when it serves HIT, STALE, or UPDATING. An Age on a DYNAMIC or BYPASS response can be one the origin set, so do not treat every Age as a Cloudflare hit. Source: Cloudflare's cache responses page, checked 10 October 2026.

Fastly documents X-Cache and X-Served-By. X-Cache is HIT when the response was satisfied from cache, including a stale object, and MISS otherwise. A PASS is reported as MISS. With shielding, the header can list more than one value. Any value other than MISS means a cache satisfied the request. X-Served-By names the cache node, often in the form cache- plus a site code. Source: Fastly's X-Cache reference, checked the same day.

Amazon CloudFront adds X-Amz-Cf-Id on the request it forwards, and the viewer response carries X-Cache. "Hit from cloudfront" and "RefreshHit from cloudfront" mean the edge used a stored object. "Miss from cloudfront" means it fetched from the origin. Source: CloudFront's origin behavior page and AWS's notes on the X-Cache header, checked the same day.

Azure Front Door attaches X-Azure-Ref on responses and an X-Cache value. Microsoft's caching page lists TCP_HIT and TCP_REMOTE_HIT as served from the Front Door cache, TCP_MISS as fetched from the origin, PRIVATE_NOSTORE as not cached because of private or no-store, and CONFIG_NOCACHE as not cached because the route says so. Source: Azure Front Door caching and the Front Door header list, checked the same day.

Akamai can add X-Akamai- headers, including X-Akamai-Request-ID. Its diagnostic notes describe X-Cache values such as TCP_HIT and TCP_MISS. Those diagnostic headers are not on every response by default. If you see an X-Akamai- header, Akamai handled the request. This guide does not give a click-path for turning the diagnostics on. Source: Akamai's pragma header list, checked the same day.

X-Sucuri-* headers, when they are present, mean Sucuri handled the response. Via means some proxy added its name to the path. Age greater than zero means a cache has been holding the object, unless a status header above already says the origin answered this request.

What you seeWhat it means for this request
CF-Cache-Status HIT, STALE, or UPDATINGCloudflare served a stored copy
CF-Cache-Status DYNAMIC or BYPASSCloudflare did not serve a stored copy
X-Cache HIT, or Hit from cloudfront, or TCP_HITA cache served a stored copy
X-Cache MISS, or Miss from cloudfront, or TCP_MISS, CONFIG_NOCACHE, PRIVATE_NOSTOREThis response was not a stored copy
Age greater than zero, and no miss or bypass aboveA cache has been holding the object
X-Amz-Cf-Id, X-Azure-Ref, X-Akamai-, X-Sucuri-, X-Served-By, or ViaA CDN or proxy was on the path. Read the cache header before you call the server up

What should you tell the client?

If you publish a status page, say what the green mark measured. "Website" is ambiguous. "Public homepage" and "application server" are not. A client who loads a cached homepage will not believe a page that says the site is down, and a client whose checkout is broken will not believe a page that says the site is up because the cache answered. The longer version is in what a client status page should show.

When the health URL fails and the homepage stays green, the server is in trouble and visitors may still be reading a stored page. Open the ticket for the server. Do not mark the public page down until it is. Two checks, two sentences.

How The Watchbill helps

The Watchbill checks each URL about every 60 seconds. A result becomes up or down only after two consecutive minutes agree, so one odd sample does not open an incident by itself. Email goes out when that confirmed state changes.

A normal check of a public URL reports the answer it received, including a cached one. The status page says so: if the site sits behind a CDN, firewall, or proxy (such as Cloudflare, Akamai, or Fastly), an Up result means that service answered, and it may be a cached copy. To watch the server, point the check at a health URL that is never cached and turn on "must not be served from cache". That rule accepts a response that was not served from cache. On Cloudflare that is CF-Cache-Status of DYNAMIC or BYPASS. The same rule also accepts a miss or a no-cache status from Fastly, CloudFront, Akamai, and Azure Front Door, and it rejects a hit from those networks. An Age greater than zero, without one of those miss or bypass statuses, does not count as up.

When the response carries one of those headers, the check page says "Served through a CDN or proxy" and adds the vendor name when the header identifies one: Cloudflare, Akamai, Amazon CloudFront, Fastly, Azure Front Door, or Sucuri. A Via header with no vendor name still gets the neutral sentence.

You can also turn on origin reach. The check sends a secret and a fresh nonce, and the server has to echo the nonce. A stored copy cannot. The portal shows a Cloudflare Cache Rule for that header, because that is the vendor path we checked, plus snippets for the server. Other networks need their own bypass for the same URL. A meta tag is not a bypass on any of them.

The checker calls itself TheWatchbill/1.0 (+https://thewatchbill.com), which is the name to allow through a firewall. Try one health URL with a free account, and use the glossary if origin and edge are still easy to mix up.

Sources

  1. Cloudflare, Cache responses (CF-Cache-Status and Age). Accessed October 10, 2026.
  2. Fastly, X-Cache header. Accessed October 10, 2026.
  3. Azure Front Door caching (X-Cache values). Accessed October 10, 2026.
  4. Azure Front Door HTTP headers (X-Azure-Ref). Accessed October 10, 2026.
  5. Akamai, Pragma headers (X-Cache and X-Akamai-Request-ID). Accessed October 10, 2026.
  6. Amazon CloudFront, request and response behavior for custom origins (X-Amz-Cf-Id). Accessed October 10, 2026.
  7. AWS re:Post, CloudFront X-Cache Hit and RefreshHit. Accessed October 10, 2026.
  8. AWS re:Post, X-Cache Miss from CloudFront. Accessed October 10, 2026.

Related guides