用 Cloudflare Worker 來佈署靜態網頁
基礎環境建立
安裝 Wrangler CLI
首先我們需要安裝用於佈署 Worker 的工具:@cloudflare/wrangler
, 我們可以透過 npm 來安裝這個工具:
~$ sudo npm install -g @cloudflare/wrangler
生成並取得 API Token
安裝完畢之後我們進入 Cloudflare 的網站, 在右欄的部份找到這個區塊,並按下 “取得您的 API Token”。
進入頁面之後點選 API Token 的 Tab 並按下”建立 Token”
並且在建立的頁面中點選 “編輯 Cloudflare Worker”,並進入頁面
在權限的區塊,這裡選定的區塊是 “編輯 cloudflare worker 必要的權限”, 因此若沒有其他的需求,就不用額外調整權限區塊。 在帳戶資源部份請至少選擇 “包含” 自己的帳戶。 區域資源的部份可以選擇特定區域,以免不小心異動其他網域的內容, 如果沒有這類的疑慮,也可以直接選擇包含所有區域。
確定之後就會跳出摘要頁面,確認資料無誤之後就可以按下”建立 Token”:
取得上述的 Token 之後我們就可以回到 Terminal,輸入指令後按照要求將 API Token 貼上
~$ wrangler config
If you are trying to use your Global API Key instead of an API Token
(Not Recommended), run `wrangler config --api-key`.
Enter API Token:
B4jLkMW933P3P5tsDOqLbKZLi9UMLPEkH8xQdOI7
Validating credentials...
Successfully configured. You can find your configuration file at: /home/floatflower/.wrangler/config/default.toml
看到 Successfully configured 之後就代表環境已經設定好囉。
佈署網站
建立一個靜態網頁模板
~$ wrangler generate --site mysite
Creating project called `mysite`...
Done! New project created /home/floatflower/mysite
You will need to update the following fields in the created wrangler.toml file before continuing:
You can find your account_id in the right sidebar of your account's Workers page, and zone_id in the right sidebar of a zone's overview tab at https://dash.cloudflare.com
- account_id
~$ cd mysite
接著我們就會拿到這些檔案:
.
├── node_modules
├── package-lock.json
├── public
│ ├── 404.html
│ ├── favicon.ico
│ ├── img
│ │ ├── 200-wrangler-ferris.gif
│ │ └── 404-wrangler-ferris.gif
│ └── index.html
├── workers-site
│ ├── index.js
│ ├── package.json
│ └── package-lock.json
└── wrangler.toml
public
資料夾就是放置靜態網站的資料夾,你可以把你的靜態網頁放在裡面。worker-site
就是 Cloudflare Worker 的程式碼, 你可以在這裡定義自己的 Cloudflare Worker 行為, 但是在這裡我們也不用做其他改變,直接用預設的就可以了。
調整 wrangler.toml
我們打開 wrangler.toml
裡面含有這些內容:
# worker 名稱
name = "mysite"
type = "webpack"
account_id = ""
# 如果選擇 true 的話,Cloudflare 會提供給你一個 .workers.dev 網域的網址,因為我們要提供網域,所以我們將其設為 false。
workers_dev = false
# 如果 workers_dev 是 false 時,這個 route 一定要提供,我這邊示範的網址是 mysite.fres.host。
route = "mysite.fres.host/*"
zone_id = ""
[site]
bucket = "./public"
其中的 account_id
以及 zone_id
我們就會回到剛才取得 API Token 那個區塊:
其中的區域識別碼貼至 zone_id
以及帳戶識別碼貼至 account_id
。
佈署上線
~$ wrangler publish
...
Built successfully, built project size is 13 KiB.
Created namespace for Workers Site "__mysite-workers_sites_assets"
Success
Uploading site files
Deployed to the following routes:
mysite.fres.host/* => created
接著我們回到 Cloudflare Worker 的頁面:
就會看到一個 mysite.fres.host/* => mysite
,但是還沒結束。 我們要進入 DNS 的頁面,新增一個 DNS Record:
DNS Record 的值可以為隨意, 因為全部都會被 Cloudflare 攔截起來, 所以網域不管指向哪裡都可以,因此我直接讓它指向 1.1.1.1
對應完畢之後我們就可以打開瀏覽器並訪問網址, 在這裡我的網址是 https://mysite.fres.host 。
就會看到 Cloudflare 的預設頁面,做到這裡最基本的 Cloudflare 佈署就完成了。
番外篇:用 Cloudflare Worker 佈署 History mode 的 Vue SPA Application
首先我們先開啟一個 Vue SPA 的應用程式, 並選擇開啟 History Mode, 然後我們將剛才產生的 wrangler.toml
以及 worker-site
移到這個 mysite_spa
底下。
~$ vue create mysite_spa
接著在設定之前首先先看一下 Vue.js 官方文件提到當需要佈署 History mode 的應用程式時, 所需要做的設定,以 Nginx 做範例:
location / {
try_files $uri $uri/ /index.html;
}
概念就是先 try-file
看看有沒有路徑對應的文件,如果沒有則遞送 index.html
。
更改 wrangler.toml
因為在 Vue Application 中 public 資料夾另有他用, 因此我們不能照常將 wrangler.toml
的 bucket 設為 ./public
資料夾, 而是要將其改成 ./dist
。
# 將 site 區塊的設定改成這樣
[site]
bucket = "./dist"
更改 worker-site/index.js
接著我們要修改 worker-site/index.js
檔案的內容, 以符合上述提到的 Nginx 遞送 Vue Application 的邏輯。
我們先打開 worker-site/index.js
import { getAssetFromKV, mapRequestToAsset } from '@cloudflare/kv-asset-handler'
/**
* The DEBUG flag will do two things that help during development:
* 1. we will skip caching on the edge, which makes it easier to
* debug.
* 2. we will return an error message on exception in your Response rather
* than the default 404.html page.
*/
const DEBUG = false
addEventListener('fetch', event => {
try {
event.respondWith(handleEvent(event))
} catch (e) {
if (DEBUG) {
return event.respondWith(
new Response(e.message || e.toString(), {
status: 500,
}),
)
}
event.respondWith(new Response('Internal Error', { status: 500 }))
}
})
async function handleEvent(event) {
const url = new URL(event.request.url)
let options = {}
/**
* You can add custom logic to how we fetch your assets
* by configuring the function `mapRequestToAsset`
*/
// options.mapRequestToAsset = handlePrefix(/^\/docs/)
try {
if (DEBUG) {
// customize caching
options.cacheControl = {
bypassCache: true,
}
}
const page = await getAssetFromKV(event, options)
// allow headers to be altered
const response = new Response(page.body, page)
response.headers.set('X-XSS-Protection', '1; mode=block')
response.headers.set('X-Content-Type-Options', 'nosniff')
response.headers.set('X-Frame-Options', 'DENY')
response.headers.set('Referrer-Policy', 'unsafe-url')
response.headers.set('Feature-Policy', 'none')
return response
} catch (e) {
// if an error is thrown try to serve the asset at 404.html
if (!DEBUG) {
try {
let notFoundResponse = await getAssetFromKV(event, {
mapRequestToAsset: req => new Request(`${new URL(req.url).origin}/404.html`, req),
})
return new Response(notFoundResponse.body, { ...notFoundResponse, status: 404 })
} catch (e) {}
}
return new Response(e.message || e.toString(), { status: 500 })
}
}
/**
* Here's one example of how to modify a request to
* remove a specific prefix, in this case `/docs` from
* the url. This can be useful if you are deploying to a
* route on a zone, or if you only want your static content
* to exist at a specific path.
*/
function handlePrefix(prefix) {
return request => {
// compute the default (e.g. / -> index.html)
let defaultAssetKey = mapRequestToAsset(request)
let url = new URL(defaultAssetKey.url)
// strip the prefix from the path for lookup
url.pathname = url.pathname.replace(prefix, '/')
// inherit all other props from the default request
return new Request(url.toString(), defaultAssetKey)
}
}
程式有點長,不過我們並沒有要修改太多,找到以下這個區塊, 這個區塊定義了當應用程式找不到指定檔案的時候就返回 404.html
, 並將 Http Status Code 設為 404 Not Found
。
// if an error is thrown try to serve the asset at 404.html
if (!DEBUG) {
try {
let notFoundResponse = await getAssetFromKV(event, {
mapRequestToAsset: req => new Request(`${new URL(req.url).origin}/404.html`, req),
})
return new Response(notFoundResponse.body, { ...notFoundResponse, status: 404 })
} catch (e) {}
}
我們將這段程式修改成這樣:
// if an error is thrown try to serve the asset at 404.html
if (!DEBUG) {
try {
let notFoundResponse = await getAssetFromKV(event, {
mapRequestToAsset: req => new Request(`${new URL(req.url).origin}/index.html`, req),
})
return new Response(notFoundResponse.body, { ...notFoundResponse, status: 200 })
} catch (e) {}
}
這樣就可以在找不到檔案時遞送 index.html
給客戶端了, 完成之後我們做一次打包, 然後我們透過 wrangler dev
指令打開測試伺服器並訪問 http://localhost:8787, 來看看設定是否正確。
~$ npm run build
~$ wrangler dev
在 Vue Application 的 Demo 頁面中就可以直接嘗試是否 History Vue Router 可以正常運作:
http://localhost:8787
http://localhost:8787/about
確定無誤之後我們就可以將其佈署至 Cloudflare Worker, 最後我們訪問一下佈署的頁面:https://mysite.fres.host
~$ wrangler publish
就可以看到結果頁面: