Neaptide canonical experiment — 4 October 2026

Question
Which configured metadata values change the emitted canonical host or path?

Design
Eight predefined cases in protocol.json; one unchanged control and seven
deliberately incorrect configurations. Each case has before/after routes.
The before and after fixtures coexist in one production build, so the runtime
and dependencies are identical. Each pair changes one metadata setting.

Environment
Next.js 15.5.22, React and React DOM 19.2.6, Node.js v25.2.1, App Router.
Static metadata exports. Local next build / next start, not a Vercel deployment.
Build environment: LAB_CANONICAL_ORIGIN=https://preview.example.com
Telemetry disabled with NEXT_TELEMETRY_DISABLED=1.

Observed evidence
32 ordinary HTTP GET responses: 16 URLs, two collection passes.
All responses returned 200 and exactly one canonical element in head.
Both HTTP passes agreed. The rendered DOM on all 16 routes agreed with HTTP.
One initial configuration matched its declared intended URL (the control).
All eight after configurations matched; the control was unchanged.
The seven initial mismatches were deliberately introduced, not discovered
in a survey. These numbers are not prevalence, ranking or improvement rates.

URL convention
/cXX/before and /cXX/after are fixture namespaces, not intended public product
paths. All canonical targets use reserved example.com demonstration domains.
The target URL is declared per case in protocol.json. No request is made to
those example domains. The fixture itself carries noindex, nofollow.

Reproduction from the downloadable archive
1. Use the recorded Node version if reproducing the original environment.
2. cd fixture
3. npm install
4. LAB_CANONICAL_ORIGIN=https://preview.example.com NEXT_TELEMETRY_DISABLED=1 npm run build
5. npm run start -- -p 8780 -H 127.0.0.1
6. In another terminal, from the archive root: python3 collect.py http://127.0.0.1:8780
7. Open http://127.0.0.1:8780 and inspect the 16 linked pages. In browser
   developer tools, compare link[rel="canonical"] in the rendered document
   with the response HTML. Record any difference, including where it appears.

The archive pins direct framework versions. Its package.json is not a complete
lock of every transitive dependency; record your installed versions and any
result differences. The original fixture-hashes.json identifies source files.
collect.py writes new observations and analysis in the extracted directory;
the original results are included separately in the archive under original/.
The collector does not automate browser checks: those are a separate step.

Scope and limitations
No Search Console, authentic Googlebot requests, Google-selected canonical,
rankings, traffic, conversions or live client incidents were measured.
No claim covers other Next.js versions, Pages Router, dynamic/streamed
metadata, middleware, a CDN rewriting HTML, or hosting-platform behavior.
Repeated reads are not independent sites or statistical replication.
Browser inspection and collection were performed by the same researcher.

Pre-analysis build repair
The first fixture build stopped on a navigation lint rule. Its index links
were changed from a elements to Next.js Link. This did not change the frozen
metadata cases. The subsequent build passed before observations were taken.

Supplementary reproduction — 13 October 2026
The October 4 fixture was identified by SHA-256 source hashes, not a Git commit.
For this additional run, its identical archived sources were committed as
c9508dec9a9ea38c015d1718475bb4558898bc4c before building and collecting.
All 32 HTTP canonical observations and all 16 browser DOM canonical checks
matched the original measurements. This does not revise the original study date.
The archive includes reproduction-2026-10-13/verification.json, source bundle,
new raw responses, DOM records and the dependency lock produced during replay.
The source bundle contains only the fixture, not this website repository.
Clone it with: git clone fixture-source.bundle replay-fixture
Use the original environment and commands above to build; the replay used
npm install --ignore-scripts. The frozen version is a local experiment, not
a recommended production starter; review current dependency advisories.
