部署到 Cloudflare Workers
adapter 設定、自訂網域、環境變數與 secret、預先產生的靜態檔,以及上線前的檢查。
1. 設定
npm i -D @sveltejs/adapter-cloudflare wrangler
// svelte.config.js
import adapter from '@sveltejs/adapter-cloudflare';
export default {
kit: {
adapter: adapter({
routes: { include: ['/*'], exclude: ['<all>'] }
})
}
};
exclude: ['<all>'] 的意思是「所有能當靜態檔送的東西都不要進 worker」。這讓資產由 Cloudflare 的邊緣直接送出,不算 worker 請求數,也更快。
# wrangler.toml
name = "perhapx-web-design"
main = ".svelte-kit/cloudflare/_worker.js"
compatibility_date = "2025-09-01"
compatibility_flags = ["nodejs_compat"]
[assets]
binding = "ASSETS"
directory = ".svelte-kit/cloudflare"
[[routes]]
pattern = "web-design.perhapxin.com"
custom_domain = true
[observability]
enabled = true
nodejs_compat 在用到 node: 開頭的模組時需要(例如某些 markdown 或加密套件)。不確定就先開著,成本是零。
2. 自訂網域
custom_domain = true 會請 Cloudflare 自動處理 DNS 與憑證,前提是這個網域已經在你的 Cloudflare 帳號裡。
流程:
- 確認
perhapxin.com的 DNS 由 Cloudflare 管理 npx wrangler deploy- Cloudflare 自動建立
web-design的 CNAME 並簽發憑證 - 第一次通常一到兩分鐘生效
如果網域不在 Cloudflare,要改用 [[routes]] 搭配 zone,並自己處理 DNS。
3. 環境變數與 secret
| 種類 | 放哪裡 | 誰看得到 |
|---|---|---|
| 公開設定 | wrangler.toml 的 [vars] |
所有人(會進 git) |
| 金鑰 | wrangler secret put NAME |
只有 worker |
| 本機開發 | .dev.vars(要 gitignore) |
只有你 |
npx wrangler secret put ANTHROPIC_API_KEY
在程式碼裡:
// 只能在 .server.ts 檔案裡 import
import { ANTHROPIC_API_KEY } from '$env/static/private';
或從 platform 拿(Workers 的執行期綁定):
export const POST = async ({ platform }) => {
const key = platform?.env?.ANTHROPIC_API_KEY;
};
永遠不要把金鑰放進 [vars] —— 那個檔案會進 git。
4. 預先產生(prerender)
純計算出來的東西應該在 build 時就變成靜態檔:
// src/routes/themes.css/+server.ts
export const prerender = true;
export function GET() {
return new Response(allThemesCss(), {
headers: { 'content-type': 'text/css; charset=utf-8' }
});
}
執行期零成本,而且由邊緣直接送出。
一個會踩到的坑
預先產生的路由不能讀 url.searchParams(一個靜態檔只有一個 URL,不可能依查詢參數變化)。如果你的 hooks.server.ts 會讀它,build 會失敗:
Error: Cannot access url.searchParams on a page with prerendering enabled
解法是在 hook 裡跳過:
import { building } from '$app/environment';
if (!building) {
const style = event.url.searchParams.get('style');
// …
}
5. 快取
| 內容 | Cache-Control |
|---|---|
| 帶 hash 的資產 | public, max-age=31536000, immutable(adapter 自動設定) |
| 產生的 CSS | public, max-age=3600 |
| HTML | no-cache |
| API 回應 | 視情況,個人化資料用 private, no-store |
| 檔案下載 | no-store |
6. 常見問題
EBUSY: resource busy or locked, rmdir .svelte-kit/cloudflare(Windows)
上一次 build 的 handle 還沒釋放。rm -rf .svelte-kit/cloudflare 再重跑。
worker 超過 3MB
免費方案有大小上限。檢查有沒有把大型依賴打進 server bundle。用 import.meta.glob 內嵌大量檔案時特別容易超標。
nodejs_compat 沒開導致某個套件爆炸
錯誤訊息通常是 Cannot find module 'node:buffer'。開起來就好。
本機正常、線上 500
先看 npx wrangler tail 的即時 log。多半是環境變數沒設,或是用了 Workers 沒有的 API(fs、process、Buffer)。
7. 部署指令
npm run build
npx wrangler deploy
# 即時看 log
npx wrangler tail
# 本機模擬 Workers 環境(比 vite dev 更接近正式)
npx wrangler dev
wrangler dev 值得在上線前跑一次 —— 它用真正的 workerd 執行期,會抓到 vite dev 抓不到的 API 相容性問題。
8. 上線前檢查
-
npm run build沒有錯誤 -
npx wrangler dev下所有頁面都正常 - build 產物裡搜不到任何金鑰
- 所有 secret 都用
wrangler secret put設過 -
.dev.vars在.gitignore裡 - 安全標頭有生效(DevTools → Network → 看回應標頭)
- 404 頁面正常
-
robots.txt存在且擋掉/api/與/embed/ - 自訂網域的憑證已簽發
-
[observability]有開,出事才有 log 可看