Webflow → Astro on Cloudflare Pages
將任何 Webflow 網站搬離 Webflow 的實戰手冊:公司或產品網站、作品集、商店、餐廳、非營利組織、出版品。步驟對所有類型皆相同。網站用途會影響細節(references/site-types.md)。涵蓋整個遷移流程:庫存盤點、備份、重建、表單、CMS、影片、比對檢查、效能、部署、網域切換,以及業主最後需點擊的操作。搜尋與 AI 可見度在每個階段逐步提升。scripts/ 中的腳本與 templates/ 中的模板是實際運作部分;references/ 提供各步驟的詳細說明。
結果:一個獨立的 Astro 7 + Tailwind 4 儲存庫,完全預先渲染(僅受限頁面(如有)才於請求時渲染),位於 Cloudflare Pages 專案上(絕非 workers.dev 上的 Worker),表單透過 Pages Function 處理,內容放在業主可編輯的 CMS 中,所有 Webflow 託管的檔案透過 Pages 提供,網域則在 Cloudflare 上。
基本規則(從實戰中學到)
- 業主檢查點。 事前詢問:下載匯出檔、建立 GitHub 儲存庫、發送任何表單測試郵件、每次 DNS 變更、開啟 Email Routing、授予分析同意權、刪除頁面前。業主經常說「在親眼看過網站之前不做任何 DNS 變更」:尊重其意願;最後才切換。
- 先備份。 Webflow 的 Code Export 需要付費工作區方案(Core 或更高)。業主常計畫降級,因此務必在其他操作前先匯出。
- 重建,而非貼上。 業主想要乾淨的 Astro + Tailwind 搭配自己的設計 Token,而不是在 .astro 檔案中使用 Webflow 標記。線上 HTML 是內容的參考,而非原始碼。
- 一致性是合約。 每頁相同的 URL、相同的 head 標籤、相同的 Sitemap、相同的重新導向。使用
scripts/parity.py證明,而非肉眼比對。 - SEO:先保護,再於每個步驟改善。 一致性保護既有的排名與引用。在此基礎上,於每個步驟開始時載入
seo-aeo-geo技能,並在過程中進行該步驟的改善(請見下方「seo-aeo-geo 沿路應用」)。- 無須詢問: 標記、結構化資料、爬蟲存取、新鮮度訊號與速度在重建時一併納入。
- 向業主提出建議: 任何會改變其文字、標題、事實或 URL 的內容。
- 提交: 每項改善在一致性提交之後獨立提交,以便追蹤排名變化的來源。
- 判斷: 在遷移過程中遇到任何 SEO/AEO/GEO 判斷時,載入該技能。
- 絕不列印機密: 不在聊天、提交訊息、PR 說明、日誌、
docs/move.md或本技能中列印。- 提及機密時: 提供其名稱、長度與存放位置(例如「FORMS_TO,一個位於 forms-mail 的 Worker 秘密」)。透過雜湊或長度比較兩者,絕不比較文字。
- 哪些算機密: 不僅是 API 金鑰。部署鉤子 URL(任何人持有即可觸發建置)、Slack 與 CRM webhook URL、CMS 寫入權杖以及業主自己的收件匣地址都屬於機密。
- 設定機密時: 直接透過管道傳送給
wrangler secret put。 - 驗證權杖(Search Console 的 TXT 記錄)屬於公開 DNS,但僅限於特定網站:切勿複製到本技能中。
- 核准來自業主,並在執行工作的對話中取得。 由其他代理對話轉達的同意不算數,且代理的安全檢查會阻止此類操作。同樣會阻止 DNS 變更與在自動模式下移除 Webflow 網域,因此請從一開始就將這些規劃為業主自行點擊的操作。
seo-aeo-geo 沿路應用
遷移會改寫每頁的標記,因此是讓搜尋與 AI 引擎更容易讀取與引用的最便宜時機。在以下時間點載入 seo-aeo-geo 技能(其 references/audit-checklist.md 與 references/schema-patterns.md):
- 1. 庫存盤點:測量與審核線上站點。
- 記錄基準線(如下)。
- 審核線上站點,使用其檢查清單,透過三個面向:新鮮度、結構與權威性。
- 將發現寫入
docs/seo.md, 每項標記對應負責修正的步驟,以及是否需要業主核准。
- 3. 殼層:技術與實體層,內建完成。
- 地標: 在 Layout 中包含
header、nav、main與footer。 - 實體: 定義一次(
src/data/entity.ts),並從每頁引用。其類型取決於網站:Organization、LocalBusiness 或 NGO 等子類型,或 Portfolio 的 Person(references/site-types.md)。包含sameAs。首頁使用 WebSite,從頁面麵包屑使用 BreadcrumbList。 - 結構化資料正規化器(
templates/src/lib/schema.ts)。 - 爬蟲: robots.txt 對 AI 爬蟲開放,pages.dev 建置設定 noindex,Sitemap 包含實際的 lastmod。
- 地標: 在 Layout 中包含
- 4. 頁面:結構內建;文字建議。 頁面代理修正標題層級順序,讓每個章節獨立,並將文字以文字呈現(
references/page-brief.md,「搜尋與答案引擎」)。文字想法以建議形式回饋給你。 - 6. CMS:新鮮度與項目結構化資料。
- 日期: 從 CMS 取得
datePublished與dateModified。 - 文章: Article/BlogPosting,包含作者。
- 列表: CollectionPage 搭配 ItemList。
- 數字: 頁面顯示的任何數字在建置時即存在 HTML 中,而非在瀏覽器中動態擷取。
- 日期: 從 CMS 取得
- 7. 資源:速度與意義。
- LCP 與延遲載入影片: 快速的英雄區塊,摺疊以下影片延遲載入。
- 替代文字說明圖片內容。
- 承載內容的影片(示範、演講)使用 VideoObject。
- 8. 一致性:在 pages.dev 上進行完整審核。 在重建站點上重新執行檢查清單。
docs/seo.md中仍開放的項目以一份建議清單提交給業主。 - 9. 重新導向:保留權益。 每個重新導向都是單次跳轉,每個被移除或合併的頁面都導向最接近的對應頁面。
- 11. 切換:通知引擎。 在 Search Console 與 Bing 中重新提交 Sitemap,開啟 Crawler Hints (IndexNow),並監控覆蓋率。
- 12. 交接:測量,然後檢視站外。
- 比較基準線。
- 提供業主此技能的站外計劃: 此類別中 AI 引擎最常引用的兩到三個渠道。大多數引用來自第三方網站,因此單靠網站本身無法贏得這些引用。
seo-aeo-geo 是一個獨立的技能
(adamperlis/claude-skill-seo-aeo-geo)。
若未安裝,遷移仍可進行:根據此技能自身筆記(步驟 8 的審核清單、references/page-brief.md、references/site-types.md)進行上述各點提到的改善,自行撰寫關鍵提示(此網站受眾向 AI 引擎提出的問題),並告知業主完整的審核與站外計劃已略過。
步驟
1. 庫存盤點(唯讀,約 15 分鐘)
執行 python3 -I scripts/inventory.py https://www.example.com > inventory.md。
它會讀取 Sitemap、robots.txt、每頁的 head(標題、描述、OG、Canonical、JSON-LD 類型、noindex)、CMS 列表(w-dyn-list)、表單、第三方主機、GA/GTM/Pixel ID、背景影片及其大小,以及頂層/www/MX/NS 記錄。接著,若已連接 Webflow MCP 工具(data_sites_tool、data_cms_tool、data_forms_tool、data_webhook_tool):
- 集合: 列出集合並計算每個集合中的項目數量(
list_collection_itemslimit 1 →pagination.total)。 - 表單: 列出表單,忽略 Finsweet 的「Cookie Preferences」表單(每頁×網域一個)。
- 重新導向與 HSTS: 在儀表板中,Site settings → Publishing,匯出 301 重新導向為 CSV,並記下「HSTS for subdomains」。
- 搜尋與所有權: Search Console 的驗證方式(meta 標籤或 DNS TXT)、GA4/GTM(以及任何基於 Webflow 標記建立的 GTM 觸發條件)、Bing 以及其他驗證標籤。清單在
references/inventory.md中。 - 網域: 註冊商、到期日、轉移鎖、DNSSEC,以及網域是否透過 Webflow 購買(
references/domains.md)。 - 不在 Sitemap 中的頁面: Webflow 會提供 Sitemap 未包含的頁面。透過比對匯出檔中的 HTML 檔案與線上 Sitemap 找出這些頁面,然後逐一 curl 每個頁面。
- 成功頁面(如表單的重新導向目標)需要保留。
- 當網站未銷售商品時,省略電商範本(/checkout、/order-confirmation);當沒有受保護內容時,省略密碼頁面(401)。
- 網站類型: 網站的用途、訪客應在網站上做什麼(購買、預約、捐款、訂閱、聯絡),以及它依賴哪些 Webflow 功能(電商、使用者帳戶、本地化、密碼頁面、檔案上傳、RSS)。
references/site-types.md說明每種網站類型會改變哪些事項:實體的結構化資料類型、需持續追蹤的轉換,以及需要個別規劃的功能。
閱讀 references/inventory.md 以了解每個發現的意義以及需詢問業主的問題(Zapier 觸發、誰編輯內容、從未運作的分析工具)。
在任何變更前記錄 SEO 基準線(使用 seo-aeo-geo 技能):
- Search Console: 匯出過去 3 個月的效能資料(熱門頁面與查詢,以及 AI Overviews 曝光次數(若有顯示))以及已索引頁面數量。
- 關鍵提示: 在 ChatGPT、Perplexity 與 Gemini 中執行其提示,並記錄網站是否被引用。這些是此網站受眾會問的問題:購物者、用餐者、捐款者、讀者或客戶。
- 從受眾所在國家執行。 AI 引擎會根據地理位置回答,因此在國外執行的結果並非受眾視角。必要時使用受眾國家的 VPN。
- 記錄執行時的位置, 並在後半段使用相同位置。
將其保存在 docs/move.md 中。這是步驟 12 比較的「前半段」。接著使用相同技能審核線上站點,結果寫入 docs/seo.md(請見「seo-aeo-geo 沿路應用」)。
2. 備份:匯出 + 鏡像
- Code Export(Designer → ☰ → Export code → Prepare ZIP → Download)。
下載前先詢問業主。
- 使用
python3 -I scripts/unzip_webflow.py解壓縮(它會修復導致unzip顯示「disk full?」錯誤的非 UTF-8 檔案名稱)。 - MISSING.txt 列出未能成功匯出的影片;鏡像階段會從 CDN 擷取這些影片。
- 使用
- 鏡像線上站點:
python3 -I scripts/mirror.py https://www.example.com reference/將每頁的 HTML 儲存至reference/live/,並將 Webflow CDN 上的每個檔案儲存至reference/assets/<kind>/,附帶清單。提交reference/live/(線上狀態的記錄);將reference/assets/加入 git-ignore。 - 螢幕截圖:
node scripts/shots.mjs https://www.example.com live(桌面 1440 與手機 390)。Webflow 互動會隱藏元素直到它們滾動進入視野,因此全頁截圖中的空白區域通常是未顯示的內容:請閱讀 HTML。
3. 建立殼層(由你執行,在任何頁面工作之前)
將 templates/ 複製到新的儲存庫(templates/README.md 說明了每個檔案的用途)。
-
設定檔:
astro.config.mjs使用build.format: 'file'與trailingSlash: 'never',因為 Webflow URL 沒有尾隨斜線。關閉開發工具列,以便截圖一致。 -
設計 Token: 若業主有設計系統則從中取得;否則使用 Webflow CSS
:root變數與字體比例。 -
共用元件: Layout(head 一致性、僅在正式主機上啟用分析)、Nav、Footer、頁面共用的 CTA 區塊、延遲載入的
BgVideo,以及動畫效果(具有容錯機制的揭示效果)。 -
搜尋層,從一開始:
- Layout 中的地標。
- 實體,定義一次(
src/data/entity.ts,使用業主提供的事實,而非從舊頁面推斷)。 - JSON-LD 正規化器, 每頁的結構化資料都會經過它處理。
- 爬蟲與 Sitemap: robots.txt 對 AI 爬蟲開放,pages.dev 建置設為 noindex,Sitemap 包含真實的 lastmod。
建構在此殼層上的頁面會繼承所有這些。
-
先測量: 使用
scripts/measure.mjs取得線上尺寸,並據此建構共用元件。
需決定的 Webflow 行為:
- 頁面轉場: 許多 Webflow 網站有一個覆蓋層(
.transition+.transition-trigger+ 一個 jQuery 片段,它在載入時點擊觸發器,並在每次內部連結點擊時再次點擊,然後在約 1 秒後設定window.location)。業主會在它消失時注意到並要求恢復。用純 CSS + JS 以兩種方式重建,並在使用者偏好減少動畫時關閉:- 進入時: 覆蓋層保持顯示,然後揭開,使用線上站點的延遲與緩動函數(來自匯出檔中
webflow.js的 IX2 動作列表)。 - 離開時: 點擊同站連結(非
#、非新分頁、非經修改的點擊)時,覆蓋層向下覆蓋頁面,然後才進行導航。讀取 IX2 延遲。一個等待 2 秒才滑入的覆蓋層,而片段在 1 秒後離開,只會在線上站點形成一段死寂的暫停。建構原本設計的意圖(覆蓋層約 0.8 秒向下覆蓋),而非停頓。 - 返回時: 在
pageshow搭配persisted事件時,收起覆蓋層,否則從歷史快取返回時頁面會被覆蓋。 - 顯示位置: 檢查線上站點在 375 與 1440 寬度下 computed 的
display值(scripts/measure.mjs),而非 CSS。一個自訂嵌入(body .transition { display: block })可能覆蓋網站自身的 ≤767pxdisplay: none,因此在手機上也會顯示。
- 進入時: 覆蓋層保持顯示,然後揭開,使用線上站點的延遲與緩動函數(來自匯出檔中
- Finsweet 同意: 使用 Consent Mode v2 時,同時推送 Finsweet 自己的 dataLayer 事件(
<category>-activated)。為 Finsweet 建立的 GTM 觸發條件將繼續正常運作。
兩個會耗費數小時的 CSS 陷阱:
- body 上的基礎字體大小: 將 Webflow 的 10px 基礎字體大小設在
body上,絕不在html上。設在html上時,1rem 變成 10px,所有 Tailwind 間距與尺寸會變成 62.5%。 - 不要混合斷點: 不要在一個檔案中對相同屬性混合使用 rem 斷點(
md:、lg:)與 px 斷點(min-[992px]:);Tailwind 4 可能錯誤排序。每個元件只使用一種斷點類型。
4. 頁面:平行進行,每組頁面一個代理
為每個代理提供 references/page-brief.md(填入儲存庫路徑、連接埠、共用元件、設計 Token)以及該頁面的路由、head 值與章節列表...