Skip to content

Commit b487b1f

Browse files
Merge issue-196-spec02-query-split: record the selector/ride-along query split in spec-02 (#196)
2 parents 397bcd7 + d7fe0a8 commit b487b1f

1 file changed

Lines changed: 27 additions & 12 deletions

File tree

_docs/specs/02-url-link-seo-compatibility.md

Lines changed: 27 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,8 @@ slug ascending as deterministic tie-breakers; genuine gaps and duplicates are pr
7878
`/podcast` path is a moving latest-season representation selected from the maximum validated numeric
7979
season, never a hard-coded season.
8080

81-
The only accepted query is one exact ASCII `season=[1-9][0-9]{0,8}` value. An existing older season
81+
The only selector the catalogue reads is one exact ASCII `season=[1-9][0-9]{0,8}` value; every
82+
other parameter in the raw query rides along under the split recorded below. An existing older season
8283
uses `/podcast?season=N`, an exact self-canonical, the title
8384
`DataTalks.Club Podcast — Season N — DataTalks.Club`, and sequence relations in descending catalogue
8485
order: `prev` targets the adjacent newer actual season and `next` targets the adjacent older actual
@@ -92,12 +93,20 @@ in numeric descending order, visibly name each `Season N`, mark the current cont
9293
`Newer season — Season 13` and `Older season — Season 11`. A response never combines seasons or
9394
invents a missing one.
9495

95-
Duplicate, empty, signed, zero, leading-zero, encoded, non-ASCII, overlong, alternate-case, mixed,
96-
unknown, former `page`, or otherwise non-exact query forms return bounded non-reflective `400`
97-
responses with `no-store`. An exact normalized positive season absent from the actual inventory
98-
returns a bounded non-reflective `404` with `no-store`, without a latest/nearest fallback. `GET` and
99-
`HEAD` have identical status and metadata behavior, with an empty HEAD body. Anonymous `POST` is
100-
rejected before catalogue/query work with `405`, exact `Allow: GET, HEAD`, and `no-store`.
96+
Commit `643ea32` (2026-08-17, recorded in #196) split the raw query before the season grammar
97+
reads it, which deliberately amends the earlier whole-query refusal: the `season` selector is kept
98+
and handed to the grammar unchanged, and every other parameter — an unknown key, a campaign tag
99+
such as `utm_source`, the former `page` parameter, an alternate-case spelling, or a mixed query —
100+
is dropped, never reflected or forwarded, so a tagged URL such as `/podcast?page=2` serves the
101+
byte-identical clean page declaring the clean canonical and never becomes an unbounded variant.
102+
The grammar stays exactly as strict about what it does read: duplicate, empty, signed, zero,
103+
leading-zero, encoded, non-ASCII, overlong, or otherwise non-exact `season` forms return bounded
104+
non-reflective `400` responses with `no-store`. The whole raw string stays length-bounded, so an
105+
enormous query is refused rather than parsed. An exact normalized positive season absent from the
106+
actual inventory returns a bounded non-reflective `404` with `no-store`, without a latest/nearest
107+
fallback. `GET` and `HEAD` have identical status and metadata behavior, with an empty HEAD body.
108+
Anonymous `POST` is rejected before catalogue/query work with `405`, exact `Allow: GET, HEAD`, and
109+
`no-store`.
101110

102111
The `.html` and slash hub aliases preserve the raw query byte-for-byte in their one-hop redirects.
103112
Season-query hub URLs stay out of the sitemap, whose podcast portion remains exactly clean
@@ -200,12 +209,18 @@ sitemap. Neither robots nor cache status is an authorization control.
200209
## Query and poisoning rules
201210

202211
- Static, detail, feed, and sitemap cache keys contain no query parameter. A named hub may allow
203-
one exact registered positive-integer selector; Podcast allows only normalized `season`.
204-
Duplicate, empty, overlong, out-of-range, malformed, former `page`, or unknown parameters become
205-
no-store or a safe 400, never an unbounded variant.
212+
one exact registered positive-integer selector; Podcast allows only normalized `season`. Since
213+
`643ea32` (#196), a raw query is split before any grammar reads it: the parameters a hub actually
214+
selects on are kept — a duplicate, empty, overlong, out-of-range, or malformed selector still
215+
becomes no-store or a safe 400 — and every other parameter, including former `page` and unknown
216+
or tracking keys, is dropped, never reflected or forwarded, so a tagged URL collapses onto the
217+
clean canonical, never an unbounded variant. The whole raw string stays length-bounded.
206218
- Known tracking keys may be removed by one safe canonical `GET`/`HEAD` redirect. They are not
207-
reflected or forwarded while absent from the key. Search text and arbitrary filters are not
208-
cached in the MVP.
219+
reflected or forwarded while absent from the key. `643ea32` (#196) records why no allowlist of
220+
tracking parameters exists: every tool invents its own, so a list of the ones we happen to know
221+
would just move the breakage to the next vendor. Dropping every non-selector parameter is the
222+
allowlist-free form of the same not-reflected, never-a-variant intent. Search text and arbitrary
223+
filters are not cached in the MVP.
209224
- Host, User-Agent, Referer, Accept-Language, CloudFront country, `X-Forwarded-*`, viewer-supplied
210225
internal headers, and arbitrary cookies are not public cache-key inputs. Accept-Encoding uses
211226
CloudFront's gzip/Brotli normalization rather than raw viewer values.

0 commit comments

Comments
 (0)