# `_redirects` rule shapes observed to fail — reference card

**Scope, read this first.** Every row below was observed on a **local Cloudflare
Pages emulator, `wrangler 4.65.0`**, on **2026-09-09**. It is **not** the
production Cloudflare edge, and nothing here should be read as "Cloudflare does
not support X". The dataset was a pre-registered matrix of 48 rule shapes, each
requested 3 times (144 requests), with four controls passing.

The axis that matters is not "does it work" but **"does anything tell you it
doesn't"**. A rule that fails loudly costs you ten minutes. A rule that fails
silently costs you the traffic and never shows up in a log.

---

## 1. Protocol-relative source — `//host/path`

```txt
//example.com/pricing/  /plans/  301
```

| | |
|---|---|
| Signal at deploy | **none** — counted inside wrangler's own `Parsed N valid` tally |
| Signal at request | **none** — a plain 404, as if the rule were absent |
| Observed | 12 of 12 shapes 404'd; **11 of them produced no diagnostic at any layer** |
| Fix | Drop the host. The source is a path: `/pricing/` |

This is the only shape in the matrix that was **accepted as valid and never
matched anything**. It is why this card exists.

## 2. Protocol-relative destination with status `200`

```txt
/promo/  //cdn.example.com/promo/  200
```

| | |
|---|---|
| Signal at deploy | **none** |
| Signal at request | **none — and worse, it returns HTTP 200** |
| Observed | 2 of 2 returned 200 with a body **byte-identical to the site's own root `index.html`**, not the remote host |
| Fix | A `200` destination must be a path on this site. For another host use `301` / `302` with a full `https://` URL |

Failing shapes at least fail. This one **succeeds with the wrong content**, so
a smoke test that only asserts "200 OK" passes.

## 3. Absolute-URL source — `https://host/path`

```txt
https://example.com/old-home/  /  301
```

| | |
|---|---|
| Signal at deploy | **named**: `Only relative URLs are allowed. Skipping absolute URL …` with the line number |
| Signal at request | 404 |
| Observed | 12 of 12 skipped and 404'd, all 12 named at parse time |
| Fix | Drop the scheme and host |

## 4. Cross-origin destination with status `200`

```txt
/docs/  https://docs.example.com/  200
```

| | |
|---|---|
| Signal at deploy | **named**: `Proxy (200) redirects can only point to relative paths. Got …` |
| Signal at request | 404, with the same body length as the site's own not-found page |
| Observed | 2 of 2 |
| Fix | Cross-origin needs a redirect, not a proxy: `301` or `302` (both worked), or proxy to a local path |

Cloudflare documents this one ("Proxying will only support relative URLs on
your site"). What the docs do not say is what the request layer then does,
which is: a normal-looking 404.

---

## Worth knowing, not a defect

Omitting the status code took the documented default wherever the rule could
match at all: **6 of the 12** omitted-status rules in the matrix answered
`302` (r04, r08, r12, r16, r20, r24 — every one of them with a plain or splat
source). The other 6 404'd for their source shape, not for the missing status:
absolute-URL sources r28, r32, r36 were rejected at parse time (section 3),
while protocol-relative sources r40, r44, r48 were counted as valid and then
never matched (section 1). If the move is permanent, write `301` — a `302`
asks search engines to keep the old URL.

## What this card does NOT cover

Splats (`:splat`), placeholders (`:name`), query strings, rule ordering and
shadowing, the per-file rule limits, and behaviour on the production edge were
**not** in the matrix. Absence from this card is not evidence of safety.

---

Reference card for the AtomStorm `_redirects` Rule Linter —
atomstorm.ai/en/tools/redirects-rule-linter/
