08 官網架構與 SEO/GEO 做法

框架選擇、靜態預產生、讓 Google 與 AI 搜尋都找得到、商業分析接法

框架定案:留 TanStack Start 開 prerender(決策者 2026-10-21 最終裁示) 部署已定:Cloudflare Pages 純靜態 待做 11 項

08 官網架構與 SEO/AI SEO/GEO 做法

這份回答三個問題:官網用什麼框架、怎麼讓搜尋引擎與 AI 搜尋(ChatGPT、Perplexity、Claude、Gemini)都看得到內容、怎麼接商業分析。結論是留 Lovable 拉回來的 TanStack Start,開 prerender 出純靜態檔;部署與 SEO/GEO 配備照公司已上線的 design-home-fe 做法,檔案改寫成 TanStack 的對應寫法。

§1

一、先講結論

框架留 TanStack Start(Lovable 產出的那套),開 prerender 出純靜態 HTML。決策者 2026-10-21 最終裁示,不再討論。

曾評估轉 Next.js(與 design-home 對齊)並派過一棒試轉,決策者中途裁定沿用 TanStack:骨架已能跑、文案與版型都在,轉換只是換底不加價值;TanStack Start 本來就是 SSR 框架,prerender 一開就出每頁 index.html,SEO/GEO 要的「HTML 裡有內文」照樣成立。design-home 的 SEO 檔案拿來當內容參考(爬蟲清單、JSON-LD 欄位、llms.txt 骨架),寫法改成 TanStack 的 head()、public/ 靜態檔與路由。

問題 答案
純前端 Vue/React SPA 能不能做 SEO/GEO 不能。Google 要第二輪才渲染 JS,GPTBot、ClaudeBot、PerplexityBot 不執行 JS,看到的是空殼
一定要 Next.js 嗎 不用。TanStack Start、Nuxt、Astro 都能出靜態 HTML;本站用 TanStack Start
要不要改成 Astro 不要,但它是最接近的第二名。技術上更適合官網(零 JS、Cloudflare 旗下、滿意度最高),輸在團隊要多懂一套、與 design-home 分家。何時反悔見一之二
留 Lovable 給的 TanStack Start 行不行 能做但不建議。框架太新(2026-10-13 才 1.0、nitro beta)、社群小出事難找答案、設定被 @lovable.dev/vite-tanstack-config 包住;留下等於替 Lovable 的選擇背一輩子,只為省一兩天
部署放哪 Cloudflare Pages,純靜態。不用 Workers;需要時轉 Workers 只改部署指令、程式碼不動(見第三節之二)
未來接 Payload CMS 行不行 行。Payload 是獨立後台,官網 build 時打它的 API 取內容,改完內容由 webhook 觸發重 build,design-home 就是這樣跑
未來要 call 自家 API 行不行 行。build 時抓一次的內容在 server component 裡 fetch;使用者操作時才打的(送表單)在 "use client" 元件裡打
§2

一之二、三個框架對九條需求的對照

決策者在意的是「不要順著單一提問改答案」,所以把需求攤開、三個候選者用同一套標準各打一次分。◎ 強、○ 可、△ 弱。

需求清單(來自決策者原話與 02 網站地圖):

# 需求
R1 SEO
R2 AI SEO/GEO,AI 搜尋找得到
R3 商業分析
R4 搭配 Payload CMS,公司已有 design-home 那套
R5 從 Lovable 轉一次,之後自己維護、不再用 Lovable
R6 架構定了就不要再變
R7 部署 Cloudflare Pages
R8 決策者對前端生態陌生;團隊既有技能是 Vue(產品)與一個已上線的 Next 站
R9 官網範圍小:18 固定頁+6 篇指南、中英、一個表單;不做登入、價格、試用

對照:

