Files
hoelee 060b0f1733
Deploy / build (push) Successful in 19s
docs: record robots.txt encoding rule and sitemap derivation in seo-reference
The crawler/sitemap mechanics now live in the repo doc, not just in the
commit log: robots.txt is a build-time endpoint that must stay pure ASCII
(no charset on a text/plain response means non-ASCII renders as mojibake),
the policy is allow-all with enforcement deliberately left to Cloudflare,
why writing a real robots.txt demotes Cloudflare's placeholder from
replacement to prepend, and that lastmod/hreflang are derived so they must
never be hand-authored.
2026-09-19 22:14:40 +08:00

4.1 KiB
Raw Permalink Blame History

SEO & Metadata Reference — blog.hoelee.com

Operational reference for the SEO/GEO decisions baked into this project. Read when adding posts, changing <head> markup, or debugging search visibility.


Two-lane strategy (2026)

Optimize for both:

  1. Classic SEO — Google blue links (still the majority of name searches).
  2. GEO (Generative Engine Optimization) — getting cited by Google AI Overviews, ChatGPT Search, Perplexity. They pull from well-structured, question-answering content.

Identity & E-E-A-T (do this first, once)

The entire blog strategy is a name-search play, so identity must be airtight:

  • One name everywhere: "Lee Teong Hoe" / "Mr Hoelee" — pick one and keep it identical on blog, LinkedIn, GitHub, git.hoelee.com.
  • One photo across all profiles.
  • sameAs handles in the Person/ProfilePage schema → LinkedIn, GitHub, git.hoelee.com.
  • ProfilePage schema wrapping every post's author bio — how Google connects my writing to my professional identity.

Per-post checklist

  • Unique title ≤ 60 chars (target keyword near front)
  • Meta description ≤ 155 chars
  • Canonical URL set
  • Custom og:image (1200×630) — never the reused logo
  • og:image:width/height declared (else WhatsApp/Telegram/LinkedIn reject the preview)
  • twitter:card: summary_large_image
  • Category + tags set
  • Author card present
  • Internal links to ≥1 pillar post

Structural checklist (site-wide)

  • sitemap.xml + robots.txt + RSS — auto-generated by Astro
  • Core Web Vitals 90+ (verify Lighthouse on 390px and 1280px)
  • Question-shaped H2/H3 (the phrasing a learner actually searches)
  • Related-posts module (3 cards) on every post
  • No orphaned/thin pages (avoid the hoelee.com ~400-attachment mistake)

Crawler / sitemap mechanics (as built)

  • robots.txt is a build-time endpoint (src/pages/robots.txt.ts), not a static file in public/. It interpolates SITE.url so the Sitemap: line can't drift. Keep it pure ASCII — the response has no charset, so a non-ASCII byte renders as mojibake () in viewers that fall back to a legacy codepage. Check: LC_ALL=C grep -c '[^ -~]' dist/robots.txt must be 0.
  • Policy is allow-all, including AI training crawlers. Rationale: robots.txt is advisory and unenforced (Cloudflare documents it as voluntarily honoured), so a blocklist there buys nothing; enforcement, if ever wanted, belongs in Cloudflare AI Crawl Control. /pagefind/ is the only exclusion (build artifacts, not content).
  • Why a real robots.txt matters at all: writing one turned Cloudflare's Content Signals Policy placeholder from a replacement into a prepend. On a Free-plan zone with no origin robots.txt, Cloudflare serves that placeholder in place of your file — a comment block with no User-agent, no directives and no Sitemap: line. Confirm the origin is serving yours before blaming Astro.
  • Sitemap lastmod + xhtml:link hreflang alternates are derived, never authored — see the rule in the project skill. sitemap-index.xml legitimately contains exactly one entry (sitemap-0.xml) and looks empty in a viewer; the URLs are in sitemap-0.xml (77 of them at 29 posts). Don't "fix" the index.
  • hreflang exists in two places on purpose: in-page <link rel="alternate"> (from BaseLayout's altLocaleUrl) and sitemap xhtml:link. Google treats the sitemap as more authoritative when they disagree, so both must be kept in sync — which is automatic for posts and for any page that passes altLocaleUrl.

Syndication policy

  • Primary home = this domain (own the SEO).
  • Cross-post to Dev.to / Medium with a rel=canonical pointing back to blog.hoelee.com — reach without platform lock-in.
  • Never make a third-party platform the primary host.

The "hard job → post" GEO framing

When writing, phrase the opening as the exact question someone types into an AI engine or Google:

"How do I set up site-to-site OpenVPN behind CGNAT?"

Then answer it directly with clear H2/H3 headings. This is what both Google and the AI engines cite.