How to allow Pingdomain through the Cloudflare WAF
A skip rule that allows your own uptime checks through the Cloudflare WAF, without opening a hole anyone else can walk through.
What you are matching on
Every check we send carries these, and they are stable:
- A user agent that names us and links to an explanation:
Pingdomain/2.0 (+https://www.pingdomain.io; monitoring)
- An HMAC signature header,
x-pingdomain-signature, of the formt=<unix seconds>,v1=<hex>. It is a SHA-256 HMAC over<timestamp>.<monitor id>using your organisation's signing secret, which you will find under your organisation settings.
We also publish an Ed25519 key directory for RFC 9421 HTTP Message Signatures and are going through Cloudflare's bot verification programme. Until that registration is approved, checks are not signed that way — a signature a verifier cannot check against an approved key list looks like forgery and gets you blocked harder, so we keep it switched off until it helps. When it is live, "verified bots" becomes the simplest rule of all. Details are on the bot page.
Step 1 — turn off the blanket block first
Go to Security → Bots. If "Block definitely automated traffic" is on, no WAF skip rule below will help, because the bot module runs before your custom rules for some products. Set it to allow, or scope it so it does not cover the path you monitor.
Step 2 — add a skip rule
Go to Security → WAF → Custom rules and create a rule with the action Skip, ticking the components you want bypassed — typically "All remaining custom rules", "Rate limiting rules" and "Super Bot Fight Mode". Use the expression editor and paste:
(http.user_agent contains "Pingdomain/" and http.request.uri.path eq "/health")
Scope it to the path you monitor. That is the whole trick. A user agent can be claimed by anyone, so a rule matching only on it is a hole across your entire site — but a rule that only exempts the one health endpoint you point the monitor at is worth almost nothing to an attacker and everything to you.
If you monitor several paths, match a prefix instead:
(http.user_agent contains "Pingdomain/" and starts_with(http.request.uri.path, "/status/"))
Step 3 — verify at your origin, if it matters
For an endpoint that must not be exposed at all, do not rely on the WAF: check the signature in your own application and return 404 to everyone else. Verification is a dozen lines, and the secret never leaves your server.
// Node.js — verify x-pingdomain-signature
import { createHmac, timingSafeEqual } from 'node:crypto';
function verify(header, monitorId, secret) {
const parts = Object.fromEntries(
header.split(',').map((p) => p.split('=')),
);
const age = Math.abs(Date.now() / 1000 - Number(parts.t));
if (!Number.isFinite(age) || age > 300) return false; // replay window
const expected = createHmac('sha256', secret)
.update(`${parts.t}.${monitorId}`)
.digest('hex');
const a = Buffer.from(expected);
const b = Buffer.from(parts.v1 ?? '');
return a.length === b.length && timingSafeEqual(a, b);
}The timestamp is inside the signed value, which is what lets you reject a replayed header rather than accepting it forever.
Step 4 — confirm it worked
Open the monitor and wait one interval. A check that was being challenged goes from a 403 at 8 ms to a 200 at whatever your origin actually takes — and the difference between those two numbers is usually the clearest proof that the rule matched.
Still blocked? The response body is kept for failed checks. Read it: it will name the product that rejected the request, which is often a second layer nobody remembered was on.
Checks you can actually allow through
An honest user agent, a signature you can verify, and an error message that says which protection blocked it. Three monitors are free.
Get started — free