Edge Routing

Cloudflare Worker latency measurements

What the NORG Edge Router Worker added to time to first byte on one Cloudflare zone, how it was measured, and how to repeat it.

1

Method

The zone is foxx.ai, served from the Sydney (SYD) Cloudflare location. Time to first byte (TTFB) was measured with curl time_starttransfer. The Worker was toggled by deleting and re-adding the zone Worker route, interleaved OFF / ON / OFF / ON, with 40 requests per phase and three OFF / ON pairs. p50 and p95 are reported in milliseconds.

The origin is a dynamic Next.js app on Vercel (cf-cache-status DYNAMIC) with a TTFB of about 1.25 s, so the results are deltas against that baseline.

2

Pass-through results

Host norg-test.foxx.ai. Means over 3 pairs, shown as p50 / p95 in ms.

Worker OFF vs Worker ON (p50 / p95, ms)
PathWorker OFFWorker ONAdded
Page "/"1245 / 14541243 / 1478p50 about 0 ms, p95 about +24 ms (within the roughly 40 ms spread between pairs)
Excluded static CSS under /_next130 / 149130 / 147about 0 ms
Per-pair raw values (p50/p95, ms)
SeriesPair 1Pair 2Pair 3
Page, Worker OFF1255/14701258/14641223/1427
Page, Worker ON1256/14941242/14981232/1441
CSS, Worker OFF129/151131/154129/142
CSS, Worker ON129/147132/148130/146
3

Mirror results

Host norg.foxx.ai, served with the agent=true query parameter. 30 requests each, alternated with the normal page in the same run, all HTTP 200, shown as p50 / p95 in ms. Mirror responses carry the header x-norg-edge: mirror and no Vercel headers.

Mirror vs normal page (p50 / p95, ms)
PathMirrorNormal page
/147 / 2441325 / 1538
/learn/development146 / 197851 / 1085
/products142 / 177417 / 477

Confirm the header before you trust a timing

The Worker's agent=true override forces the mirror path. If NORG has no mirror for that path, the request falls through to the origin, so a mirror miss looks like a normal page. In the original A/B run, all 16 sitemap pages on norg-test.foxx.ai were mirror misses; norg.foxx.ai has mirrored pages. Confirm x-norg-edge: mirror on the response before treating a timing as a mirror timing.

Mirror enablement

A Worker that has never authenticated never attempts the mirror. On the test host the stored site key did not match the key held by Norg (test environment DB refresh), so the install state was awaiting_heartbeat with no heartbeat and no deployed version recorded, and the Worker served every page from the origin. Rotating the site key (manage_edge_routing rotate_key) and writing the new key into the Worker's NORG_SITE_KEY secret restored the heartbeat and mirror serving. On this Cloudflare Worker, a rotation is not pushed to the Worker automatically and the update action does not carry a new key. Confirm the x-norg-edge: mirror response header before treating a timing as a mirror timing.

4

Fallback results

Host norg-test.foxx.ai, mirror enabled and serving, URL /learn/development?agent=true, TTFB in ms. Every response was HTTP 200 and nothing failed. The three fallback cases were served from the origin; the baseline was served by the mirror. Origin TTFB for this page is about 0.9 s, and the added delays are measured against that, not against the baseline row or the about 1.25 s origin in the Method section.

Fallback when Norg is unreachable (TTFB, ms)
CaseSamplesMedian (ms)Range (ms)Added over origin
Baseline, mirror served normallyn=8155143 to 612 (first sample cold, 612)n/a (mirror response)
Warm bot list, mirror fetch hangsn=848334686 to 6673about 3.9 s
Cold, Norg API and mirror both unreachablen=560355871 to 6095about 5.1 s
Cold, bot list answers after 4.5 s then mirror hangsn=496509552 to 9723about 8.8 s
  • Warm bot list, mirror fetch hangs: the 4 s mirror timeout, then fall through to the origin.
  • Cold, Norg API and mirror both unreachable: the 5 s bot-list timeout, then straight to the origin with no mirror attempt.
  • Cold, bot list answers after 4.5 s then mirror hangs: the 4.5 s bot-list wait plus the 4 s mirror timeout.

The visitor always gets the normal page in the end. The cost of Norg being unreachable is a one-off delay bounded by the two timeouts, and the bot list is then cached. If Norg stays down, the bot-list check repeats periodically, and requests routed to the mirror can each wait up to the mirror timeout.

Norg being unreachable was simulated by temporarily pointing the Worker's NORG_API_URL and NORG_CONTENT_BASE bindings at throwaway Workers that either never answer (30 s) or answer after 4.5 s. Cold samples used a fresh SITE_ID per sample. The original bindings were restored and verified afterwards. To repeat it:

  1. Record the current bindings first. Then repoint the Worker's NORG_API_URL and NORG_CONTENT_BASE bindings at hosts that never answer (or answer after 4.5 s).
  2. Send the complete binding list when you patch the settings, with the inherit binding type for the secret NORG_SITE_KEY, so the secret is kept.
  3. Use a fresh SITE_ID per cold sample, so the Worker's bot-list cache key misses.
  4. Restore all bindings afterwards and verify.
5

Targets and status

These are proposed targets, not guarantees.

Proposed targets and status
MeasureTargetStatus
Added TTFB for pagesat most 30 ms p50 and 60 ms p95p50 met; p95 met but not tightly resolved at this sample size
Added TTFB for excluded filesat most 5 ms p50met
Mirror TTFBat most 300 ms p50 and 600 ms p95met on norg.foxx.ai
Fallback when Norg is unreachable, warm instanceat most 4 s plus TTFBmet, about 3.9 s added
Fallback when Norg is unreachable, cold instanceat most 5 s plus TTFBmet, about 5.1 s added
Fallback, cold instance with slow bot list then mirror failureabout 9 s plus TTFBmet, about 8.8 s added
6

Caveats

  • Run-to-run p95 noise on a roughly 1.25 s origin is about 40 ms, too coarse to prove a 60 ms p95 target with 40 samples. Use 200 or more samples per arm, or a faster origin.
  • Plain curl is not an agent-verified source, so the pass-through numbers measure the non-agent path.
  • A warm agent list was assumed for the pass-through and mirror figures.
  • Mirror figures are Sydney to Sydney; cold, long-idle paths may be slower.
  • Fallback sample sizes are small (4 to 8).
7

Reproduce it

You need your own Cloudflare access to change routes; this page contains no credentials.

  1. List the zone Worker routes and note the route for the test host: its pattern (host/*), script name and request_limit_fail_open setting.
  2. To turn the Worker OFF, delete that route. To turn it ON, re-create it with the same pattern and script and request_limit_fail_open set to true.
  3. Gotcha: re-creating a route gives it a new route ID, so look the ID up fresh before every delete.
  4. Gotcha: a re-created route defaults request_limit_fail_open to false. Set it to true to match the original.
  5. Wait about 15 seconds after each toggle, then run the timed requests, alternating OFF and ON phases.
  6. Keep the /_next/* and /_vercel/* bypass routes (null script) untouched; they exclude static assets from the Worker.
  7. Always restore the route when finished.
# One phase: 40 requests, TTFB in seconds, sorted ascending.
# Run it once per OFF and ON phase, after the 15 second wait.
for i in $(seq 1 40); do
  curl -s -o /dev/null -w '%{time_starttransfer}\n' "https://{host}/"
done | sort -n > ttfb_phase.txt

# Mirror check: confirm the header before trusting a timing as a mirror timing.
curl -s -D - -o /dev/null "https://{host}/?agent=true" | grep -i '^x-norg-edge'