← sorigamis.com

behlabs · engineering notes · september 2026

How I got SoriGamis indexed by Google, Bing, and the AI crawlers in one day

A marketing site can have flawless meta tags and still be invisible — because a CDN toggle is quietly telling every crawler to go away. Here's the full pipeline I built, the four traps I hit, and the playbook I'll reuse on every project from now on.

5 pages 3+ search engines ~10 min to first Google crawl same day homepage indexed

SoriGamis is a mobile app that turns recorded conversations into speaker-attributed transcripts, summaries, and decision records. Its marketing site is a small static-first React app — five routes, prerendered to plain HTML at build time, deployed on Vercel behind Cloudflare. The goal wasn't just classic SEO: I wanted the site legible to AI search too — ChatGPT (whose web search leans on Bing), Claude, and Perplexity all crawl the web with their own bots, and they can only cite what they're allowed to read.

What follows is what I actually did, in order, including everything that went wrong. The reusable step-by-step playbook is at the end.

Phase 01Get the on-site foundation honest

The site already had the basics — per-route titles, descriptions, canonicals, Open Graph tags, and JSON-LD structured data baked into prerendered HTML. The audit still found real rot:

Gotcha № 1 — git lies in CI

Deriving lastmod from git log works great locally and silently produces wrong dates in CI: shallow clones (Vercel builds, actions/checkout with default depth) report the clone-boundary commit's date, stamping every page as freshly modified on every deploy — worse than the stale dates I was replacing. The fix: check git rev-parse --is-shallow-repository and omit lastmod entirely in shallow clones. A missing date is honest; a wrong one erodes crawler trust.

Gotcha № 2 — hydration undoes your SEO

On a React site, client-side components that rewrite <head> tags and JSON-LD will overwrite your server-rendered metadata after page load. I fixed the server copies, shipped them — and a code review caught that two client components still carried the old text and replaced the fresh JSON-LD on every hydration. Any JavaScript-executing crawler (Googlebot included) was indexing the stale version. If metadata lives in more than one place, sync all of them or the newest copy loses.

Phase 02Discover your CDN is blocking everything

With the site itself in order, I checked the live robots.txt — and found a block of directives I never wrote, sitting above mine:

# BEGIN Cloudflare Managed content
User-agent: *
Content-Signal: search=yes,ai-train=no,use=reference
Allow: /

User-agent: ClaudeBot
Disallow: /

User-agent: GPTBot
Disallow: /
# ... CCBot, Google-Extended, Amazonbot, Bytespider, meta-externalagent ...

Cloudflare was rewriting my robots.txt at the edge to tell the exact crawlers I'd just welcomed to go away — and separately 403-blocking them at the network level, so even a bot that ignored robots.txt couldn't get through. My repo's file was irrelevant; the deployed origin had its own policy.

It turned out to be two independent toggles in the Cloudflare dashboard, both under AI Crawl Control:

Disabling one without the other isn't enough — I know because I disabled one, re-checked the live file, and the block was still there. After turning both off, the served robots.txt finally matched the repo byte for byte.

Immediate signal

Within 24 hours of unblocking, Cloudflare's crawler analytics showed Claude-SearchBot had already made 11 successful requests. The crawlers were waiting at the door the whole time.

Phase 03Register with Google and Bing — without granting anyone account access

Both consoles offer convenient shortcuts that trade account access for saved clicks. I skipped both:

Gotcha № 3 — the proxy eats your verification record

