seo(landing): serve the IndexNow key file - #416
Open
BSalaeddin wants to merge 1 commit into
Open
Conversation
Review or Edit in CodeSandboxOpen the branch in Web Editor • VS Code • Insiders |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Serves the IndexNow ownership key file on the indexable host of
useupup.com(
apps/landing):apps/landing/src/lib/indexnow.ts— one constant module holdingINDEXNOW_KEY/INDEXNOW_KEY_PATH, with the protocol rules that make thefile load-bearing written down next to it.
apps/landing/src/app/5cb30cbda540958e8d033652400e59e3.txt/route.ts— aforce-staticroute handler returning the bare key astext/plain; charset=utf-8, cached a day. (A directory whose name ends in.txtis an established route shape in this app —src/app/docs-llms/llms.txt/already does it.)
apps/landing/src/__tests__/indexnow.test.ts— pins the directory name andthe constant to each other.
No dependencies added, no config changed, nothing submitted anywhere.
Why
IndexNow lets us push URL changes to Bing / Yandex / Seznam / Naver the moment
they happen instead of waiting to be recrawled. Bing currently reports 56
pages indexed and 0 clicks for this domain (2026-09-11 fleet pull), so
getting new and changed docs pages in front of it promptly is cheap upside.
The protocol proves ownership by fetching
https://<host>/<key>.txtandrequiring the body to be exactly the key
(https://www.indexnow.org/documentation). The key file has to be live in
production before a single submission can succeed — that is the whole scope
of this PR.
The key is freshly generated for this app and is not a secret: publishing it is
the mechanism.
What CI covers
apps/landing/src/__tests__/indexnow.test.tsguards the one coupling that cansilently break verification — the App Router directory is named after the key,
so a change to the constant without the matching rename (or vice versa) leaves
the site serving one key while claiming another, and every submission fails
ownership against a 200 response. It asserts:
INDEXNOW_KEY_PATHis the root-level.txtform of it;src/app/<INDEXNOW_KEY>.txt/route.tsexists on disk, resolved from theconstant — not from a second hardcoded copy of the key;
GET()returns 200,content-type: text/plain; charset=utf-8, and a bodythat is exactly the key with nothing appended.
Indexability was checked rather than assumed:
src/app/robots.tsdisallows only/api/and/mobile-demo/, andsrc/app/sitemap.tsenumerates explicit routes,so the key file is neither blocked nor advertised as content.
Verification
Gates, from the worktree (via
rtk proxywherever output fidelity matters):turbo run lint typecheck test --filter=@useupup/landing --forcepnpm run test:qualitypnpm run vocab:checkprettier --checkon the three touched filesturbo run build --filter=@useupup/landing --forceThe build prerenders the key file statically —
.next/server/app/5cb30cbda540958e8d033652400e59e3.txt.bodyis 32 bytes,exactly the key with no trailing newline.
Noted for the record, not a finding:
@useupup/coretests/strategies/tus-upload-missing.test.tsintermittently timed out at 15 swhile the authoring box was heavily loaded (vitest reported
transform 858s / import 1110sunder contention from other checkouts). Per the flake protocol itwas re-run isolated —
@useupup/coreis 146 files / 1843 tests, all green,exit 0 — and the pre-commit hook then passed clean. Nothing in this diff
touches
packages/.Live against a production
next build+next start -p 4467:trailingSlash: trueadds no redirect here, which is the property IndexNowactually depends on — an engine that gets a 308 on the key file treats
verification as failed. Confirmed rather than assumed:
That matches the rule already documented in
next.config.mjs: Next neverappends a trailing slash to a path with an extension. The body was also compared
byte-for-byte against the constant (32 chars, exact match, no newline).
Crawl policy checked on the same running server:
/robots.txtservedDisallow: /api/+Disallow: /mobile-demo/only, and/sitemap.xmldoes notcontain the key.
Next step (not in this PR)
Owner / SEO: once this is deployed to production and the URL returns 200 there,
submit URLs through the fleet SEO MCP's IndexNow surface. No submission API is
called from this codebase, and none should be added here — the app's job ends at
serving the key.