從 Vercel 遷移至 Cloudflare Workers + Railway,如何讓我們的網站基礎架構成本降低 80%

我們將網站技術架構從 Vercel 遷移至 Cloudflare Workers 和 Railway,並依據各項工作負載的實際需求,改用 TanStack Router、TanStack Start 和 Hono 的組合,取代 Next.js。結果,基礎設施成本降低了約 80%;在邊緣端過濾惡意自動化流量後,降幅進一步提高至約 90%。大部分遷移工作由 AI 程式開發代理完成,工程師則專注於架構設計、限制條件、審查及正式環境驗證。

2026年9月23日

Quang Lam · Founder & CEO

從 Vercel 遷移至 Cloudflare Workers + Railway,如何讓我們的網站基礎架構成本降低 80%

多年來,在 WebCatalog,我們幾乎所有網頁專案都預設部署在 Vercel。這些專案大多使用 Next.js,因此兩者搭配起來很自然。我們的行銷網站、產品介面、帳號設定、開發者主控台、身分驗證、管理工具和 API,最後都採用了大致相同的技術堆疊。

這套做法在很長一段時間裡都運作得很好。Vercel 讓部署變得簡單,Next.js 提供了高生產力的全端框架,而我們幾乎不必操心兩者底層的基礎設施。

但最終,這份便利不再符合我們產品的需求。

我們的行銷網站仍然受益於 SSR(伺服器端渲染),但其他大多數網頁應用程式並不需要。它們完全可以是一般的 SPA(單頁應用程式)。我們的 API 則需要支援原生相依套件與長時間執行程序的標準 Node.js 環境。前端技術堆疊中,幾乎所有其他專案也都已經在使用 Vite。與此同時,基礎設施成本越來越難以忽視,尤其是在公開流量和自動化流量持續增長的情況下。

一開始,我們試著最佳化現有架構。我們考慮保留 Next.js,透過轉接層將它搬到 Cloudflare Workers。我們試著讓行銷網站使用 Astro,也曾短暫將 API 放在 Cloudflare Containers 上。

最後,我們採用了更簡單的配置:現在大多數網頁應用程式都使用部署在 Cloudflare Workers 上的 Vite + TanStack Router;行銷網站使用部署在 Cloudflare Workers 上、具備 SSR 的 TanStack Start;API 則使用 Hono + tRPC,以長時間執行的 Node.js 容器形式部署在 Railway。

這次遷移讓我們的網頁基礎設施成本降低了約 80%。之後,我們進一步收緊 Cloudflare 的安全規則,在邊緣節點封鎖更多惡意自動化流量,總節省幅度因此提高到約 90%。

大部分遷移實作也都是由 AI 程式設計代理完成,主要使用 Claude Fable 5 和 Claude Opus 5。工程師則負責決定架構、界定限制、審查變更並驗證成果。

我們一開始並沒有打算離開 Vercel

我們的第一個念頭是最佳化現有配置。

webcatalog.io 是最明顯的起點:它有大量公開流量,但大多數頁面變動相對不頻繁。我們發現網站仍有太多內容採用動態渲染,因此修正了非預期的動態渲染、為高流量路由加入 ISR(增量靜態再生)、讓更多頁面可以快取,並最佳化耗費資源的網站地圖處理流程。

這些工作確實有幫助,但也讓底層問題更加清楚:我們花越來越多時間弄明白,為什麼相對靜態的內容會被動態渲染、是哪個框架行為造成的,以及得引入哪項框架機制才能讓它重新變得可快取。

與此同時,其他許多應用程式的情況正好相反。帳號設定、開發者主控台、管理應用程式,以及大多數產品介面,根本不需要伺服器端渲染。它們只是與 API 通訊的瀏覽器應用程式。

到了這個階段,問題不再是「怎麼降低 Vercel 帳單?」,而變成「如果這些應用程式原本不是 Next.js 專案,我們會怎麼建構它們?」

我們考慮過在 Cloudflare 上執行 Next.js

變動最小的選項,是保留 Next.js,將它從 Vercel 搬到 Cloudflare Workers。

我們認真考慮過這個方案,因為 Next.js 可以透過轉接層在 Cloudflare 上執行,讓我們保留大部分現有的應用程式結構。

