ch07-static-assets
對應 07. Static Assets 與全端 Worker
在 GitHub 上檢視·15 個檔案·2.5 KB
取得並執行
這個範例可以獨立 clone 執行,不依賴其他章節。
npx degit mrsuner/cloudflare-serverless-platform-manuals/examples/ch07-static-assets
cd ch07-static-assets
npm install可用指令
npm run dev # wrangler dev
npm run deploy # wrangler deploy
npm run typecheck # tsc --noEmit
npm run cf-typegen # wrangler types --env-interface CloudflareBindings說明
ch07 — Static assets routing
Section titled “ch07 — Static assets routing”Companion example for docs/07-static-assets.md.
npm install && npm run devB=localhost:8787p() { printf "%-18s " "$1"; curl -s -o /tmp/b -w "%{http_code} %{redirect_url}" "$B$1"; printf " | "; head -c 55 /tmp/b; echo; }
p / # 200 index.htmlp /about # 200 about.htmlp /about.html # 307 -> /aboutp /nope # 200 worker fallbackp /api/hello # 200 workerp /api/docs # 200 worker <-- NOT excluded by !/api/docs/*p /api/docs/ # 200 static filep /app.js # 200 asset beats workerp /old-about # 301 -> /about (_redirects)p /go/index.html # 302 -> /app/index.htmlp /asset-binding # 200 env.ASSETS.fetch()Three extra configs demonstrate not_found_handling:
npx wrangler dev -c spa-a.jsonc # unset -> /deep/route 404, empty bodynpx wrangler dev -c spa-b.jsonc # SPA -> /deep/route 200, index.htmlnpx wrangler dev -c spa-c.jsonc # 404-pageVerified findings
Section titled “Verified findings”2026-07-28 · wrangler@4.114.0 · workerd@1.20260722.1
not_found_handling defaults to "none"
Section titled “not_found_handling defaults to "none"”With no Worker and public/index.html present:
| Setting | GET /deep/route |
|---|---|
unset ("none") | 404, empty body |
"single-page-application" | 200, index.html |
"404-page" | 404, 404.html (documented; not measured here) |
Any client-side-routed app breaks on deep links and refresh without this set.
Negation patterns have a trailing-slash edge
Section titled “Negation patterns have a trailing-slash edge”"run_worker_first": ["/api/*", "!/api/docs/*"]
/api/hello -> {"from":"worker",...} expected/api/docs -> {"from":"worker",...} NOT excluded/api/docs/ -> public/api/docs/index.html expectedThe glob /api/docs/* requires the slash, so /api/docs only matches
/api/*. Write both "!/api/docs" and "!/api/docs/*", and curl every rule.
Assets beat the Worker
Section titled “Assets beat the Worker”public/app.js exists and the Worker handles every path:
GET /app.js -> console.log("asset js");The Worker never ran, and was not billed.
_headers and _redirects work in local dev
Section titled “_headers and _redirects work in local dev”✨ Parsed 1 valid header rule.✨ Parsed 2 valid redirect rules.GET /app/ -> Cache-Control: public, max-age=3600 / x-frame-options: DENYGET /old-about -> 301 /aboutGET /go/index.html -> 302 /app/index.htmlNeither applies to responses generated by Worker code.
原始碼
wrangler.jsoncpackage.jsonsrc/index.tsspa-a.jsoncspa-b.jsoncspa-c.jsoncpublic/_headerspublic/_redirectspublic/404.htmlpublic/about.htmlpublic/api/docs/index.htmlpublic/app.jspublic/app/index.htmlpublic/index.htmltsconfig.json
wrangler.jsonc
{
"$schema": "./node_modules/wrangler/config-schema.json",
"name": "ch07-static-assets",
"main": "src/index.ts",
"compatibility_date": "2026-07-24",
"assets": {
"directory": "./public",
"binding": "ASSETS",
"run_worker_first": ["/api/*", "!/api/docs/*"]
}
}package.json
{ "name": "ch07-static-assets", "private": true, "type": "module",
"scripts": { "dev": "wrangler dev", "deploy": "wrangler deploy", "typecheck": "tsc --noEmit",
"cf-typegen": "wrangler types --env-interface CloudflareBindings" },
"devDependencies": { "typescript": "^5.9.0", "wrangler": "^4.114.0" } }src/index.ts
export default {
async fetch(request: Request, env: CloudflareBindings): Promise<Response> {
const url = new URL(request.url);
// Only reached for paths where run_worker_first matched, or where no
// asset matched and not_found_handling is "none".
if (url.pathname.startsWith("/api/")) {
return Response.json({ from: "worker", path: url.pathname });
}
if (url.pathname === "/asset-binding") {
// The hostname is ignored; only the pathname is used to match assets.
const res = await env.ASSETS.fetch(new URL("/about.html", url));
return new Response(await res.text(), {
headers: { "content-type": "text/plain; charset=utf-8" },
});
}
return new Response(`worker fallback: ${url.pathname}\n`, { status: 200 });
},
} satisfies ExportedHandler<CloudflareBindings>;spa-a.jsonc
{ "name": "spa-a", "compatibility_date": "2026-07-24", "assets": { "directory": "./public" } }spa-b.jsonc
{ "name": "spa-b", "compatibility_date": "2026-07-24", "assets": { "directory": "./public", "not_found_handling": "single-page-application" } }spa-c.jsonc
{ "name": "spa-c", "compatibility_date": "2026-07-24", "assets": { "directory": "./public", "not_found_handling": "404-page" } }public/_headers
/app/*
X-Frame-Options: DENY
Cache-Control: public, max-age=3600public/_redirects
/old-about /about 301
/go/* /app/:splat 302public/404.html
<!doctype html><title>404</title><h1>404.html</h1>public/about.html
<!doctype html><title>about</title><h1>about.html</h1>public/api/docs/index.html
<!doctype html><title>docs</title><h1>public/api/docs/index.html (static)</h1>public/app.js
console.log("asset js");public/app/index.html
<!doctype html><title>app</title><h1>public/app/index.html</h1>public/index.html
<!doctype html><title>SPA shell</title><h1>index.html (SPA shell)</h1>tsconfig.json
{ "compilerOptions": { "target": "esnext", "lib": ["esnext"], "module": "esnext",
"moduleResolution": "bundler", "types": ["./worker-configuration.d.ts"],
"strict": true, "skipLibCheck": true, "noEmit": true, "isolatedModules": true },
"include": ["src/**/*.ts", "worker-configuration.d.ts"] }