Install NORG edge routing in front of an existing Cloudflare Worker
This document sets out how to give verified AI assistants a fast, text-first mirror of every public page in the site’s sitemap at its own {domain} address, by installing one new Cloudflare Worker in front of the Worker that already routes the zone. People and search engines keep receiving the existing site through the existing router, with no change. Edge routing reference: the white paper.
- the site owner
- The organisation that runs the site and its Cloudflare account, and approves the install.
- the existing router
- The Worker that already answers {domain} and routes by path to one or more origins (origin A, origin B). It is attached in one of two ways: as a Workers Custom Domain, or by a {domain}/* route.
- Workers Custom Domain
- A hostname attached to a Worker as its origin; Cloudflare manages its DNS record. A route Worker on the same hostname runs first, and the NORG Worker hands every other request to the Custom Domain Worker through the ORIGIN Service Binding.
- route-attached
- The existing router owns a {domain}/* route. One Worker runs per route, and a same-zone fetch() from a route Worker cannot reach another route Worker, so the route is moved to the NORG Worker, which reaches the router through the Service Binding.
- Service Binding
- A private connection between two Workers in the same account, used in both set-ups. The NORG Worker reaches the existing router through one named ORIGIN, with no public URL, no second billed request and no reliance on which Worker takes precedence.
- record-only mode
- A trial posture (a NORG capture_only install) in which the Worker changes nothing for anyone and only reports AI-agent visits after the page has been delivered.
- verified agent
- A request whose user agent is on NORG’s list AND whose source address falls inside that operator’s published ranges. A user agent alone is never trusted.
- kill switch
- The EDGE_DISABLED Worker variable. Set to true, every request goes straight to the existing router within seconds.
Objective
One new Worker, installed first in record-only mode (a NORG capture_only install that changes nothing for anyone), then switched to serving once the site owner approves.
- Deploy the NORG Edge Router Worker to the account, with no route, in record-only mode. It observes and changes nothing; every request is handed to the existing router, and each AI-agent visit is recorded after the page is delivered.
- Put it in front of the existing router. In both set-ups the new Worker gets a Service Binding named ORIGIN to the router. When the router is a Workers Custom Domain (the expected case), add a {domain}/* route for the new Worker: nothing moved, and deleting the route undoes it. When the router owns the {domain}/* route instead, move the route to the new Worker after the binding is in place; to undo, move the route back.
- Only if needed: one WAF skip rule for AI-agent user agents and the health probe, if the acceptance checks show them being challenged.
Which set-up the zone has
How the existing router is attached decides the install. Finding out is the first install step, before anything is changed.
The install adds one route, {domain}/*, for the NORG Worker. Cloudflare runs a route Worker before a Custom Domain Worker on the same hostname and treats the Custom Domain Worker as the origin (Cloudflare: Custom Domains, interaction with Routes). The NORG Worker reaches it through a Service Binding named ORIGIN, and nothing is moved: the existing router and its Custom Domain are untouched. The binding is a private call by name, so there is no second public request and no reliance on precedence. To undo, delete the NORG route.
Verified by NORG on a live zone whose router is a Custom Domain Worker, running NORG Worker 0.15.0 with the ORIGIN binding: people received the Custom Domain Worker’s response with its headers unchanged, and an ?agent=true review request received the mirror.
Cloudflare runs one Worker per route, and a same-zone fetch() from a route Worker does not reach another route Worker; it goes to the origin in DNS (Cloudflare: Worker to Worker communication). Here the NORG Worker is given the same Service Binding named ORIGIN to the existing router first, then the route is moved from the router to the NORG Worker. To undo, move the route back.
- In the Cloudflare dashboard: Workers & Pages → the existing Worker → Settings → Domains & Routes. A Custom Domain entry for {domain} is the Custom Domain set-up; a {domain}/* route is the route-attached set-up.
- By API: GET /accounts/{account_id}/workers/domains lists every Workers Custom Domain with its hostname and Worker; GET /zones/{zone_id}/workers/routes lists the zone’s routes.
- Check separately whether a {domain}/* route already exists on the zone. If one does, whatever else is attached, the Worker on that route is the existing router and the route-attached steps apply.
Current state
One Worker on the zone already routes every request by path: some paths to origin A, the rest to origin B. Every visitor, human or machine, receives the same page.
Figure 1 — today: one router for everyone
Target state
One Worker is added in front of the existing router on the same zone. Everything to the right of the router is unchanged; blue marks what is added in the site owner’s account. Both set-ups hand off to the router through the ORIGIN binding; only how the route is attached differs.
Figure 2 — expected: the router is a Custom Domain, reached through ORIGIN
Figure 3 — alternative: the router owned the route, reached through ORIGIN
A Worker that does a same-zone fetch(request) to its own hostname sends the request back out: when the router is on a route, that fetch goes straight to the origin in DNS and skips the router; when the router is a Custom Domain, it is a second public request that relies on route precedence to land. The Service Binding (one named setting, ORIGIN) is a private call from the NORG Worker to the router by name, inside Cloudflare, so the router’s path split, caching and headers are unchanged.
Custom Domain: the NORG route is added beside the Custom Domain, nothing is moved, and undoing the install means deleting the NORG route. Route-attached: the binding is added before the route is moved from the router to the NORG Worker; to undo, move the route back to the router.
| Item | Router on a Custom Domain (expected) | Router on a route (alternative) |
|---|---|---|
| Route {domain}/* | Added, pointing at the NORG Worker | Moved from the existing router to the NORG Worker |
| The existing router’s Custom Domain | Unchanged | Not applicable |
| Service Binding | ORIGIN: NORG Worker to the Custom Domain Worker; nothing is moved | ORIGIN: NORG Worker to existing router (added before the route moves) |
| Route failure mode | Set to fail open, or be on Workers Paid | Set to fail open, or be on Workers Paid |
| Workers on the account | Router and NORG Worker | Router and NORG Worker |
| Worker variables and secret | SITE_ID, CAPTURE_ONLY, EDGE_DISABLED; secret NORG_SITE_KEY | Same |
| WAF custom rule | One skip rule, only if acceptance checks show agents or the probe challenged | Same |
| Bot settings | Confirm “Block AI bots”, managed robots and classic Bot Fight Mode are off | Same |
| DNS, SSL/TLS, cache rules | None | None |
| Existing router code and origins | None | None |
| To undo | Delete the NORG route | Move the route back to the existing router |
How the Worker decides
Record-only first (a NORG capture_only install), then serving once the site owner approves. Each request is checked cheapest-first; the first match decides, and “router” means the request is handed to the existing router unchanged.
| Request | Path at the edge | Response |
|---|---|---|
| Person (browser) | NORG Worker → existing router (ORIGIN Service Binding) → origin A or B | Today’s page, unchanged |
| Search crawler | Same as a person | Today’s page, unchanged |
| Assets and non-GET | Same as a person | Unchanged |
| Verified AI agent, record-only | Same as a person; visit recorded | Today’s page |
| Verified AI agent, serving | NORG Worker → NORG content host | Mirror, private, no-store |
| Claimed AI agent outside the ranges | Same as a person; recorded as unverified | Today’s page |
| Gate | Check | Outcome |
|---|---|---|
| 1 | Kill switch EDGE_DISABLED is true | Router |
| 2 | Loop-guard header present | Router |
| 3 | Health-probe header carrying the Site Key | Valid key: JSON status; otherwise router |
| 4 | Not GET or HEAD, or a static path or asset extension | Router |
| 5 | Search-crawler user agent (Googlebot, Google-InspectionTool, Bingbot, Applebot, DuckDuckBot, Baiduspider, Yandex, Slurp) | Router |
| 6 | Agent list unavailable, or the site not active in NORG | Router |
| 7 | User agent not on the agent list | Router |
| 8 | Source address outside the operator’s published ranges and not a Cloudflare-verified bot | Router; recorded as unverified |
| 9 | Record-only mode | Router; visit recorded |
| 10 | Fetch the mirror for the path | 200: mirror. 404: render requested, router. Error, oversize or timeout: router |
Cache-Control: private, no-store and CDN-Cache-Control: private, no-store; X-Norg-Edge: mirror; a canonical Link header to the page’s own {domain} address; status 200 at the same URL, with no redirect.
Any unexpected error also ends at the router. The kill switch EDGE_DISABLED sends every request straight to the existing router within seconds. A review override can show the mirror for a path to anyone, so the site owner can review mirrors before serving is switched on.
Mixed-stack sites
Some sites serve the home route from one front-end stack and every inner route from a separate framework and application server behind the same {domain}. The install records exactly one platform, so a mixed-stack site needs its exclusion list set by hand to cover both stacks.
A mixed-stack site answers different paths from different front-end stacks behind one {domain}. The home route is served by one stack — here Next.js — and the inner routes by a separate framework and application server with a headless CMS behind them; a router Worker on the Custom Domain splits by path. The two stacks are told apart from their response headers: x-powered-by and the x-routed-via routing marker carry different values on / than on an inner route. A curl -s -D - -o /dev/null (a GET that prints only the headers) of the home route and one inner route shows the split.
| Stack | Response-header signal | What the install records |
|---|---|---|
| Home route (Next.js) | x-powered-by: Next.js, plus the hosting platform’s own x- response headers; x-nextjs-* and the x-routed-via value for / are also visible | The one platform the install records |
| Inner routes (a separate framework and application server) | A different x-powered-by value naming its own server, and a different x-routed-via value | Served from the same host but not what the install keys on |
The install probes the site once and records a single platform (the one its probe resolves), so the other stack’s own-host assets are not excluded automatically. Set the route-exclusion list to the union of what each stack serves from the site’s own host: the Next.js build output under /_next/* together with the inner stack’s own-host static and bundle paths. See how the Worker decides — gate 4 sends a static path or asset extension straight to the router — for where these exclusions take effect.
Only requests to the site’s own {domain} reach the Worker. Media served from a separate admin host, and third-party analytics or scripts loaded from other hosts, resolve to those hosts directly and never pass through the route — so neither needs an exclusion.
Mirrors are built from the public pages each stack already serves. Neither front-end application is modified: the Worker reads the rendered public page and serves a text-first mirror only to verified AI agents, while people and search crawlers receive each stack’s own response unchanged.
How data flows
Three flows. Only the verified-AI-agent flow serves something different, and every failure in it ends at the normal page. Control traffic is outbound HTTPS only, from the Worker to NORG’s API and content hosts; nothing inbound is opened to any site-owner system.
Figure 4 — three flows at the edge
The crawler reads public pages only: no CMS, API or database access. CMS edits reach the mirrors at the next re-crawl or on a manual re-render.
Cost
A small running cost. The hand-off to the existing router is a Service Binding call in both set-ups, which adds no billed request. The CPU of both Workers is billed.
The Workers Paid upgrade guide carries the current pricing table. In short: the Free plan allows 100,000 requests a day account-wide with 10 ms CPU per invocation; the Paid plan has a monthly minimum and includes a request and CPU allowance, with a small charge per extra million of each.
Above the Free cap a route fails open (the Worker is bypassed) or fails closed (an error page), depending on the route’s failure mode (Cloudflare: daily requests). NORG’s API-token install sets fail open on the routes it creates, but that applies only to those routes. A route created by hand or by API comes back fail closed (request_limit_fail_open: false), so for this install the route’s failure mode is set to fail open, or the account is on Workers Paid.
In both set-ups the NORG Worker reaches the router through the ORIGIN Service Binding, and Cloudflare documents that Service Binding calls carry no additional request fee; the CPU of both Workers is billed (Cloudflare: Workers pricing, Service bindings).
A plain fetch() from the NORG Worker to a Custom Domain Worker would instead likely be billed as a second Worker request, because the Custom Domain Worker is invoked as an origin. That is one reason the install binds the router rather than fetching it.
Model the running cost from the site’s own Cloudflare Analytics: worker requests per page view, plus an allowance for crawlers and agents, at roughly 1 ms CPU per request per Worker. Mirror storage, crawling and model processing are borne by NORG.
Route exclusions
Asset paths can be carved out of the Worker with no-Worker routes, trimming the NORG Worker’s CPU from those requests. Whether billed requests change, and whether an exclusion is safe at all, depends on the set-up (below).
Scripts, stylesheets, fonts and images are not pages an agent reads, so they need no mirror and nothing is lost by leaving the Worker out of them. An exclusion is one route with no Worker, placed beside the main {domain}/* route. The more specific route wins, so the excluded path is served straight from the origin while every page still goes through the NORG Worker.
Route matching includes the query string, so a path with no trailing wildcard matches only that exact URL. To also catch a cache-busted request such as /app.js?v=123, end the route with a wildcard (/app.js*); the prefix sets below (such as /dist/*) already do this.
Start from the paths the site actually serves: look at the same-host requests a page makes and exclude the asset prefixes, not the page paths. Two common shapes:
| Site shape | Suggested no-Worker routes |
|---|---|
| A framework that serves its build output and static assets under one path prefix | The framework asset prefix (e.g. /_next/*) plus the hosting platform’s own insights/analytics asset prefix |
| A site that serves individually named static files | /app.js*, /index.css*, /dist/*, /images/*, /favicon.ico, /api/* |
- Page paths — the HTML an agent reads is exactly what the mirror serves, so a page path must stay on the main route.
- NORG agent paths such as /mcp and /llms.txt — excluding them cuts agents off from the site’s machine-readable surface.
- Catch-all HTML paths — an over-broad prefix such as /products/* shadows every page beneath it, so no page there is ever mirrored.
When the existing router keeps its own Custom Domain — the expected set-up — exclusions do not reduce billed requests. An excluded request is a no-Worker route for NORG, but the router still answers it as the Custom Domain origin exactly as before, so it is billed exactly as before. What an exclusion removes is the NORG Worker (about 1 ms of CPU) from those requests, not the request itself.
The route-attached set-up is different: the router’s {domain}/* route was moved to the NORG Worker, so the router has no route of its own. A no-Worker exclusion there runs neither Worker, and the request goes straight to the DNS origin — bypassing the router. That can break any asset or API path that relies on the router’s origin selection or headers, so do not add no-Worker exclusions in the route-attached set-up unless bypassing the router is intended.
Both differ from a plain install, where the NORG Worker is the only Worker on the hostname and a no-Worker route means the request is not billed at all.
Adding a path later is one more no-Worker route beside the others; it takes effect at the edge within the usual refresh. Rollback is deleting the no-Worker route: the path then goes through the main {domain}/* route again, exactly as before.
Where data is stored
Everything that is true of every NORG install — mirror storage, the site record and AI-agent visit analytics, data residency and retention — is on the solution-architecture page.
The existing router and the NORG Worker code live in the site owner’s own Cloudflare account; the site owner can disable, unroute or delete either at any time. The Site ID is a Worker variable and the Site Key an encrypted Worker secret, not readable after it is set, including by NORG. NORG holds no private site data, customer records, form submissions, cookies or email.
For where mirrors and analytics are stored, how the Site Key is held and how long data is kept, see the solution architecture. NORG reaches two hosts, both outbound HTTPS only: content-craft-api.norg.ai (control plane) and edge-content.norg.ai (the mirror per verified agent visit).
Installation and access
The first step is finding out how the existing router is attached; nothing is changed until that is known. In the route-attached set-up the binding is added before the route moves, so the NORG Worker is never live without a path to the router. Not required: DNS changes, a new hostname, a zone plan change, or application changes.
- Find out which set-up the zone has (see “Which set-up the zone has”): Workers & Pages → the existing Worker → Settings → Domains & Routes, or GET /accounts/{account_id}/workers/domains, and check whether a {domain}/* route already exists.
- NORG creates the site record and issues the Site ID, a one-time Site Key and the wrangler configuration. Both set-ups use the standard pinned Worker (version 0.15.0 or later), which honours the ORIGIN binding.
- The site owner confirms the account’s Workers plan; the existing router’s script name, how it is attached, and its maintainer; and the zone’s bot settings.
- Deploy the NORG Worker with no route, in record-only mode, with the Site Key secret and the ORIGIN binding to the existing router (both set-ups). Nothing changes on the site.
- In an agreed change window, attach the NORG Worker. Custom Domain: add a {domain}/* route pointing at the NORG Worker; the Custom Domain stays as it is. Route-attached: edit the existing {domain}/* route to point at the NORG Worker. Either way, set the route’s failure mode to fail open unless the account is on Workers Paid.
- Run the acceptance checks (appendix). If any fails, delete the NORG route (Custom Domain) or move the route back to the existing router (route-attached).
- Only if needed: the WAF skip rule for AI-agent user agents and the health probe.
- After the record-only period and the site owner’s approval, switch to serving for the agreed operators.
DNS: nothing. The Cloudflare account — the Worker, secret, route, the ORIGIN binding, WAF rule and later approved updates — is done by a site-owner administrator (recommended) with NORG on a call, or by NORG with a time-limited token. Hosting, CMS and application code: nothing.
If NORG installs, the minimum token is account-scope Workers Scripts: Edit and, on this one zone only, Zone: Read, DNS: Read and Workers Routes: Edit. Zone WAF: Edit and Bot Management: Edit are added only if NORG is also to add the WAF skip rule or change bot settings. Cloudflare has no per-script scope, so even this token could change any Worker on the account, including the router; a token scoped to every zone, or with other account permissions, is far broader than this install needs. To contain it, restrict the token with Client IP Address Filtering to the egress addresses NORG publishes (see the install guide’s token section), give it a short lifetime, and delete it after installation.
If no token will be issued at all, the install still proceeds in one of three ways, and any secret changes hands by a single-view link only:
- Guided screen-share: a site-owner administrator makes each change in the Cloudflare dashboard while NORG guides them through it; NORG never touches the account.
- Read-only staging copy: NORG shares its staging account read-only, so the administrator can see the finished set-up and copy it into their own account.
- Single-view link: any secret (the Site Key, or a token if one is issued) is passed as a single-view, one-time link, never by email or chat.
- Rotating the Site Key returns the new key once. The deployed worker keeps the old key and is rejected with a 401 by /api/v1/edge/* immediately.
- The update action does not push the new key — the NORG_SITE_KEY secret is inherited, not rewritten.
- The site keeps serving its normal page (the worker fails open); only mirror serving and the heartbeat stop, until the deployed secret matches the new key.
- Recover by setting NORG_SITE_KEY to the new value by hand in the customer’s Cloudflare dashboard, or by uninstalling then installing again.
- Confirm recovery with the Site Key health probe (a JSON reply means the worker holds the new key), then a mirror check that an AI-agent request returns X-Norg-Edge: mirror.
Security and governance
The design fails safe, never cloaks, and serves only verified agents.
- Fails safe: every error, timeout, missing mirror or NORG outage hands the request to the router; people never wait on NORG.
- No cloaking: search crawlers on a fixed list always receive the same page as people.
- Verified agents only: a browser identifying as an AI agent gets the normal page; the source address must be in the operator’s published ranges.
- No cache contamination: mirrors are private, no-store.
- Minimal data handling: the Worker reads method, path, user agent, source address and Cloudflare’s network fields; never cookies, authorisation headers, form data or bodies.
- The site owner approves every change: the code cannot update itself; any later change needs a new submission and a new token or an administrator to deploy.
Rollout and rollback
Each stage moves on only with the site owner’s agreement, and every stage is reversible within seconds.
| Rollback option | Effect | Takes effect |
|---|---|---|
| Set EDGE_DISABLED to true | Every request goes to the router | Seconds |
| Custom Domain: delete the NORG route | Exactly today’s set-up; the existing router and its Custom Domain were never changed | Seconds |
| Route-attached: move the route back to the router | Exactly today’s set-up | Seconds |
| Delete the Worker | Removed from the account | Immediate |
| NORG-side revocation | Worker stops serving mirrors | Next hourly refresh |
Risks
Each risk and how the design mitigates it, and the decisions the site owner settles before an install.
| Risk | Mitigation |
|---|---|
| Path split broken by the new Worker | The ORIGIN binding hands every other request to the existing router in both set-ups; the acceptance checks confirm the router’s headers, and the change is undone in seconds |
| Wrong set-up assumed | The set-up check is the first install step; a Custom Domain install never moves a route, and a route-attached install never goes live without the binding |
| Hand-made route fails closed | A route created by hand or by API defaults to fail closed; set it to fail open, or be on Workers Paid |
| SEO impact | Search crawlers always get the human page; canonical URLs unchanged; mirrors never cached |
| Workers Free plan cap | Fail open on the NORG route; Workers Paid removes the cap |
| The bound Worker is later deleted or renamed | The NORG Worker falls back to the origin in DNS; the binding is shown on the site’s status so it can be re-pointed |
| NORG outage or slow response | Fail-safe to the router; short timeout; people never call NORG |
| Spoofed AI agents | Source-address verification against published operator ranges |
| Broad API token | The site owner installs, or a short-lived token limited to NORG’s addresses and deleted after |
| Mirrors out of date after CMS edits | Scheduled re-crawl and manual re-render; missing mirrors fall back to the live page |
- Who installs: the site owner’s team, or NORG with a short-lived, address-limited token.
- Confirm the account’s Workers plan.
- Confirm how the existing router is attached (Custom Domain or route), its script name and its maintainer.
- The change window and the length of the record-only period.
Appendix
The deployment file and the acceptance checks. The wrangler configuration is deployed first with no routes line; the route is then added (Custom Domain) or moved (route-attached) in the dashboard, with its failure mode set to fail open. The secret is set separately with wrangler secret put NORG_SITE_KEY.
The worker source is not inlined. The standard worker is the pinned public repository norgai/norg-edge-mirror-cloudflare-deploy. From version 0.15.0 the standard pinned worker honours the ORIGIN binding, so both set-ups use it as it is; there is no separate Worker file.
name = "norg-edge-router"
main = "src/edge-router-worker.js"
compatibility_date = "2026-10-01"
workers_dev = false
[vars]
CAPTURE_ONLY = "true" # "false" when serving is approved
EDGE_DISABLED = "false" # "true" = kill switch
SITE_ID = "ISSUED-BY-NORG"
[observability]
enabled = trueBoth set-ups — add the ORIGIN Service Binding before the route is added or moved:
# Both set-ups: everything this Worker does not serve goes to the existing
# router, whether it is a Workers Custom Domain or the Worker whose route moved.
[[services]]
binding = "ORIGIN"
service = "EXISTING-ROUTER-SCRIPT-NAME" # the site owner confirms thisRun straight after the route is added or moved. Any unexpected result: delete the NORG route (Custom Domain) or move the route back to the router (route-attached).
# These checks send a GET and read the response headers (-D - -o /dev/null),
# never a HEAD. The Worker forwards a HEAD straight to the origin, so a
# HEAD check can look clean even when serving is broken.
# 1. People: same routing as today, no NORG header
curl -s -D - -o /dev/null https://{domain}/ | grep -iE "x-norg-edge"
# expect: no X-Norg-Edge; the router's own headers unchanged
# 2. Search crawler user agent: identical to people
curl -s -D - -o /dev/null -A "Mozilla/5.0 (compatible; Googlebot/2.1)" https://{domain}/ | grep -iE "x-norg-edge"
# expect: no X-Norg-Edge
# 3. Spoofed AI agent from an office address: normal page
curl -s -D - -o /dev/null -A "GPTBot/1.2" https://{domain}/ | grep -iE "x-norg-edge"
# expect: no X-Norg-Edge
# 4. Health probe (Site Key held by the installer)
curl -s -H "x-norg-edge-check: $NORG_SITE_KEY" https://{domain}/
# expect: JSON with site_id, version, disabled, capture_only, entitled and ts.
# capture_only is true in record-only mode and false in serving mode.
# entitled can read false on a Worker instance that has not yet handled a
# real request, so send a normal request (check 1), then probe again; if it
# stays false after real traffic, check the Site Key secret. Scripted checks
# can be challenged by Cloudflare bot-challenge settings (Bot Fight Mode).
# 4b. Health probe with a wrong key: the normal page, no JSON status
curl -s -D - -o /dev/null -H "x-norg-edge-check: wrong-key" https://{domain}/ | grep -iE "x-norg-edge"
# expect: no X-Norg-Edge; a wrong or missing key returns the normal page, not the JSON
# 5. Mirror review (once NORG has built the mirrors)
curl -s -D - -o /dev/null "https://{domain}/some-page/?agent=true" | grep -iE "x-norg-edge|cache-control"
# expect: X-Norg-Edge: mirror and Cache-Control: private, no-storeMeasuring added latency
How to measure the latency the Worker adds, how to read p50 and p95, and what counts as a reasonable target. The targets are proposals to check against on a given site, never a guarantee, and the cases this method does not measure are stated.
Added latency is measured by comparing time-to-first-byte (TTFB) with and without the NORG Worker in the path, from a location outside the origin's region, 40 requests per case. The cold case uses a unique query string per request to force a cache MISS; the warm case primes one URL and then re-requests it. Router-only and with-worker runs are alternated so the baseline and the measured path see the same network conditions. Mirror cases append ?agent=true because the test location is not inside an operator's published source ranges, so that is the only way to pull the mirror from outside those ranges.
Cold and warm sampling, then the percentile read:
# Measure from OUTSIDE the origin's region. 40 requests per case; TTFB is read
# as curl's %{time_starttransfer}.
# Cold cache (MISS): a unique ?cb per request so the edge never serves a warmed
# copy. 40 samples, sorted ascending.
for i in $(seq 1 40); do
curl -s -o /dev/null -w '%{time_starttransfer}\n' \
"https://{domain}/some-page/?cb=$i"
done | sort -n > ttfb_cold.txt
# Warm: prime the URL once, then take 40 samples of that same primed URL.
curl -s -o /dev/null "https://{domain}/some-page/"
for i in $(seq 1 40); do
curl -s -o /dev/null -w '%{time_starttransfer}\n' \
"https://{domain}/some-page/"
done | sort -n > ttfb_warm.txt
# p50 (median) and p95 from a sorted 40-sample file: the 20th and 38th values.
sed -n '20p;38p' ttfb_cold.txtThe router-only baseline and the mirror variant:
# Router-only baseline: the SAME 40-request method with the NORG route removed
# (or EDGE_DISABLED=true), so every request goes straight to the existing
# router. Alternate a router-only run and a with-worker run to average out
# network noise.
# Mirror: the test location is outside the operator's published source ranges,
# so append ?agent=true to pull the mirror a verified agent would receive.
for i in $(seq 1 40); do
curl -s -o /dev/null -w '%{time_starttransfer}\n' \
"https://{domain}/some-page/?agent=true&cb=$i"
done | sort -n > ttfb_mirror.txtp50 (the median, the 20th of 40 sorted samples) is the typical figure; p95 (the 38th) is the tail a slow request falls into. Added latency for people is the with-worker TTFB minus the router-only baseline at the same percentile — compare p50 with p50 and p95 with p95, never a median against a tail. The mirror figure is read directly from its own run; it is a served document, so there is no router-only number to subtract.
Proposed, not guaranteed: for people, added latency at the median within a low tens-of-milliseconds band and a p95 that stays close to it; for a mirror, a served TTFB well under the origin's typical page TTFB. These are proposals to measure against on a given site, not a commitment — every site's origin, region and CDN settings differ.
One observed staging run (40 requests per case, from outside the region) saw the Worker add roughly −7 to +15 ms at the median for people and a mirror served in about 140 ms cold and warm. That is a single observation on one site, not a guarantee of what any other site will see.
Unmeasured. This method does not cover warm mirrors under concurrent load, other regions or client networks, the first request a cold Worker instance serves, or TTFB once a page body is streaming. State which of these a given run did not exercise rather than implying the single-URL figures hold everywhere.