但我們不認為在 Cloudflare 上執行 Next.js,等同於在 Vercel 上執行 Next.js。Next.js 與 Vercel 的執行環境、部署系統、快取行為及平台功能整合得最緊密。在 Cloudflare 上,轉接層必須將這些預設前提轉換到另一種執行環境。

這樣做可以運作得很好,但如果我們的目標是簡化技術堆疊,再增加一層相容機制就不太理想。

工具方面還有一個更大的問題。我們正在開發的其他前端專案幾乎都已使用 Vite:桌面應用程式、瀏覽器擴充功能、SPA,以及其他用戶端專案。

Next.js 已大幅轉向 Turbopack,而 Turbopack 也有很大的進步。這並不是對效能的抱怨。對我們而言,更大的差異在於生態系。Vite 已經是大部分前端程式碼的共同基礎,擁有更廣泛的外掛生態系,而且各專案之間也能重複使用更多設定。

因此,將 Next.js 保留並搬到 Cloudflare,雖然能解決一部分託管問題,卻會讓框架不一致的問題,以及獨立的建置工具生態系繼續存在。

於是,我們退一步,重新檢視各種工作負載實際需要什麼。

我們依工作負載拆分技術堆疊

當我們不再尋找一個通用的 Next.js 替代品,架構就變得簡單多了。

行銷網站需要 SSR,因為它們有公開且多語系的頁面、中繼資料、標準網址、網站地圖,以及來自搜尋引擎的流量。

其他大多數網頁應用程式不需要 SSR,直接採用 Vite 搭配 TanStack Router 即可。

API 完全不需要 React 框架,更適合在標準的 Node.js 執行環境中運作。

最後的劃分如下:

網頁應用程式
Vite + TanStack Router
        │
        └── Cloudflare Workers

行銷網站
TanStack Start + SSR
        │
        └── Cloudflare Workers

API
Hono + tRPC
        │
        └── Railway / Node.js 容器

原則變得很直接:SSR 只用在真正能帶來價值的地方;不需要 SSR 時,就使用一般的 SPA;後端服務則在後端執行環境中運作。

大多數網頁應用程式都變成了 SPA

遷移 SPA 是最容易的一部分。

WebCatalog 網頁應用程式的遷移尤其迅速:我們建立 Vite + TanStack Router 替代版本的初始架構、移植介面、將部署改到 Cloudflare Workers,並在同一個上午移除了舊版 Next.js。

我們用同樣的方式遷移了 Lexibird 網頁應用程式、帳號設定、開發者主控台、身分驗證和管理應用程式。從建立第一個 SPA 初始架構,到刪除最後一個 Next.js 應用程式,總共花了約 19 天。

對這些應用程式來說,Cloudflare Workers 的運作方式刻意保持單純。Vite 建置靜態資源、Cloudflare 提供這些資源,而應用程式路由則回退至 index.html。不需要 SSR 執行環境,因為根本沒有任何內容需要伺服器端渲染。

比較困難的是保留這些應用程式長期累積下來的行為。部分身分驗證邏輯必須從 Next.js 伺服器路由移到 API;舊版 WebCatalog 桌面應用程式也仍依賴一些舊有端點,我們必須確保它們繼續運作。

搬移 React UI 很容易。

維持既有介面契約比較難。

我們試了 Astro,五天後就把它刪掉了

行銷網站比較棘手。

我們的首選是 Astro。webcatalog.io 和 lexibird.com 都是公開、內容豐富的網站,而 Astro 與 Cloudflare 的整合也很好,因此看起來相當適合。

我們開始移植這兩個網站,但五天後就停了下來。

沒有哪一個單獨的問題足以讓我們放棄;相反地,是一些小小的不匹配不斷累積。有些路由需要在預先渲染時存取資料庫,但我們刻意不在建置環境中提供正式環境的資料庫憑證。React islands 也讓共用應用程式狀態變得比較不自然。此外,我們還遇到預先渲染路由與執行時渲染路由之間的差異,以及儲存庫中的一些工具使用摩擦。

這些問題沒有一個是無法解決的,而這正是繼續做下去的風險。我們很容易再花上幾週,一個接一個地修正問題。