需求 Next.js 靜態 export Astro TanStack Start(Lovable 給的)
R1 SEO 技術面 ○ HTML 完整,但每頁帶 80~120KB React,Lighthouse 多在 80~90 ◎ 預設零 JS,Lighthouse 98~100 ○ 同 Next,帶 React runtime
R2 GEO ○ 內文在 HTML;robots/sitemap/metadata 有內建 API ◎ 內文在 HTML;2026 年多數評測點名對 AI 搜尋最友善 ○ 內文在 HTML;配備要自己補
R3 商業分析 ○ 加一段 script ○ 同 ○ 同
R4 Payload ◎ Payload 本身跑在 Next 上;design-home 的 lib/payload.ts 直接抄 ○ Astro 官方有 Payload 指南,走 REST;社群有 loader 與 live preview 套件 ○ 走 REST,無現成範例
R5 從 Lovable 轉換 ○ React 元件原封搬、路由重寫,一到兩天 ○ React 元件當島嵌入、頁面骨架重寫成 .astro,兩到三天 ◎ 零成本
R6 五年不變 △ 使用率第一但滿意度 68%→55%,State of JS 2025 收到 303 則「過度複雜」抱怨;12→13→15→16 心智模型改過多次;靜態 export 是它的冷門子集,design-home 的 build.js 就是為了繞限制寫的 ◎ State of JS 2025 滿意度第一(約 91%),大版本升級附 codemod;靜態輸出是預設不是繞出來的 △ 2026-10-13 才 1.0,承諾 API 穩定,但生態最小、出事最難找答案
R7 Cloudflare Pages ○ 可以;要關 image 優化、不能有 API route、redirects 改寫成 _redirects ◎ Astro 是 Cloudflare 旗下專案,第一公民 ○ nitro 打包,可以
R8 團隊與決策者 ◎ 已上線一站、有 runbook、Payload 同一套;只多懂一種技術 △ 要多懂一種;但它是「HTML 加一點 JS」,對不熟前端的人最容易讀懂 △ 要多懂一種,而且是最新的那種
R9 範圍小而靜態 ○ 只用到 Next 的一小塊 ◎ 正是它設計的目標場景 △ 強項(型別路由、server functions)全用不到

怎麼讀這張表與最後的決定:

  • 三個框架在 R1~R3(SEO、GEO、分析)都是「內文在 HTML 裡」,差別只在配備要不要自己補;對 B2B 官網的排名影響不大。
  • Next 的優勢在 R4、R8(與 design-home 同一條生產線);Astro 在 R1、R6、R9(為內容站而生);TanStack Start 在 R5(零轉換成本)。
  • 決策者最終選 TanStack Start:骨架已經在跑、文案與版型都在,轉換只是換底不加價值;R4(Payload)之後接時走 REST 即可;R6、R8 的風險(框架新、要多懂一種)接受,出事時可把 vite.config.ts 攤平、脫離 Lovable 包裝層。一次轉換曾派出(A1)並在中途停掉,工作區已還原。

什麼情況會回頭改框架(反悔條件)

① TanStack Start 大版本不相容升級讓站起不來、或 nitro beta 出不了靜態檔而官方無修。② 官網長到上百頁、由非工程師頻繁改——Astro 的內容集合明顯好維護。③ 要與 design-home 共用 Payload 的程式碼(不只共用內容)——Next 直接贏。

目前三條都不成立。任一條成立時回來改這一節,並在 README 討論紀錄記推翻。

資料來源(2026-10 查證):TanStack Start 1.0 is Stable、State of JavaScript 2025: Meta-Frameworks、Astro vs Next.js for Static Sites: 2026 Corporate Website Stack Decision Guide、Astro vs Next.js (2026): Real Benchmarks、Astro 官方 Payload CMS 指南、What is TanStack Start (Cloudflare)。

§3

二、現況盤點(guidant-ai-website repo,Lovable 拉回的版本)

