The package I almost used, and why the ephemeral build container said no
Bing Webmaster Tools wants IndexNow set up: a small protocol where you ping one endpoint and Bing, Yandex, Seznam, and Naver all find out a page changed, instead of waiting for each of them to crawl around to it eventually. The protocol itself is almost insultingly simple - host a text file with a random key, POST a URL list somewhere, done. The interesting part of this issue wasn’t the protocol. It was figuring out what “somewhere” and “when” actually mean for a site that deploys the way this one does.
The obvious answer was almost the wrong one
There’s a real, maintained Astro package for this - astro-indexnow. Astro 4 through 7, MIT license, does exactly what the issue asked for: hashes your built pages, only resubmits the ones that changed since last time. I went in assuming I’d install it, wire up a key, close the issue in ten minutes.
Then I read how it tracks “changed since last time”: it hashes every built HTML file into a JSON cache, and to compare against the previous build, that cache file has to be committed back to the repo after each build. Their docs say this outright - it’s designed for CI/CD setups that can add a “commit the cache file” step after building.
This site deploys through Cloudflare Workers Builds: push to main, Cloudflare spins up a throwaway container, builds, deploys, and the container is gone. There’s no step where anything gets written back to the repository - by design, it’s a one-way pipe. The package assumes a shape of CI/CD that this deploy doesn’t have. Not a bug in the package, just a mismatch I wouldn’t have caught without actually reading past the install instructions.
What I built instead
IndexNow’s own docs say resubmitting URLs that haven’t changed costs nothing - there’s no penalty, no rate-limit trap for being “wasteful.” So for a site with about 26 pages, change-diffing was solving a problem I didn’t have. I skipped the cache entirely and just resubmit everything, every deploy.
That turned into a small Astro integration instead of a dependency. Astro hands every integration a pages array in its astro:build:done hook - literally every prerendered route, already deduped, no HTML-walking or sitemap-parsing required. I read the actual hook source in node_modules rather than trust what I remembered about the API, since “I remember it works like X” is exactly the kind of assumption that costs an hour later. Forty lines, zero new dependencies, one fetch call.
The part that needed a second lookup
Without a guard, this integration would fire on every single build - my local npm run dev iterations, and the separate preview-branch trigger this repo uses for non-main pushes. Both would happily submit the production domain’s URLs to a real search index, repeatedly, for no reason.
Cloudflare Workers Builds injects WORKERS_CI_BRANCH into the build environment - I hadn’t used this before, had to go find it in Cloudflare’s own docs. Gate on === 'main' and the integration goes silent everywhere except the actual production build.
Proving it before it could really be proven
The key file wasn’t live yet - it ships in this same commit - so I couldn’t get a real “yes, IndexNow accepted this” until after merge. What I could do: force WORKERS_CI_BRANCH=main locally and let the integration make a real network call to the real API. It came back 202 Accepted, not the 403 I was expecting for an unverifiable key. Turns out IndexNow queues submissions for async key verification rather than bouncing them - so a 202 pre-deploy tells you the request shape is correct, and you find out for certain post-deploy. Good enough to ship with a documented follow-up instead of blocking on it.
Related reading
The 404 that was already correct, and the robots.txt that wasn't
A Sitemap directive that had to point at the index, a 404 fix that almost got overbuilt, and a documented Cloudflare behavior that didn't materialize when checked.
Fixing four small lies on the Deep Cut Atlas page
A dash that didn't match the app's own typography, a tagline that didn't parse, a feature claim the app can't back up, and one missing trailing slash in the structured data.
The description nobody wrote
Bing said the site's meta descriptions were too short. Almost every page had a good one - except the homepage and the blog index, the two pages everybody actually lands on.