因此,我們刪除了這份實作,重新開始。

AI 代理改變了這個決定的成本。大量機械性的移植工作並未耗費工程師數週的時間,因此沉沒成本較低,也比較容易承認我們更偏好另一種架構。

TanStack Start 更適合我們

我們從原本的 Next.js 程式碼重新開始遷移行銷網站,這次改用 TanStack Start。

它更自然地融入我們的架構,因為 SPA 應用程式已經在使用 TanStack Router,因此路由、載入器、搜尋參數和導覽都遵循類似的概念。程式碼仍是一般的 React 和 TypeScript,建置系統則是 Vite。

我們一天內就將 lexibird.com 移植到 TanStack Start,接著也完成了 webcatalog.io。

它的優勢並不是 TanStack Start 神奇地解決了所有問題,而是它契合我們整體技術堆疊的發展方向。

在單一儲存庫中,這點很重要。共用建置工具代表更多可重複使用的外掛與設定、除錯時更少特殊情況、開發者更少需要切換思維脈絡,也讓程式設計代理需要摸索的專案專屬慣例變少。

對我們的流量而言,Cloudflare 便宜得多

架構只是帳單下降的部分原因。Cloudflare 的定價是重要因素,尤其是頻寬。

目前 Cloudflare Workers 付費方案主要依請求數和 CPU 使用量計費,且不另收 Workers 的資料傳輸或頻寬費用。(developers.cloudflare.com) 對公開網站來說,這點影響很大,因為一次請求可能只使用很少的 CPU,卻仍會傳輸 HTML、JavaScript、圖片、字型及其他資源。

Vercel 的定價模式則包含與網路相關的用量類別和額度,其中包括 Fast Data Transfer 和 Fast Origin Transfer。(vercel.com) 以我們的流量組成而言,Cloudflare 的成本效益明顯更好。

節省的費用並非只來自更換服務供應商。我們也將許多應用程式改為靜態 Vite 建置、減少不必要的 SSR、讓快取策略更明確,並將 API 工作負載搬到更合適的執行環境。但 Cloudflare 的定價,特別是頻寬方面,確實是我們觀察到成本降低約 80% 的重要因素。

這個數字只適用於我們的工作負載,並不是說每個從 Vercel 搬到 Cloudflare 的應用程式都能省下 80%。

API 最後搬到了 Railway

API 是另一個獨立的問題。

雖然它們是 Next.js 專案,實際上卻已經大致獨立於 Next.js。WebCatalog API 約有 34,000 行程式碼,但只有七個檔案匯入 next/server。tRPC 已經在使用 Fetch 轉接器,而 Inngest 也支援 Hono。

我們以 Hono 取代 Next.js 的 HTTP 外層。這部分的改動小得令人意外。

比較困難的問題是該把它部署在哪裡。一般的 Workers 並不適合,因為這些 API 會使用 Node.js,以及 sharp 這類原生相依套件。

我們起初試了 Cloudflare Containers,很快便得以脫離 Vercel。但這套配置在正式環境運作一段時間後,我們發現它所需的手動容量調整,比當時我們想投入的更多。

因此,我們又搬了一次 API。

如今,它們在 Railway 上以一般長時間運作的 Node.js 容器服務執行。CI(持續整合) 負責建置 Docker 映像檔並推送到我們的映像檔儲存庫,Railway 則負責水平自動擴展。

不是所有東西都需要在邊緣節點執行。

離開 Vercel 意味著必須自己承擔更多責任

較低的成本伴隨著取捨。

Vercel 和 Next.js 過去在較高層次處理了許多基礎設施行為。改用 TanStack Start 和 Workers 後,我們轉向明確的 HTTP 快取策略:頁面完成渲染後,我們回傳快取標頭,再由 Cloudflare 快取回應。瀏覽器和邊緣節點可以採用不同的快取策略,包括邊緣節點的 stale-while-revalidate 行為。

我們喜歡讓這些決策變得明確,但基礎設施一旦由自己明確設定,犯下的錯也得自己承擔。

