DEV Community

Cover image for 用 Cloudflare Worker 來佈署靜態網頁
Jian Lin Huang
Jian Lin Huang

Posted on

用 Cloudflare Worker 來佈署靜態網頁

得益於 Cloudflare 在全球超過 200 多個節點,我們可以很輕易的將我們的應用程式快速的推向 Cloudflare 的全球網路邊緣,這可以讓我們的應用程式的 Response Time 變得更低,如一個以 SPA 技術開發的 Web Application 就可以很輕易的佈署至 Cloudflare Worker。


基礎環境建立

首先我們需要安裝用於佈署 Worker 的工具:@cloudflare/wrangler,我們可以透過 npm 來安裝這個工具:

~$ sudo npm install -g @cloudflare/wrangler 
Enter fullscreen mode Exit fullscreen mode

安裝完畢之後我們進入 Cloudflare 的網站,在右欄的部份找到這個區塊,並按下 "取得您的 API Token"。

Alt Text

進入頁面之後點選 API Token 的 Tab 並按下"建立 Token"

Alt Text

並且在建立的頁面中點選 "編輯 Cloudflare Worker",並進入頁面

Alt Text

在權限的區塊,這裡選定的區塊是 "編輯 cloudflare worker 必要的權限",因此若沒有其他的需求,就不用額外調整權限區塊。在帳戶資源部份請至少選擇 "包含" 自己的帳戶。區域資源的部份可以選擇特定區域,以免不小心異動其他網域的內容,如果沒有這類的疑慮,也可以直接選擇包含所有區域。

確定之後就會跳出摘要頁面,確認資料無誤之後就可以按下"建立 Token":

Alt Text

取得上述的 Token 之後我們就可以回到 Terminal,輸入指令後按照要求將 API Token 貼上

~$ wrangler config To find your API Token, go to https://dash.cloudflare.com/profile/api-tokens and create it using the "Edit Cloudflare Workers" template. 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 
Enter fullscreen mode Exit fullscreen mode

看到 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 
Enter fullscreen mode Exit fullscreen mode

接著我們就會拿到這些檔案:

. ├── 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 
Enter fullscreen mode Exit fullscreen mode
  • 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" 
Enter fullscreen mode Exit fullscreen mode

其中的 account_id 以及 zone_id 我們就會回到剛才取得 API Token 那個區塊:

Alt Text

其中的區域識別碼貼至 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 
Enter fullscreen mode Exit fullscreen mode

接著我們回到 Cloudflare Worker 的頁面:

Workers

就會看到一個 mysite.fres.host/* => mysite,但是還沒結束。我們要進入 DNS 的頁面,新增一個 DNS Record:

Alt Text

DNS Record 的值可以為隨意,因為全部都會被 Cloudflare 攔截起來,所以網域不管指向哪裡都可以,對應完畢之後我們就可以打開瀏覽器並訪問網址,在這裡我的網址是 https://mysite.fres.host

Alt Text

就會看到 Cloudflare 的預設頁面,做到這裡最基本的 Cloudflare 佈署就完成了。

番外篇:用 Cloudflare Worker 佈署 History mode 的 Vue SPA Application

首先我們先開啟一個 Vue SPA 的應用程式,並選擇開啟 History Mode,然後我們將剛才產生的 wrangler.toml 以及 worker-site 移到這個 mysite_spa 底下。

~$ vue create mysite_spa 
Enter fullscreen mode Exit fullscreen mode

接著在設定之前首先先看一下 Vue.js 官方文件提到當需要佈署 History mode 的應用程式時,所需要做的設定,以 Nginx 做範例:

location / { try_files $uri $uri/ /index.html; } 
Enter fullscreen mode Exit fullscreen mode

概念就是先 try-file 看看有沒有路徑對應的文件,如果沒有則遞送 index.html。

更改 wrangler.toml

因為在 Vue Application 中 public 資料夾另有他用,因此我們不能照常將 wrangler.toml 的 bucket 設為 ./public 資料夾,而是要將其改成 ./dist

# 將 site 區塊的設定改成這樣 [site] bucket = "./dist" 
Enter fullscreen mode Exit fullscreen mode

更改 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) } } 
Enter fullscreen mode Exit fullscreen mode

程式有點長,不過我們並沒有要修改太多,找到以下這個區塊,這個區塊定義了當應用程式找不到指定檔案的時候就返回 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) {} } 
Enter fullscreen mode Exit fullscreen mode

因此我們將這段程式修改成這樣:

// 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) {} } 
Enter fullscreen mode Exit fullscreen mode

這樣就可以在找不到檔案時遞送 index.html 給客戶端了,完成之後我們做一次打包,然後我們透過 wrangler dev 指令打開測試伺服器並訪問 http://localhost:8787 ,來看看設定是否正確。

~$ npm run build ~$ wrangler dev 
Enter fullscreen mode Exit fullscreen mode

在 Vue Application 的 Demo 頁面中就可以直接嘗試是否 History Vue Router 可以正常運作:

http://localhost:8787

Alt Text

http://localhost:8787/about

Alt Text

確定無誤之後我們就可以將其佈署至 Cloudflare Worker,最後我們訪問一下佈署的頁面:https://mysite.fres.host

~$ wrangler publish 
Enter fullscreen mode Exit fullscreen mode

就可以看到結果頁面:

Alt Text

Top comments (1)

Collapse
 
leon0824 profile image
Leon

第一次在 dev.to 看到台灣人的文,推一下!