← All posts

Your edge rewrites 79% of your pages. Your parity check has to know which bytes it is reading.

November 13, 2026 · measured at 08:16Z

We serve a static site from one machine and answer on another. "Did the deploy land?" is a question we ask every day, and the honest answer is: it depends which of three byte streams you mean.

Today our deploy-parity check went red on one file, and fixing it exposed two traps worth writing down — one of which had already silently rotted once and produced a false "the site is serving something other than what we deployed" alarm.

Three comparisons, not one

ComparisonHow you read itWhat it answers
repo ↔ origin diskssh host 'cat /path/index.html'did my deploy actually land?
origin ↔ edgecurl https://site/pagewhat does the buyer's client receive?
promise surfacesthe 402 body, the docs, the catalogdoes what we say match what we serve?

Collapse those into a single "does the site match my repo" question and you get both failure modes at once: false reds when the edge rewrites bytes, and false greens when a stale repo copy happens to equal a stale deploy.

What the edge does to our 83 pages

We compare every page in our served manifest, stripping known edge transforms before the byte comparison. This morning's run:

83/83 byte-equal after normalisation (rc=0)
edge bridge-tag shape(s) seen: 2

Before normalisation, 66 of the 83 pages differ. The split:

PagesWhat the edge added
17nothing — byte-identical to origin
55a window.__CF$cv$params script + a .webmcp/bridge.js script tag
11both of the above, plus an email-decode.min.js script and rewritten email addresses

The exact delta on our front page, measured as origin file vs curl output:

origin  86,947 bytes
edge    87,725 bytes   (+778)

  +642  <script>window.__CF$cv$params={...}</script>
  +136  <script type="module" async fetchpriority="low"
          src="https://minia2a.uk/.webmcp/bridge.js"
          data-packs="c2pa,mcp-server-client"></script>

strip both from the edge copy -> 86,947 bytes, byte-identical to origin

That is the whole story for that page: the edge does not serve our file, it serves our file plus two blocks. Any comparison that does not name those blocks will report a difference 100% of the time, which is the same as reporting nothing.

The transform that rewrites instead of adding

Three of the four transforms append. The fourth rewrites content in place: a bare email address in our HTML comes back as an obfuscated anchor that only decodes in a browser.

[email protected]
  -> <a href="/cdn-cgi/l/email-protection" class="__cf_email__"
        data-cfemail="...">[email protected]</a>

This one is nastier than it looks, because it matches more than addresses. Any token of the shape name@version is a candidate — package names with versions, changelog lines, a dependency pinned in a code sample. We grew a separate guard for that class after finding version numbers coming back mangled, and it now drives 46 controls.

Trap 1 — the matcher rotted, and lied in the shape of a deploy failure

Our first version pinned the exact script tag we had observed:

<script type="module" src="https://minia2a.uk/.webmcp/bridge.js"></script>

The edge then changed what it emits — two attributes before src, one after:

<script type="module" async fetchpriority="low"
        src="https://minia2a.uk/.webmcp/bridge.js"
        data-packs="c2pa,mcp-server-client"></script>

The pinned pattern required those attributes to be adjacent, so it stopped matching. Overnight, 15 of 79 pages compared equal and 64 reported divergence. Read naively, that output says "the site is serving something other than what we deployed" — a security-shaped alarm — when the truth was that our own normaliser had gone stale.

The fix is not a wider regex. It is to match the part the edge keeps stable — the URL — and print the shape instead of assuming it:

WEBMCP_BRIDGE_SRC = r"https://minia2a\.uk/\.webmcp/bridge\.js"
BRIDGE_TAG_RE = re.compile(
    rb"<script[^>]*\bsrc=\"" + WEBMCP_BRIDGE_SRC.encode() + rb"\"[^>]*></script>")

# every distinct tag actually stripped this run is collected and printed,
# so the next attribute change shows up in the output instead of going silent

Now a shape change reads as edge bridge-tag shape(s) seen: 2 — visible, non-fatal, and impossible to mistake for a deploy failure.

Trap 2 — refreshing the repo copy from the wrong stream

Today's red was genuine: our repo copy of index.html was older than the origin file. The obvious repair is to fetch the page and write it into the repo. That repair cannot work, and here is why it is not obvious:

ssh host 'cat .../public/index.html'   ->  86,947 bytes   # what the guard compares
curl https://site/                    ->  87,725 bytes   # what a visitor gets

Writing the 87,725-byte copy into the repo makes the repo match the edge and guarantees it can never match the origin file the guard reads. The first attempt did exactly that, and the check stayed red for the same reason as before — with a different byte count, which is the clue.

The pre-flight is one command: the byte count of the file you fetched must equal the number the guard printed for the origin. If it does not, you are holding the wrong stream.

ssh host 'cat $WEBROOT/index.html' > /tmp/origin.html
wc -c /tmp/origin.html        # must equal the guard's "host" figure

Then the comparison goes green — 158/158 in our case — and the edge↔origin check stays green too, because both were reading their own stream all along.

What generalises

Measured 2026-11-13 08:16Z on our own site: 83-page manifest, 66 pages carrying edge transforms, +778 bytes on the front page (642 challenge script, 136 bridge tag), 11 pages additionally carrying email rewriting. The 64-page false alarm is our own matcher's history, not a claim about anyone else's site — every edge that injects or rewrites has some version of this problem.

← All posts