3C科技 娛樂遊戲 美食旅遊 時尚美妝 親子育兒 生活休閒 金融理財 健康運動 寰宇綜合

Zi 字媒體

2017-07-25T20:27:27+00:00
加入好友
SiteGround 3 折主機優惠 + WordPress 一鍵安裝完整教學 Hostinger 1.2 折主機優惠 + WordPress 一鍵安裝完整教學 3 分鐘閱讀 我之前介紹過,大部分網站都會使用 Cloudflare 的服務來加速網站,而使用 CloudFlare 擁有許多好處,除了免費的 DNS 服務之外,最重要的還有 CloudFlare 提供免費的 CDN 服務,能夠幫助我們的網站獲得更佳的載入速度,並降低主機的負載。 CloudFlare 的 CDN 服務原理是將網站的檔案內容快取在 Cloudflare 的全球節點當中來實現網站加速,而近期 Cloudflare 提供了另外一項新的功能 —「 Cloudflare Workers」。 今天就要來教大家如何在 CloudFlare 當中實作「Workers」的功能,來加快 WordPress 網站的速度。 在進行教學之前,首先我來要先簡單介紹一下 Cloudflare Workers 的功能以及運作原理。 目錄 hide 1 Cloudflare Workers™ 原理 2 使用 Cloudflare Workers 之前 3 Cloudflare Workers 教學 4 安裝 Cloudflare Page Cache 外掛 5 啟用 Cloudflare Workers 功能 6 新增 Script 7 Workers 優化成果展示 7.1 延伸閱讀 7.2 猜你喜歡 Cloudflare Workers™ 原理 Cloudflare Workers™ 究竟 Cloudflare Workers 是什麼?為什麼它可以讓我們的 WordPress 能夠比以往更快的自動更新快取、同時也能強化降低主機的負載? 依據 Cloudflare 官方的說法,Cloudflare Workers 的功能為「在邊緣執行程式碼,提供强大的網路延展能力」。 透過 Cloudflare 的 Workers 功能,能夠讓我們在 Cloudflare 端自定義規則與邏輯,來偵測魁儡程式、爬蟲機器人…等等程式,用以防止它們過度消耗主機資源,進而拖垮整個網站速度,以及維護提升整體網站主機的安全性。 Cloudflare Workers 的工作原理是,透過在每個網頁標頭當中加入 x-HTML-Edge-Cache,接著使用 JavaScript 辨識每個頁面內容,以及你進行你所自定義的過濾邏輯,針對每個頁面進行個別的執行內容。 使用 Cloudflare Workers 之前 在 Cloudflare Workers 推出之前,開發人員主要可在兩個地方部署程式碼: 在使用者裝置上執行的前端程式碼 在中央資料中心執行的後端程式碼 而上述這兩種方式都有著一些致命的缺點。因此,Cloudflare Workers 為開發人員提供了接近顧客的第三個地方來部署他們的程式碼:Cloudflare 不斷擴大的全球網路的邊緣。 如此就納入了雲端資料中心的能力與彈性,以及大量傳輸系統的備援能力,而且僅在毫秒之間就能傳給幾乎每一位網際網路使用者。 開發人員現在可以打造日趨複雜且充滿動態的應用程式,以滿足消費者需求:更具個人化與彈性的豐富環境。現在客戶可以將現有投資最大化,聚焦於: 減少對來源基礎結構的依賴 增進快取命中率 簡化應用程式與數量日增的 API 通訊的方式 為任何裝置或網路上的使用者提供更良好的使用者體驗 減少惡意傀儡程式對其基礎結構的衝擊 那麼,應用 Cloudflare Workers 在 WordPress 有什麼好處? 根據上述我們對 Workers 工作原理的理解,我們可以讓以往需要手動清除 Cloudflare 過期快取的操作,改由透過 Workers 自動即時更新最新的內容、更新快取,加快這一過程與減少人為的介入。 此外,我們也能透過 Cloudflare Workers 在 Cloudflare 的邊緣當中加入 JavaScript 程式碼,讓我們得以實現各種爬蟲、惡意程式的阻擋。更進一步,我們還可以自定義使用者的 Cookies 邏輯,讓以往我們僅能夠過快取外掛來判斷使用者是否是登入狀態來進行顯示快取與否,改由 Workers 來自動判別現在的使用者登入狀態。 如此除了能夠確實的辨別已知與未知使用者所需進行的相對應的處理之外,也能夠有效的使我們的 WordPress 網站更加的安全。 Cloudflare Workers 教學 在應用 Cloudflare Workers 功能之前,請先確保你的網站目前使使用 Cloudflare 的服務,若你尚未使用 Cloudflare 進行 DNS 的代管,可以先參考我們先前的 Cloudflare 介紹。 步驟一 安裝 Cloudflare Page Cache 外掛 首先,在我們的 WordPress 網站當中,需要先安裝一個外掛「Cloudflare Page Cache」: Cloudflare Page Cache Author(s): Patrick Meenan Current Version: 1.4 Last Updated: 2019-05-22 cloudflare-page-cache.zip 100%Ratings 700+Installs 3.3.1Requires 步驟二 啟用 Cloudflare Workers 功能 接著我們前往 Cloudflare 控制台 > Workers 頁面,點選「Launch Editor」打開編輯界面: 在 Cloudflare 後台當中選取 Workers 頁面啟用 步驟三 新增 Script 接著我們點選「Add Sciprt」來新增腳本: 點擊「Add Script」新增腳本 接著給予這個 Script 一個名稱,這個名稱隨便取都可以,我們在這裡取「techmoon-edge-cache」: 設定 Workers 當中 Script 的名稱 新增完一個 Script 之後,就能在左側列表當中看當我們剛剛新增名為「techmoon-edge-cache」的 Script,點選「Edit」就能進行編輯: 點選「Edit」編輯剛剛新增的 Script 進入後,Script 裡面會有預設的程式碼,我們先清空裡面的內容: 先將 Script 當中預設的程式碼刪除 接著我們借鑑在 Github 上,Workers 的 Script 程式碼,將所有內容貼至我們所建立的 Script 當中: JS // IMPORTANT: Either A Key/Value Namespace must be bound to this worker script // using the variable name EDGE_CACHE. or the API parameters below should be // configured. KV is recommended if possible since it can purge just the HTML // instead of the full cache. // API settings if KV isn't being used const CLOUDFLARE_API = { email: "", // From https://dash.cloudflare.com/profile key: "", // Global API Key from https://dash.cloudflare.com/profile zone: "" // "Zone ID" from the API section of the dashboard overview page https://dash.cloudflare.com/ }; // Default cookie prefixes for bypass const DEFAULT_BYPASS_COOKIES = [ "wp-", "wordpress", "comment_", "woocommerce_" ]; /** * Main worker entry point. */ addEventListener("fetch", event => { const request = event.request; let upstreamCache = request.headers.get('x-HTML-Edge-Cache'); // Only process requests if KV store is set up and there is no // HTML edge cache in front of this worker (only the outermost cache // should handle HTML caching in case there are varying levels of support). let configured = false; if (typeof EDGE_CACHE !== 'undefined') { configured = true; } else if (CLOUDFLARE_API.email.length && CLOUDFLARE_API.key.length && CLOUDFLARE_API.zone.length) { configured = true; } // Bypass processing of image requests (for everything except Firefox which doesn't use image/*) const accept = request.headers.get('Accept'); let isImage = false; if (accept && (accept.indexOf('image/*') !== -1)) { isImage = true; } if (configured && !isImage && upstreamCache === null) { event.passThroughOnException(); event.respondWith(processRequest(request, event)); } }); /** * Process every request coming through to add the edge-cache header, * watch for purge responses and possibly cache HTML GET requests. * * @param {Request} originalRequest – Original request * @param {Event} event – Original event (for additional async waiting) */ async function processRequest(originalRequest, event) { let cfCacheStatus = null; const accept = originalRequest.headers.get('Accept'); const isHTML = (accept && accept.indexOf('text/html') >= 0); let {response, cacheVer, status, bypassCache} = await getCachedResponse(originalRequest); if (response === null) { // Clone the request, add the edge-cache header and send it through. let request = new Request(originalRequest); request.headers.set('x-HTML-Edge-Cache', 'supports=cache|purgeall|bypass-cookies'); response = await fetch(request); if (response) { const options = getResponseOptions(response); if (options && options.purge) { await purgeCache(cacheVer, event); status += ', Purged'; } bypassCache = bypassCache || shouldBypassEdgeCache(request, response); if ((!options || options.cache) && isHTML && originalRequest.method === 'GET' && response.status === 200 && !bypassCache) { status += await cacheResponse(cacheVer, originalRequest, response, event); } } } else { // If the origin didn't send the control header we will send the cached response but update // the cached copy asynchronously (stale-while-revalidate). This commonly happens with // a server-side disk cache that serves the HTML directly from disk. cfCacheStatus = 'HIT'; if (originalRequest.method === 'GET' && response.status === 200 && isHTML) { bypassCache = bypassCache || shouldBypassEdgeCache(originalRequest, response); if (!bypassCache) { const options = getResponseOptions(response); if (!options) { status += ', Refreshed'; event.waitUntil(updateCache(originalRequest, cacheVer, event)); } } } } if (response && status !== null && originalRequest.method === 'GET' && response.status === 200 && isHTML) { response = new Response(response.body, response); response.headers.set('x-HTML-Edge-Cache-Status', status); if (cacheVer !== null) { response.headers.set('x-HTML-Edge-Cache-Version', cacheVer.toString()); } if (cfCacheStatus) { response.headers.set('CF-Cache-Status', cfCacheStatus); } } return response; } /** * Determine if the cache should be bypassed for the given request/response pair. * Specifically, if the request includes a cookie that the response flags for bypass. * Can be used on cache lookups to determine if the request needs to go to the origin and * origin responses to determine if they should be written to cache. * @param {Request} request – Request * @param {Response} response – Response * @returns {bool} true if the cache should be bypassed */ function shouldBypassEdgeCache(request, response) { let bypassCache = false; if (request && response) { const options = getResponseOptions(response); const cookieHeader = request.headers.get('cookie'); let bypassCookies = DEFAULT_BYPASS_COOKIES; if (options) { bypassCookies = options.bypassCookies; } if (cookieHeader && cookieHeader.length && bypassCookies.length) { const cookies = cookieHeader.split(';'); for (let cookie of cookies) { // See if the cookie starts with any of the logged-in user prefixes for (let prefix of bypassCookies) { if (cookie.trim().startsWith(prefix)) { bypassCache = true; break; } } if (bypassCache) { break; } } } } return bypassCache; } const CACHE_HEADERS = ['Cache-Control', 'Expires', 'Pragma']; /** * Check for cached HTML GET requests. * * @param {Request} request – Original request */ async function getCachedResponse(request) { let response = null; let cacheVer = null; let bypassCache = false; let status = 'Miss'; // Only check for HTML GET requests (saves on reading from KV unnecessarily) // and not when there are cache-control headers on the request (refresh) const accept = request.headers.get('Accept'); const cacheControl = request.headers.get('Cache-Control'); let noCache = false; if (cacheControl && cacheControl.indexOf('no-cache') !== -1) { noCache = true; status = 'Bypass for Reload'; } if (!noCache && request.method === 'GET' && accept && accept.indexOf('text/html') >= 0) { // Build the versioned URL for checking the cache cacheVer = await GetCurrentCacheVersion(cacheVer); const cacheKeyRequest = GenerateCacheRequest(request, cacheVer); // See if there is a request match in the cache try { let cache = caches.default; let cachedResponse = await cache.match(cacheKeyRequest); if (cachedResponse) { // Copy Response object so that we can edit headers. cachedResponse = new Response(cachedResponse.body, cachedResponse); // Check to see if the response needs to be bypassed because of a cookie bypassCache = shouldBypassEdgeCache(request, cachedResponse); // Copy the original cache headers back and clean up any control headers if (bypassCache) { status = 'Bypass Cookie'; } else { status = 'Hit'; cachedResponse.headers.delete('Cache-Control'); cachedResponse.headers.delete('x-HTML-Edge-Cache-Status'); for (header of CACHE_HEADERS) { let value = cachedResponse.headers.get('x-HTML-Edge-Cache-Header-' + header); if (value) { cachedResponse.headers.delete('x-HTML-Edge-Cache-Header-' + header); cachedResponse.headers.set(header, value); } } response = cachedResponse; } } else { status = 'Miss'; } } catch (err) { // Send the exception back in the response header for debugging status = "Cache Read Exception: " + err.message; } } return {response, cacheVer, status, bypassCache}; } /** * Asynchronously purge the HTML cache. * @param {Int} cacheVer – Current cache version (if retrieved) * @param {Event} event – Original event */ async function purgeCache(cacheVer, event) { if (typeof EDGE_CACHE !== 'undefined') { // Purge the KV cache by bumping the version number cacheVer = await GetCurrentCacheVersion(cacheVer); cacheVer++; event.waitUntil(EDGE_CACHE.put('html_cache_version', cacheVer.toString())); } else { // Purge everything using the API const url = "https://api.cloudflare.com/client/v4/zones/" + CLOUDFLARE_API.zone + "/purge_cache"; event.waitUntil(fetch(url,{ method: 'POST', headers: {'X-Auth-Email': CLOUDFLARE_API.email, 'X-Auth-Key': CLOUDFLARE_API.key, 'Content-Type': 'application/json'}, body: JSON.stringify({purge_everything: true}) })); } } /** * Update the cached copy of the given page * @param {Request} originalRequest – Original Request * @param {String} cacheVer – Cache Version * @param {EVent} event – Original event */ async function updateCache(originalRequest, cacheVer, event) { // Clone the request, add the edge-cache header and send it through. let request = new Request(originalRequest); request.headers.set('x-HTML-Edge-Cache', 'supports=cache|purgeall|bypass-cookies'); response = await fetch(request); if (response) { status = ': Fetched'; const options = getResponseOptions(response); if (options && options.purge) { await purgeCache(cacheVer, event); } let bypassCache = shouldBypassEdgeCache(request, response); if ((!options || options.cache) && !bypassCache) { await cacheResponse(cacheVer, originalRequest, response, event); } } } /** * Cache the returned content (but only if it was a successful GET request) * * @param {Int} cacheVer – Current cache version (if already retrieved) * @param {Request} request – Original Request * @param {Response} originalResponse – Response to (maybe) cache * @param {Event} event – Original event * @returns {bool} true if the response was cached */ async function cacheResponse(cacheVer, request, originalResponse, event) { let status = ""; const accept = request.headers.get('Accept'); if (request.method === 'GET' && originalResponse.status === 200 && accept && accept.indexOf('text/html') >= 0) { cacheVer = await GetCurrentCacheVersion(cacheVer); const cacheKeyRequest = GenerateCacheRequest(request, cacheVer); try { // Move the cache headers out of the way so the response can actually be cached. // First clone the response so there is a parallel body stream and then // create a new response object based on the clone that we can edit. let cache = caches.default; let clonedResponse = originalResponse.clone(); let response = new Response(clonedResponse.body, clonedResponse); for (header of CACHE_HEADERS) { let value = response.headers.get(header); if (value) { response.headers.delete(header); response.headers.set('x-HTML-Edge-Cache-Header-' + header, value); } } response.headers.delete('Set-Cookie'); response.headers.set('Cache-Control', 'public; max-age=315360000'); event.waitUntil(cache.put(cacheKeyRequest, response)); status = ", Cached"; } catch (err) { // status = ", Cache Write Exception: " + err.message; } } return status; } /****************************************************************************** * Utility Functions *****************************************************************************/ /** * Parse the commands from the x-HTML-Edge-Cache response header. * @param {Response} response – HTTP response from the origin. * @returns {*} Parsed commands */ function getResponseOptions(response) { let options = null; let header = response.headers.get('x-HTML-Edge-Cache'); if (header) { options = { purge: false, cache: false, bypassCookies: [] }; let commands = header.split(','); for (let command of commands) { if (command.trim() === 'purgeall') { options.purge = true; } else if (command.trim() === 'cache') { options.cache = true; } else if (command.trim().startsWith('bypass-cookies')) { let separator = command.indexOf('='); if (separator >= 0) { let cookies = command.substr(separator + 1).split('|'); for (let cookie of cookies) { cookie = cookie.trim(); if (cookie.length) { options.bypassCookies.push(cookie); } } } } } } return options; } /** * Retrieve the current cache version from KV * @param {Int} cacheVer – Current cache version value if set. * @returns {Int} The current cache version. */ async function GetCurrentCacheVersion(cacheVer) { if (cacheVer === null) { if (typeof EDGE_CACHE !== 'undefined') { cacheVer = await EDGE_CACHE.get('html_cache_version'); if (cacheVer === null) { // Uninitialized – first time through, initialize KV with a value // Blocking but should only happen immediately after worker activation. cacheVer = 0; await EDGE_CACHE.put('html_cache_version', cacheVer.toString()); } else { cacheVer = parseInt(cacheVer); } } else { cacheVer = -1; } } return cacheVer; } /** * Generate the versioned Request object to use for cache operations. * @param {Request} request – Base request * @param {Int} cacheVer – Current Cache version (must be set) * @returns {Request} Versioned request object */ function GenerateCacheRequest(request, cacheVer) { let cacheUrl = request.url; if (cacheUrl.indexOf('?') >= 0) { cacheUrl += '&'; } else { cacheUrl += '?'; } cacheUrl += 'cf_edge_cache_ver=' + cacheVer; return new Request(cacheUrl); } 貼過來之後,記得要在其中的三個變數當中,填入你的 Cloudflare Credentials。這三個變數分別為: email:這裡填入你的 Cloudflare 帳號(註冊的 Email 信箱) key:這裡要填入 Cloudflare 的 Global API Key。你可以在 Cloudflare 帳號的「My Profile」頁面底下找到「Global API Key」,一個帳號僅有一個 Global API Key。 zone:這裡填入你所要啟用的網站的 Zone ID,每一個由 Cloudflare 託管的網站都會有一個獨立的 Zone ID。你可以在「Overview」頁面當中,的右下方找到該網址的 Zone ID。 貼上程式碼之後,需要分別輸入你的 Email、Global API Key 與 Zone ID 當我們按下「Save」儲存後,會跳出提示「This Worker won’t be deployed yet until you assign it to a route.」,這是因為我們還需要指派 Route 才能真正的啟用這個 Script 的功能。 儲存 Script 後,需要指定 Route 才能真正的啟用剛剛設定的腳本規則 因此,我們在剛剛「Scripts」右邊點選「Routes」選項,接著點擊「Add Route」新增一個 Route: 到「Routes」點擊「Add Route」新增一個 Route 我們在 Route 當中右邊 Script 的部分,選擇剛剛的「techmoon-edge-cache」,左側則是填入你要應用的網址。 由於我們要應用在整個網站,因此左側這裡我們填入「techmoon.xyz/*」表示我們應用在整個網域當中。 左側填入要應用的網域,右側則是指定剛剛先增的 Script 儲存之後就大功告成了! Workers 優化成果展示 最後,我們要來檢測一下剛剛所設定的 Workers 是否有確實的啟用快取。因此,我們可以透過 Chrome 瀏覽器,在頁面上點選滑鼠右鍵「檢查」,開啟開發人員工具,或是直接按下 F12 也能啟用。接著選擇「Network」分頁之後,在重整一次網頁,就可以獲取網站的載入資訊: Cloudflare 快取狀態 html edge 快取狀態 從上面兩張圖可以看出,我們的 Cloudflare 快取以及 html edge 快取狀態都顯示為「HIT」,即表示快取功能已經正式的部屬成功。 如果你喜歡今天的 Cloudflare Workers 教學,歡迎幫我們分享出去,若有任何想和我們說的,也歡迎在下方留下評論。 猜你喜歡 WordPress 是什麼?新手入門教學 - 深入淺出帶你了解什麼是 WordPress 網站 2019 SugarHosts 6 月 - 糖果主機評價:購買 8 個主機,使用超過 3 年的經驗與教學 2 步驟教你如何在 Blogger/Blogspot 當中設定自定義網域/子網域 2019 WPX Hosting 6 月 - 主機評測 - VPS 規格 Shared Hosting 價格,是你 WordPress 網站託管的最佳選擇 3 分鐘了解 WordPress.com 與 WordPress.org 之間的分別與差異 CloudFlare SSL Test Tool 線上憑證檢測工具,快速檢測 SSL 憑證! Cloudflare - 免費 DNS 代管網域名稱提升網站安全性,提供免費 CDN 與免費網站優化加快網站的載入速度 CloudAccess Free WordPress Hosting - 免費 WordPress 虛擬主機申請與免費一鍵安裝 WordPress & Joomla 戰國策虛擬主機 - 專屬優惠 8 折 20% OFF 優惠券,國內 WordPress 架站的好選擇 14 個最佳 WordPress Cache Plugins 緩存快取外掛推薦 - 2019 免費/付費通通有

本文由techmoonxyz提供 原文連結

寫了 5860316篇文章,獲得 23313次喜歡
精彩推薦