跳到主要內容

部署到 Cloudflare Workers

adapter 設定、自訂網域、環境變數與 secret、預先產生的靜態檔,以及上線前的檢查。

約 6 分鐘 · deployment-cloudflare.md

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 帳號裡

流程:

  1. 確認 perhapxin.com 的 DNS 由 Cloudflare 管理
  2. npx wrangler deploy
  3. Cloudflare 自動建立 web-design 的 CNAME 並簽發憑證
  4. 第一次通常一到兩分鐘生效

如果網域不在 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(fsprocessBuffer)。

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 可看

顯示設定

這裡改的每一項,會即時套用到站上所有預覽。

風格

密度

圓角

動態

系統層級的「減少動態效果」永遠優先於這裡的設定。

語言