feat(seo): IndexNow submissions for changed pages - #11202
Conversation
… Naver, Yep
Bing Webmaster Tools' first recommendation for the site. A public key file
(app/public/<key>.txt, served by an explicit nginx location so crawler UAs
are not proxied to /seo-proxy and a 404) proves control of the host, and a
new workflow maps every push to main that touches plots/ onto the affected
/{spec} and /{spec}/{language}/{library} URLs and POSTs them to
api.indexnow.org. workflow_dispatch with scope=sitemap submits the whole
live sitemap for the initial load. Google does not take part.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SrKzcwZBnref1sWYtdXynu
There was a problem hiding this comment.
🟢 Approval recommended
The changes are cohesive and low-risk, with only a minor misleading comment in the new workflow noted for cleanup.
Pull request overview
Adds IndexNow support so search engines that participate in IndexNow can be notified immediately when plot/spec pages change, improving crawl freshness for the plots/-driven site surface.
Changes:
- Add an IndexNow submission GitHub Actions workflow that submits either changed URLs (on
mainpushes touchingplots/) or the full sitemap (manual dispatch). - Serve the IndexNow key file directly from nginx (including for crawler user agents) and add documentation describing setup and operation.
- Document the new workflow and add a CHANGELOG entry under Added.
File summaries
| File | Description |
|---|---|
docs/workflows/overview.md |
Documents the new indexnow-submit.yml workflow in the workflows overview table. |
docs/reference/seo.md |
Adds an “IndexNow” section describing the key file, workflow behavior, and manual sitemap submission. |
CHANGELOG.md |
Adds an Added entry describing the new IndexNow integration and operational details. |
app/public/anyplot-indexnow-ab738f04ea92446a.txt |
Adds the public IndexNow key file required for ownership verification. |
app/nginx.conf |
Adds an exact-match location = so the key file is served directly (not routed through the bot proxy path). |
.github/workflows/indexnow-submit.yml |
New workflow to collect URLs (diff-based or sitemap) and POST batches to the IndexNow API. |
Review details
- Files reviewed: 6/6 changed files
- Comments generated: 1
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
Review feedback: the header pointed at api/routers/seo.py, which does not carry the key; the nginx exact-match location does. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SrKzcwZBnref1sWYtdXynu
Mirrors the sister repo's review: a slow or flaky api.indexnow.org must not burn the job timeout, and three retries cover a transient error. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SrKzcwZBnref1sWYtdXynu
There was a problem hiding this comment.
🟡 Changes recommended
The new IndexNow workflow has reliability/correctness issues (diff range handling, shallow checkout assumptions, and missing network hardening) that can cause missed submissions or flaky failures.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Review details
Suppressed comments (3)
.github/workflows/indexnow-submit.yml:98
- The IndexNow POST uses curl without retry/time limits; intermittent network issues or temporary 5xx responses can make pushes to
mainreport a failure even though a retry would succeed. Consider adding the same--retryand--max-timesafeguards used elsewhere in the repo's monitoring workflows.
# Bounded: a slow or flaky api.indexnow.org must not burn the job
# timeout; three retries cover a transient error.
code=$(curl -sS -o response.txt -w '%{http_code}' \
--max-time 30 --retry 3 --retry-delay 5 --retry-all-errors \
.github/workflows/indexnow-submit.yml:66
- With
scope=sitemap, add retry/timeout and fetch the sitemap from the direct app origin (set viaAPP_ORIGIN) to reduce flaky failures from transient network issues or edge blocking.
curl -sfS "https://${HOST}/sitemap.xml" \
| grep -oE '<loc>[^<]+</loc>' | sed -E 's#</?loc>##g' | sort -u > urls.txt
.github/workflows/indexnow-submit.yml:72
- Diffing only
HEAD~1..HEADmeans a multi-commit push tomainwill submit URLs for just the last commit, and the all-zerobeforeSHA (first push / history rewrite) is not handled. Usinggithub.event.before/github.sha(likesync-labels.ymldoes) avoids missing submissions.
git diff --name-only HEAD~1 HEAD -- plots/ | awk -F/ '
$1 != "plots" || NF < 3 { next }
$3 ~ /^specification\./ { print "https://'"$HOST"'/" $2; next }
($3 == "metadata" || $3 == "implementations") && NF == 5 {
lib = $5; sub(/\.[^.]+$/, "", lib)
- Files reviewed: 6/6 changed files
- Comments generated: 3
- Review effort level: Lite
There was a problem hiding this comment.
🔵 Needs a closer look
The workflow’s URL count output can be space-padded and trigger empty submissions, and the new changelog bullet is missing the required PR reference.
Review details
Suppressed comments (2)
Previously missed (1) — in code that hasn't changed since the last review.
.github/workflows/indexnow-submit.yml:77
wc -l < urls.txtproduces a space-padded count (e.g. " 0"), which then gets written toGITHUB_OUTPUTand makesif: steps.urls.outputs.count != '0'evaluate true even when there are 0 URLs. That can lead to submitting an emptyurlListbatch and failing the run with a 4xx from IndexNow.
CHANGELOG.md:39
- This new changelog bullet is missing the required PR reference (e.g.
(#12345)) at the end. The file header explicitly states every[Unreleased]entry must include its PR reference, and the surrounding bullets follow that pattern.
`.github/workflows/indexnow-submit.yml` maps every push to `main` that touches `plots/`
onto the affected `/{spec}` and `/{spec}/{language}/{library}` URLs and POSTs them to
`api.indexnow.org` (10,000 per request; a deleted implementation is submitted too).
`workflow_dispatch` with `scope=sitemap` submits the whole live sitemap for the initial
load. Google does not take part and keeps reading the sitemap. The protocol is free.
- Files reviewed: 6/6 changed files
- Comments generated: 0 new
- Review effort level: Lite
…i-commit pushes Review feedback: fetching the live sitemap puts the Cloudflare edge between a GitHub runner and the list; the checkout carries the same information (spec directories, metadata files, the static pages). A push with more than one commit falls back to the full list instead of a diff that fetch-depth 2 cannot resolve. Transport errors after the curl retries warn instead of failing the run; 4xx still fails. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SrKzcwZBnref1sWYtdXynu
There was a problem hiding this comment.
🟡 Changes recommended
The changed-URL mapping currently misses implementation URLs when only specification.* changes, so IndexNow won’t be notified for affected /{spec}/{language}/{library} pages.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Review details
- Files reviewed: 6/6 changed files
- Comments generated: 1
- Review effort level: Lite
…r the key file Review feedback: implementation pages render the spec's title and description, so a changed specification touches all of them, not only the hub. The submit step now also waits up to 8 min for the key file the app deploy serves (a rollout or key rotation), submitting either way because IndexNow verifies the key itself and a runner may be 403'd at the edge. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SrKzcwZBnref1sWYtdXynu
There was a problem hiding this comment.
🟢 Approval recommended
The implementation and wiring look coherent and low-risk, with only minor documentation wording/style nits identified.
Review details
Suppressed comments (2)
Previously missed (2) — in code that hasn't changed since the last review.
CHANGELOG.md:32
- The changelog entry is missing the Oxford comma in the IndexNow engine list ("Naver and Yep"), which is inconsistent with the repo's documentation style baseline.
docs/reference/seo.md:508 - The sentence ending with "Three pieces, kept in sync…" is a fragment and the list is missing the Oxford comma; this reads awkwardly in docs and diverges from the repo's Google-style prose baseline.
- Files reviewed: 6/6 changed files
- Comments generated: 0 new
- Review effort level: Lite
## Summary - Follow-up to #11202, from the review of the sister repo's twin (kurrentschrift #491): the key-file readiness loop used `curl -f`, so a 403 from Cloudflare's bot management looked like "not deployed yet" and a blocked runner would sleep the full 8 minutes on every run. - The loop now reads the status: `200` and `403` both end the wait (a 403 means the file is served but this runner is blocked at the edge; Bing's own fetch is not), anything else keeps waiting, and the final warning names the last status seen. ## Plan N/A ## Test plan - [x] YAML parses, the run step passes `bash -n`. - [ ] The first `changed` run after the next pipeline merge logs either `key file reachable` or the 403 notice within one probe. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01SrKzcwZBnref1sWYtdXynu --------- Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
Summary
app/public/anyplot-indexnow-ab738f04ea92446a.txtproves control ofanyplot.aito the IndexNow engines. It is public by design.app/nginx.confgets an explicitlocation =for it (likerobots.txt/llms.txt), because Bing verifies with a crawler UA that the bot map would otherwise proxy to/seo-proxy/<key>.txtand a 404..github/workflows/indexnow-submit.ymlruns on every push tomainthat touchesplots/: the diff (HEAD~1..HEAD, one squash merge) is mapped to/{spec}and/{spec}/{language}/{library}URLs and POSTed tohttps://api.indexnow.org/indexnowin batches of 10,000 (the protocol limit). A deleted implementation is submitted too — IndexNow means "this URL changed".200/202is success;4xxfails the run so a key or payload problem is visible.workflow_dispatchwithscope=sitemapsubmits every URL of the live sitemap (4.6k today) — I will run it once after the app deploy has served the key file.docs/reference/seo.md, a row indocs/workflows/overview.md, CHANGELOG under Added.Plan
N/A
Test plan
bash -n, the diff→URLawkmapping was dry-run against a synthetic diff (spec page, metadata and implementation paths, an unrelated file).curl https://anyplot.ai/anyplot-indexnow-ab738f04ea92446a.txtreturns the key both with a browser UA and with-A bingbot.gh workflow run indexnow-submit.yml -f scope=sitemapanswers 200/202; Bing Webmaster Tools lists the submission under IndexNow within a day.changedrun with exactly the touched URLs.🤖 Generated with Claude Code
https://claude.ai/code/session_01SrKzcwZBnref1sWYtdXynu