我們確實犯了幾個錯。有一條正式環境的快取規則意外匹配了訪客專屬的 TanStack 伺服器函式回應;另一項快取設定則與 SPA 的回退行為產生了不良交互作用。這些錯誤提醒我們,光看程式碼儲存庫,無法完整證明遷移的正確性。正式環境的基礎設施也是系統的一部分。

AI 代理改變了我們的遷移方式

大部分實作工作由程式設計代理完成,主要使用 Claude Fable 5 和 Claude Opus 5。

框架遷移特別適合交給代理,因為大部分工作都有清楚的現有實作可供參考:搬移這條路由、保留這個網址、替換這項框架 API、維持相同的中繼資料、執行型別檢查、修正錯誤,然後與舊版實作比較結果。

針對 webcatalog.io,我們維護了一份遷移追蹤表,將工作分為基礎架構、產品頁面、目錄頁面、搜尋、部落格、價格資訊、重新導向、網站地圖,以及正式切換等階段。我們沒有要求代理「將 webcatalog.io 遷移到 TanStack Start」,而是交給它範圍明確、附有必須維持不變條件的任務。

既有應用程式成了規格。代理的工作是使用新架構重現原有行為,而不是重新發明產品。

這讓人類投入的心力從手動轉換程式碼,轉向思考以下問題:這個頁面真的需要 SSR 嗎?哪些行為是刻意設計的?哪些內容可以全域快取?哪些網址必須完全不變?身分驗證應該在哪裡進行?什麼時候該停止嘗試讓某種做法行得通?

我們仍然審查產出、執行建置與型別檢查,並手動驗證身分驗證、SEO(搜尋引擎最佳化)、重新導向和快取等風險較高的部分。

重要的改變並不是 AI 幫我們寫了程式碼,而是 AI 降低了實驗成本。

試用 Astro 五天後就刪除它,變得更容易做出這個決定。先搬移一次 API,之後又認定另一個平台更合適,也沒那麼令人難以接受。機械性工作的成本降低了,因此當架構方向不對時,我們可以轉向,而不是因為已經投入太多就硬著頭皮做下去。

代理讓實作不再那麼稀缺。

這也讓架構、限制條件、審查和驗證變得更加重要。

從節省 80% 到約 90%

遷移後,我們的網頁基礎設施成本降低了約 80%。

接著,我們開始更加留意:究竟有哪些請求會送達應用程式。

公開網際網路上的流量,有相當一部分來自自動化程式:搜尋引擎、AI 爬蟲、代理、資料擷取程式、掃描程式,以及不那麼友善的機器人。有些流量是有用的,有些則不是。

應用程式直接位於 Cloudflare 後方後,我們收緊了安全規則,讓惡意自動化流量在邊緣節點就被拒絕,不必觸發應用程式運算或資料庫作業。

完成這一步後,與舊配置相比,我們的總節省幅度達到約 90%。

最終的成本降幅來自多項因素共同作用:Cloudflare 較低的定價,尤其是頻寬方面;更少的伺服器端工作;更適合 API 的執行環境;更明確的快取策略;以及一開始就避免不必要的請求送達應用程式。

最後的架構

我們的儲存庫中已經沒有任何 Next.js 應用程式。大多數網頁應用程式現在都是部署在 Cloudflare Workers 上的 Vite + TanStack Router SPA。webcatalog.io 和 lexibird.com 使用部署在 Cloudflare Workers 上的 TanStack Start,因為這兩個網站確實能從 SSR 受益。我們的 API 使用部署在 Railway 上的 Hono + tRPC,以一般 Node.js 容器的形式執行。現在幾乎所有前端專案都在 Vite 生態系中。

我們的基礎設施成本降低了約 80%;Cloudflare 對我們的流量而言明顯更划算,尤其是頻寬方面,對此貢獻良多。在邊緣節點過濾惡意自動化流量後,整體節省幅度進一步提高到約 90%。

但我們最在意的成果是架構本身:行銷網站使用 SSR;凡是可以做成 SPA 的其他應用程式,就做成 SPA;而當使用真正的伺服器更簡單時,API 就在真正的伺服器上執行。

我們起初只是想降低 Vercel 的費用。

最後才發現,我們付費使用的架構,其實有很大一部分根本不需要。