On Cloudflare, new CNAME records default to Proxied, which hides the record's real target behind Cloudflare's edge — and breaks DNS-based verification. Set verification CNAMEs to DNS only. (TXT records are safe; they can't be proxied.) And afterward: never delete these records. Each one silently anchors its console's ownership verification.

Both submissions confirmed instantly — Google reported "Sitemap submitted successfully" with 5 pages discovered on the spot; Bing showed the sitemap as processing, flipping to Success, 5 URLs discovered by the next morning.

Phase 04Automate freshness with IndexNow

Submitting a sitemap once is a snapshot. IndexNow is the standing arrangement: an open protocol where you ping an API whenever content changes, and Bing, Naver, Yandex, and Seznam re-crawl within minutes instead of on their own schedule. (Naver in that list is a real bonus for a Korean/English product.) The setup is refreshingly simple:

  1. Generate a random key (openssl rand -hex 16) and serve it as <key>.txt at your site root — that public file is the authentication.
  2. On every production deploy, POST your changed URLs to api.indexnow.org with the key. My script fetches the site's own live sitemap for the URL list, so the sitemap generator stays the single source of routes.
  3. Wire it into CI. Mine is a small GitHub Actions workflow on the release branch: wait two minutes for the deploy to settle, then ping.
const sitemap = await fetch(`${siteUrl}/sitemap.xml`);
const urlList = [...(await sitemap.text())
  .matchAll(/<loc>([^<]+)<\/loc>/g)].map(m => m[1]);

await fetch("https://api.indexnow.org/indexnow", {
  method: "POST",
  headers: { "Content-Type": "application/json; charset=utf-8" },
  body: JSON.stringify({ host, key, keyLocation, urlList }),
});
Gotcha № 4 — your own guardrails will fight you (let them win)

Two failures on the way in, both legitimate: the repo's secret scanner flagged the hardcoded IndexNow key as a leaked API key — the right fix wasn't an allowlist entry but removing the literal, deriving the key from the checked-in public key file instead (and amending the commit so the literal never entered history). Then the workflow's hardened permissions: {} stripped even contents: read, so checkout couldn't see the private repo. Minimal isn't zero: permissions: contents: read.

First automated ping: IndexNow accepted 5 URLs (HTTP 202) — accepted, key validation pending. By the next day, a direct submission to Bing's endpoint returned HTTP 200: key validated, submissions accepted at face value. From now on, every release re-pings automatically.

Phase 05What happened, and how fast

When Event
Day 0, ~11:00 Sitemap submitted to Google Search Console
Day 0, 11:11 Googlebot crawls the homepage — eleven minutes later
Day 0 Cloudflare unblocked; Claude-SearchBot logs 11 successful requests within 24h
Day 0 IndexNow ships; first automated ping accepted (202)
Day 1 URL Inspection: homepage "URL is on Google", canonical agreed, all signals green
Day 1 Bing sitemap status: Success, 5 URLs discovered; IndexNow key validated (200)
Day 1 /faq "Discovered — currently not indexed" → priority indexing requested

Aggregate dashboards lag behind all of this — Search Console's Pages report and Bing's IndexNow insights both take days to start rendering for a new property. The per-URL inspection tools are the ground truth in week one; don't panic-debug a dashboard that's still saying "processing data".

PlaybookThe reusable checklist

The order matters: on-site correctness first (so crawlers see something worth indexing), unblocking second (so they can reach it), registration third (so you can see what they saw), automation last.

  1. Prerender or SSR every page that should rank. LLM crawlers mostly don't execute JavaScript, and Google indexes plain HTML faster. Every route needs a crawler-visible H1, title, meta description, and canonical.
  2. Generate the sitemap at build time from your real route registry. Never hand-maintain it. Derive lastmod from git history of page sources — and omit it in shallow clones rather than emit wrong dates. Fail the build if a route is missing.
  3. Make visible content the canonical source for all structured data. FAQ schema must mirror the questions actually rendered. Audit every copy of your metadata — server, template, client, manifest — and remember hydration overwrites the head.
  4. Write an AI-friendly robots.txt and an llms.txt. One stacked group allowing GPTBot, OAI-SearchBot, ChatGPT-User, ClaudeBot, Claude-User, Claude-SearchBot, PerplexityBot, Google-Extended; point to the sitemap and llms.txt.
  5. Fetch your LIVE robots.txt and diff it against your repo. The single highest-leverage check in this list. CDNs inject policy at the edge — on Cloudflare, check AI Crawl Control for both "Managed robots.txt" (Signals) and "Block AI bots" (Security). They're separate toggles; disable both.
  6. Verify Google Search Console with a Domain property + manual DNS TXT. Skip the "authorize access to your DNS provider" OAuth shortcut. Submit the sitemap immediately after verifying.
  7. Verify Bing Webmaster Tools manually with a DNS-only CNAME. Skip the GSC import unless you want your Google data linked to Microsoft. On Cloudflare, the CNAME must be "DNS only", not proxied. Submit the same sitemap. Bing feeds ChatGPT search and Copilot.
  8. Keep the verification DNS records forever. Deleting either one silently un-verifies the property. Document them where your team will see the warning.
  9. Set up IndexNow and ping on every deploy. Random hex key served at the root, CI job POSTs the sitemap's URLs to api.indexnow.org after each production deploy. Don't hardcode the key where a secret scanner will flag it — read it from the served key file.
  10. Use per-URL inspection for week-one truth; request priority indexing for your highest-value page. Aggregate reports lag by days. URL Inspection (Google) answers immediately; spend the priority-queue quota on the one page that earns rich results.
  11. Then go earn the off-site half. LLM answers lean on corroboration: directory listings, consistent product descriptions across the web, Organization schema with sameAs links, and content depth. On-site plumbing gets you read; substance gets you cited.

The whole thing — audit to automated pings — fit in a day, and most of that day was spent on the two things no meta tag can fix: the CDN silently overriding site policy, and five copies of "the same" metadata quietly disagreeing. Check the live wire, not the repo.