Playbook: Cloudflare edge blocks a subdomain with no matching enumerable WAF rule

Status: Workaround shipped (2026-08-07, p24-infra#5263) — openai-monitor.py and Alertmanager now call the p24-email-sender Worker via its workers.dev subdomain (§Recommended next steps, option (b) below) instead of the zone-proxied custom domain. The underlying edge WAF block on email-api.zintegrowana.online is still unresolved (option (a), a Cloudflare support ticket / plan question, has not been opened) — this only routes the two known internal callers around it. See workers/email-sender/wrangler.toml (workers_dev = true), scripts/openai-monitor.py, monitoring/alertmanager/alertmanager.yml.tpl, and .github/workflows/deploy-email-sender-worker.yml.

Trigger

A request to a Cloudflare-proxied subdomain (Worker route or otherwise) returns HTTP 403 with body Your request was blocked. and CF response headers (server: cloudflare, cf-ray, report-to: cf-nel), from a known-legitimate source (our own server IPs), regardless of User-Agent.

Known occurrence: email-api.zintegrowana.online (routes to Worker p24-email-sender) blocking bms-4 (54.36.123.110) and vps-i1 (217.154.82.162), both IPv4 and IPv6. See p24-infra#4112, #5263.

How to confirm

  1. Reproduce with headers: curl -s -D - -o body.html https://<host>/ -A "<ua>" — confirm server: cloudflare and the exact block body text (rules out an origin/Worker-side 403).
  2. Get the cf-ray value from the response, e.g. a26d8d5c8a6d66de-AMS.
  3. Query Firewall Events by ray/host+time window (GraphQL, CF_GLOBAL_API_KEY from secrets/administration.env.sops, broker via scripts/sops-invoke.ps1):
    query { viewer { zones(filter: {zoneTag: "<zone_id>"}) {
      firewallEventsAdaptive(filter: {datetime_geq: "<iso>", datetime_leq: "<iso>",
        clientRequestHTTPHost: "<host>"}, limit: 20, orderBy: [datetime_DESC]) {
        action source ruleId clientIP datetime
      }
    } } }
    datetime_geq/datetime_leq MUST be within a 1-day span — the API rejects wider ranges.
  4. Note the ruleId and source (expect source=firewallManaged).
  5. Check whether that ruleId exists in any enumerable ruleset:
    GET /zones/{zone_id}/rulesets                      # list all zone rulesets
    GET /zones/{zone_id}/rulesets/{ruleset_id}          # each one's full rule list
    GET /accounts/{account_id}/rulesets                 # account-level rulesets too
    If the ruleId is not found in any of these, it’s very likely Cloudflare’s internal “Cloudflare Specials” baseline ruleset — present on every zone (including Free plan), not listed via /rulesets, and (per this investigation) not overridable by a Free-plan zone’s Custom Rules skip action.

What does NOT fix it (confirmed 2026-08-06, Free-plan zone)

A zone Custom Rule (phase http_request_firewall_custom) with action: skip, tried as:

  • action_parameters.products: ["waf"]
  • action_parameters.products: ["waf","bic","hot","securityLevel"]
  • action_parameters.phases: ["http_request_firewall_managed"] (skip the entire phase)

All three save successfully via the API (no error) but do not stop the block at the edge.

  1. Cloudflare support ticket — reference the cf-ray IDs and the hidden ruleId, ask for either an exception or confirmation that a Pro+ plan is required for full WAF Managed Rules override. Still not opened — the zone stays on Cloudflare Free; this is a genuine human/billing decision, not something an agent can execute.
  2. Route around it — if the caller only needs the Worker and not the zone’s proxy features, point the caller at the Worker’s workers.dev subdomain instead of the zone-proxied custom domain. workers.dev requests bypass the zone’s WAF entirely (different code path), so this is a legitimate escape hatch for internal-only traffic (not for anything needing the custom domain’s TLS cert / branding). Implemented 2026-08-07 (p24-infra#5263) for the two known email-api.zintegrowana.online callers — openai-monitor.py and Alertmanager’s critical/default/warning-digest/direct-email-only receivers now use https://p24-email-sender.radieu.workers.dev/{send,alert}. workers_dev = true was added to workers/email-sender/wrangler.toml alongside the existing [[routes]] entry — the zone-proxied custom domain route is untouched, so anything still relying on email-api.zintegrowana.online directly is unaffected (and still blocked, per §What does NOT fix it above, until step 1 happens).

Verification (2026-08-07)

.github/workflows/deploy-email-sender-worker.yml smoke-tests both routes on every deploy: the custom domain is expected to keep returning 403 (unchanged, tracked here) while workers.dev must return 401 (reached the Worker, bypassed the WAF). A gh workflow run openai-monitor.yml --repo radieu/p24-infra after merge is the end-to-end check for the actual consumer.

Reference

  • p24-infra#4112 (original discovery), #5263 (duplicate/related)
  • secrets/administration.env.sops: CF_GLOBAL_API_KEY (account owner radieu@gmail.com — the CF_API_TOKEN in monitoring.env.sops lacks permission to read Firewall Rules/Rulesets)
  • docs/playbooks/p24-network-admin.md, .claude/task-playbooks/network-ops.md