項目 現況 怎麼處理
框架 TanStack Start 1.168+TanStack Router 檔案路由+React 19+Vite 8+nitro 3.0 beta,vite.config.ts 被 @lovable.dev/vite-tanstack-config 包一層 留。vite.config.ts 加 tanstackStart: { prerender: { enabled: true, crawlLinks: true } },build 出 .output/public/ 靜態檔
路由 中文在根層、英文在 /en/ 同構一份,共 34 支;每支只做 head+掛頁面元件 留。新頁照樣中英各一支;指南用 $slug 動態路由,prerender 時列出全部 slug
頁面元件 components/pages/:HomePage 113 行、ProductPage 119 行、StubPage 12 行 不動
全站元件 components/site/:SiteHeader、SiteFooter、primitives(LLink、Logo、Placeholder、CTA) 不動
文案 i18n/zh-TW.json、en.json 各 429 行;元件不寫字串 不動;pageHead() 擴充 canonical、hreflang、og(見第四節)
樣式 styles.css 設計 token(oklch)、亮暗兩套、Tailwind 4 不動
shadcn components/ui/button.tsx 不動
<head> pageHead() 已出 title、description、og:title、og:description、twitter:card 在 pageHead() 的 links/meta 補 canonical、hreflang、og:image、og:url、og:locale
robots.txt 只放行 Googlebot、Bingbot、Twitterbot、facebookexternalhit 與 * 直接改 public/robots.txt,抄 design-home robots.ts 的 AI 爬蟲清單
sitemap、JSON-LD、llms.txt、分析工具 無 內容抄 design-home;sitemap 用 build 腳本從路由清單產到 public/sitemap.xml,JSON-LD 放 head().scripts,llms.txt 放 public/
Lovable 殘留 lib/lovable-error-reporting.ts、server.ts 錯誤包裝、@lovable.dev/* 依賴 lovable-error-reporting 與 __lovableEvents 只在 Lovable 預覽有效,留著無害;想清就刪

估工:不用搬;開 prerender 半天,SEO 十項約三天。

§4

三、TanStack Start 怎麼出靜態檔

// vite.config.ts(@lovable.dev/vite-tanstack-config 的 defineConfig 允許透傳 tanstackStart 設定)
export default defineConfig({
  tanstackStart: {
    server: { entry: "server" },
    prerender: { enabled: true, crawlLinks: true, autoSubfolderIndex: true },
  },
});

npm run build 後 .output/public/ 下每個網址一支 index.html;動態路由(指南 $slug)要在 prerender.pages 或路由的 loader 可列舉時由 crawlLinks 從清單頁連結爬到。Cloudflare Pages 輸出目錄填 .output/public。

三個坑

① prerender 時 useI18n() 靠 useRouterState 取路徑,SSR 期間可用,不必改。② localStorage 主題腳本已在 __root.tsx 用 inline script,prerender 不會執行它,正確。③ .output/public/ 已含 public/ 的靜態檔(robots.txt、sitemap.xml、llms.txt、og 圖);sitemap 用 scripts/gen-sitemap.mjs 在 build 前從路由清單產,不手寫。

§5

三之二、部署定案:Cloudflare Pages 純靜態

官網部署在 Cloudflare Pages,只上傳 .output/public/ 靜態檔,不用 Workers。

需求裡沒有任何一項需要伺服器:SEO/GEO 要的是 HTML 裡有內文,靜態檔是最強版本;Payload 改內容是觸發重 build,不是即時運算;表單是瀏覽器端打 BE。Workers 多出來的只有「每次請求即時算頁面」這一項能力,用不到卻多一層會出錯的程式、多一組 wrangler 設定、多一項帳單。

元件 放哪 備註
官網 Cloudflare Pages,純靜態 連 GitLab repo,build 指令 npm run build,輸出目錄 .output/public,push 自動部署。與 design-home 的 WEB_BUILD_MODE=export 同一條路
Payload CMS 自架 VPS,Cloudflare Tunnel 對外 之後內容要搬進去時才起;存檔打 Pages Deploy Hook 重 build,約 1~2 分鐘生效
圖片、og 圖 Cloudflare R2 與 design-home 同
表單、自家 API 現有 Guidant BE BE 對官網網域開 CORS;表單端點要有 rate limit 或驗證碼(公開可打)。也可比照 design-home 直送 Google Forms,不經 BE

Pages 上誰能 call API:

誰在 call 什麼時候 能不能 例子
使用者的瀏覽器 使用者操作時 能 送預約表單、聯絡表單
build 機器 npm run build 那一刻 能 從 Payload 抓文章、FAQ,寫進 HTML
Pages 伺服器 使用者每次開頁 不能 純靜態沒有伺服器。官網沒有「每個訪客看到不同內容」的頁,不需要

之後要轉 Workers 怎麼辦:nitro 本來就以 cloudflare 為預設目標,.output/server/ 已是 Workers 可跑的格式,關掉 prerender 改部署 server 即可,頁面程式碼不動。Pages Functions 等同把 Workers 塞回來,不用。

會需要轉 Workers 的只有兩種情況,目前都沒有:登入後才看得到或每個訪客不同的頁(官網不做登入、不做試用);編輯在 Payload 存檔前要即時預覽(design-home 也沒做,等重 build 就夠;真要做是另開一條預覽路由給編輯,官網本體不動)。

設定步驟照 design-home-fe/docs/runbooks/production-deploy.md Part B/C3(Pages)、C2(R2)、C1(Tunnel)。

§6

四、SEO(讓 Google 與 Bing 收錄)

配備 做法 design-home 參考
每頁 title、description 已有,確認每頁文案獨一無二、title 60 字內、description 150 字內 apps/web 各 page.tsx 的 generateMetadata
canonical 每頁 <link rel="canonical"> 指自己的正式網址(含網域、不含 query) 同上
hreflang 中英各一套網址,每頁互指:zh-Hant、en、x-default 指中文 本站新寫
Open Graph 圖 每頁或至少每區一張 1200×630 的 og:image,社群與 AI 引用時會抓 本站新寫
sitemap.xml prerender 自動產;確認含中英全部頁 apps/web/src/app/sitemap.ts
robots.txt 放行全部,附 Sitemap: 行 apps/web/src/app/robots.ts
效能 靜態檔天生過關,LCP 目標 2.5 秒內;圖片走 WebP、標寬高 無
送件 上線後到 Google Search Console 與 Bing Webmaster Tools 驗證網域、送 sitemap docs/ai-seo/local-presence-checklist.md
§7

五、AI SEO/GEO(讓 ChatGPT、Perplexity、Claude、Gemini 引用)

AI 搜尋引擎抓網站的方式跟 Google 不同:不跑 JS、偏好一段就能摘出答案的文字、很依賴結構化資料與站外的一致訊號。四件事:

1. 放行 AI 爬蟲(public/robots.txt)

照 design-home 已查證過的清單,User-agent: * 已放行時另外明列是「明確表態」,部分爬蟲會先找針對自己的規則:

User-agent: GPTBot
User-agent: OAI-SearchBot
User-agent: ChatGPT-User
User-agent: ClaudeBot
User-agent: Claude-User
User-agent: Claude-SearchBot
User-agent: PerplexityBot
User-agent: Google-Extended
User-agent: Amazonbot
User-agent: CCBot
User-agent: meta-externalagent
Allow: /

User-agent: *
Allow: /

Sitemap: https://<官網網域>/sitemap.xml

參考:design-home-fe/apps/web/src/app/robots.ts(內含各家 UA 的查證註解,Claude-Web 是舊名已棄用)。

2. JSON-LD 結構化資料

每頁 <head> 放一段 <script type="application/ld+json">。Guidant AI 是 B2B 軟體,型別與 design-home(室內設計、LocalBusiness)不同:

頁 schema.org 型別 關鍵欄位
全站(放在 root) Organization name 竣盟科技股份有限公司、url、logo、address、telephone、email、sameAs(LinkedIn、billows.tech)
產品總覽、功能細頁 SoftwareApplication name Guidant AI、applicationCategory BusinessApplication、operatingSystem、description、offers 不填(報價制)
指南六篇 Article headline、datePublished、dateModified、author(Organization)、inLanguage
有問答的頁(解決方案、信任中心) FAQPage mainEntity 每題一組 Question/Answer,答案第一句就是答案
每頁 BreadcrumbList 麵包屑,幫 AI 理解站的階層

參考:design-home-fe/apps/web/src/lib/jsonld.ts,注意它把 < 轉義成 < 的作法要照抄,避免文案含 </script> 把標籤提早關掉。

3. llms.txt

站根放一份純文字 /llms.txt,給 AI 助理的網站導覽:一段公司與產品介紹、主要頁面清單(每行「標題、網址、一句說明」)、指南文章清單。prerender 後放 public/llms.txt 即可,內容改動時手動更新。

參考:design-home-fe/apps/web/src/app/llms.txt/route.ts(它從 CMS 動態產,本站先寫死)。

4. 內容寫法與站外佈局

  • 問答式內容:每個解決方案頁與指南開頭放「什麼是 X、誰需要、怎麼過」三段直接回答;FAQ 每題 150 字內、第一句就是答案。AI 引用時摘的就是前兩句。
  • 實體一致:公司名、地址、電話、產品名在官網、LinkedIn 公司頁、billows.tech、G2/Capterra 等平台一字不差。AI 靠這個確認是同一家。
  • 站外訊號(B2B 版,對應 design-home 的 Google 商家與評論):LinkedIn 公司頁與產品頁、G2 或 Capterra 建產品檔案、產業媒體或合作夥伴(CMMC C3PAO、顧問公司)的報導與連結、GitHub 若有開源元件。這些是「別人怎麼說你」,官網做得再好、站外空白的公司 AI 不敢推薦。

參考:design-home-fe/docs/ai-seo/faq-draft.md(FAQ 寫法原則)、local-presence-checklist.md(站外清單的結構,內容要換成 B2B 平台)。

§8

六、商業分析怎麼接

要看什麼 工具 做法
流量、來源、停留、轉換 GA4(或 Plausible、Cloudflare Web Analytics 等不用 cookie 同意的方案) 在 root 加 script;預約展示與聯絡表單送出設成轉換事件
Google 收錄、查詢詞、點閱率 Google Search Console 驗證網域、送 sitemap
Bing 收錄 Bing Webmaster Tools 同上,ChatGPT 搜尋的索引來源是 Bing
AI 搜尋帶來的流量 GA4 自訂管道群組 把 referrer 含 chatgpt.com、perplexity.ai、claude.ai、copilot.microsoft.com、gemini.google.com 歸成「AI 搜尋」管道
對外投放與引用 UTM 參數 所有對外貼的連結帶 utm_source/utm_medium,才分得出 AI 引用與自然搜尋
有沒有被 AI 引用 手動抽查 每月用 ChatGPT、Perplexity 問「CMMC 合規管理工具」「ISO 27001 稽核軟體 台灣」之類的題,看有沒有出現 Guidant AI
§9

七、驗收:AI 看得到內容嗎

開 prerender、部署後,用爬蟲身分抓原始 HTML,不是用瀏覽器看:

# 數字是 0 代表 AI 爬蟲看到的是空殼
curl -s -A "GPTBot" https://<官網網域>/ | grep -c "Guidant"
curl -s -A "ClaudeBot" https://<官網網域>/solutions/cmmc | grep -c "CMMC"

# 每一支路由都要有靜態檔(本機 build 後)
find out -name index.html | wc -l                # 應等於 17 頁 × 2 語言 = 34(加指南 6 篇 × 2)

# 結構化資料驗證
# https://validator.schema.org/ 貼網址
# https://search.google.com/test/rich-results

另外 robots.txt、sitemap.xml、llms.txt 三支用瀏覽器直接開要能看到內容。

§10

八、待做清單(對應 Notion 子卡 CM-2940~2953)

# 做什麼 檔案
1 開 prerender、確認 .output/public/ 每頁一支 index.html(併入 B1 第一步) vite.config.ts
2 pageHead() 補 canonical、hreflang(zh-Hant/en/x-default)、og:url、og:image、og:locale src/i18n/index.ts、各 routes/**.tsx
3 public/robots.txt 放 AI 爬蟲清單與 Sitemap 行 public/robots.txt
4 sitemap 產生腳本,中英全頁 scripts/gen-sitemap.mjs → public/sitemap.xml
5 JSON-LD 工具與五種型別,root 掛 Organization,各頁掛對應型別 src/lib/jsonld.ts、__root.tsx、各路由 head()
6 寫 llms.txt public/llms.txt
7 每區一張 og:image(1200×630) public/og/
8 接分析工具、設轉換事件、AI 搜尋管道群組 __root.tsx、Cloudflare 後台
9 Cloudflare Pages 專案連 GitLab repo,build npm run build,輸出 .output/public Cloudflare 後台
10 上線後 Search Console、Bing Webmaster 驗證與送 sitemap 後台操作
11 B2B 站外佈局清單(LinkedIn、G2/Capterra、合作夥伴連結)給決策者排程 本 FR 新文件

網址先用 VITE_SITE_URL 環境變數佔位(Vite 的公開變數要 VITE_ 前綴)。

§11

九、參考資料

公司內部(抄內容,寫法改 TanStack):~/Projects/Design-Home/design-home-fe/

檔 看什麼
apps/web/src/app/robots.ts AI 爬蟲 UA 清單與查證註解
apps/web/src/app/sitemap.ts sitemap 結構
apps/web/src/lib/jsonld.ts JSON-LD 工具、< 轉義
apps/web/src/app/llms.txt/route.ts llms.txt 內容骨架
apps/web/scripts/build.js 產 _headers 的做法
apps/web/src/app/layout.tsx、(main)/*/page.tsx 每頁 meta 要有哪些欄位(寫法不同,欄位照抄)
apps/web/src/lib/payload.ts 官網 build 時打 Payload API 的資料層(含租戶過濾),未來接 CMS 直接抄
docs/decisions/ADR-003、ADR-005 為什麼選純靜態+Cloudflare Pages+webhook 重 build
docs/runbooks/production-deploy.md Cloudflare Pages、R2、Tunnel、Access 的設定步驟
docs/ai-seo/faq-draft.md、local-presence-checklist.md FAQ 寫法、站外佈局清單結構
apps/admin/src/hooks/revalidateWeb.ts Payload 改內容後打 Deploy Hook 重 build(未來接 CMS 用)

外部: