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

Discussion (1)

Collapse
leon0824 profile image
Leon

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