Commit 508b74

2026-03-14 18:22:07 Claude (MCP): [mcp] [mcp] E-2 client-side: add SEO strategy (meta tags, bot routing, sitemap)
Tasks/E-2_CDN_Read_Path_ClientSide.md ..
@@ 222,8 222,60 @@
- Fewer Lambdas to manage is preferred
- Faster cache-miss latency matters
+ ## SEO Strategy (Future: Public Wikis)
+
+ Client-side assembly means page content is not in the initial HTML — a problem for search engines and link previews. This is irrelevant for private wikis (our current state) but matters when public wikis exist. Three layered mitigations:
+
+ ### 1. Page-specific meta tags (low effort, high value)
+
+ On page save, extract the first ~160 characters of plain text from the rendered content. Store alongside the content fragment as a metadata file:
+
+ `s3://bucket/fragments/{username}/{wiki}/meta/{page_path}.json`
+ ```json
+ {
+ "title": "Page Title",
+ "description": "First 160 chars of page content...",
+ "og_image": null
+ }
+ ```
+
+ The CloudFront Function injects these as `<meta>` tags when rewriting the shell URL — or the shell JS sets them before fragment fetch. This handles **link previews** (Slack, Twitter, Discord, iMessage) without any server-side rendering, since unfurlers read `<meta>` tags from the initial HTML without executing JS.
+
+ ### 2. Dynamic rendering for bots (medium effort, full coverage)
+
+ Add the assembly Lambda (from [[Tasks/E-2_CDN_Read_Path]]) as a second origin behind the same CloudFront distribution. A CloudFront Function on viewer-request checks `User-Agent`:
+
+ ```javascript
+ var botPattern = /googlebot|bingbot|slurp|duckduckbot|baiduspider|yandexbot|facebot|twitterbot|linkedinbot/i;
+ if (botPattern.test(request.headers['user-agent'].value)) {
+ // Route to assembly Lambda origin — returns complete HTML
+ request.origin = { /* assembly Lambda origin config */ };
+ }
+ // Humans get client-side assembly (default S3 origin)
+ ```
+
+ This is Google's recommended approach for JS-heavy sites. Bots get full HTML from the assembly Lambda. Humans get the fast client-side path. The assembly Lambda can be cold (nobody cares if Googlebot waits 1s).
+
+ Since the fragment generation (Wave 1) is identical in both plans, adding the assembly Lambda later is a bolt-on — not a rearchitecture.
+
+ ### 3. Sitemap generation (low effort, aids discovery)
+
+ On page create/delete/rename, regenerate `sitemap.xml` for public wikis and upload to S3:
+
+ `s3://bucket/fragments/{username}/{wiki}/sitemap.xml`
+
+ Serve via CloudFront at `/{username}/{wiki}/sitemap.xml`. Helps search engines discover pages regardless of rendering method. Submit to Google Search Console for faster indexing.
+
+ ### Phasing
+
+ - **Now (MVP):** No SEO support needed. All wikis are private.
+ - **When public wikis ship:** Add meta tags (item 1) and sitemap (item 3). Covers link previews and search discovery. ~0.5 day effort.
+ - **If search ranking matters:** Add bot-routed assembly Lambda (item 2). ~1 day effort on top of the existing fragment infrastructure.
+
## Recommendation
- Client-side assembly is simpler, has fewer moving parts, and performs as well or better than the assembly Lambda for our use case. The content flash is mitigated by CSS skeleton and fast CDN fetches. SEO is not a current concern (private wikis, MCP-primary access pattern).
+ Client-side assembly is simpler, has fewer moving parts, and performs as well or better than the assembly Lambda for our use case. The content flash is mitigated by CSS skeleton and fast CDN fetches.
+
+ The SEO gap is addressed in layers: meta tags for link previews (cheap), bot routing to the assembly Lambda for full indexing (bolt-on later). Fragment generation is shared between both paths, so the assembly Lambda is an additive enhancement — not a prerequisite or a rearchitecture.
- If SEO becomes important for public wikis later, the assembly Lambda can be added as an additional origin behind the same CloudFront distribution — the fragment generation (Wave 1) is identical in both plans.
+ Start with client-side assembly. Add SEO layers when public wikis ship.
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9