[WordPress]利用 Cloudflare Workers 来缓存博客的 HTML 网页(写于:20201130)

好久没写过 WordPress 相关的东西了,这次会想起来写是因为最近看到了 Cloudflare 出的一个新功能,叫 Automatic Platform Optimization for WordPress,这个功能是专门为 WordPress 推出的,用处就是和他家的 WordPress 插件配合着用来优化你的 WordPress 网站,最关键的是,这次可以用它来缓存 HTML 了!然而比较遗憾的是,这是个付费功能,要用的话要么每月付费 5 美元单独开通这个功能,要么开一个自带此功能 Pro 套餐,每月 20 刀,虽然我是挺心动这个功能的,不过苦于囊中羞涩,想了想只能是放弃了。

虽然是不能用了,不过官方的博客在介绍这个功能时,有提到缓存 HTML 用的是他家的 Workers 来实现的,而用 Workers 来缓存 HTML 其实官方以前就有出过教程,只不过那时候 Workers KV 这个功能还不是免费的,导致在清除缓存时只能借助全局 API 的方式来清除,弊端就是这个清除是不分缓存类型的,会把其它资源的缓存给一块清除了,会比较浪费,所以我当初试了几天最终还是放弃折腾了。好巧不巧的是,在官方推出 Automatic Platform Optimization for WordPress 不久,Cloudflare 后面又给免费用户开放了 Workers KV 的使用,虽然和 Workers 一样是有限制的,但是我想了想,用来给自己的博客用好像也没什么问题,毕竟我的博客负载不高,加上 Workers 超额了也可以设置直接返回源站,这样子在限额没到的时候用 Workers 缓存网页可以提高网站的访问速度,而限额到了也只是返回源站,和现在的体验没什么差别,所以虽然是不能用最好的官方支援功能了,自己折腾折腾 Workers 用个低配版的也不错嘛!

基于以上的原因,所以我决定抄一抄官方的例子,配合现在开放的 Workers KV 功能来弄一个低配版的 HTML 页面缓存功能。因为其实就是照抄官方的例子弄好的,所以我本来没打算写教程分享的,只是看了下网上比较热门的文章,很多针对 Workers 的都是在把它当代理用的,其它方面的分享似乎不是很多,所以想了想还是决定水一篇比较简单的教程,算是分享点经验给也有此想法的朋友吧~

注意:官方给出的例子里面有两种使用方法,一种是 KV的(推荐),一种是 API 的,由于我上面说的原因,再加上 KV 现在也可以免费用了,所以下面的教程只针对 Workers KV 来写,不包含 API 的设置方法。

一、添加 Workers KV 命名空间

在开始之前,我们需要先添加一个 Workers KV 的命名空间,后面 Workers 有关的数据就会存储在这里面。添加方法也比较简单,我直接截图吧!

根据我上面的两张图来添加命名空间就可以了,命名空间的名字大家可以自己随便设置,根据官方的说法,用已经添加好的也是没问题的,所以如果你已经有添加过命名空间的话,这一步是可以不用做的。

二、设置 Workers 脚本

找到下图的位置,然后添加 Workers 脚本:

点击上面的创建功能后,你会看到一个类似于下图的页面:

看到这个页面后,先把最左边框出来的示例代码给全部删除了,然后复制下面的代码添加进去,最后点击页面左下的“Save and Deploy”就可以了。

PS:忘记说了,脚本的名字在我框出来的这部分的上面,默认是随机的名字,你可以自己改一个好识别的名字,名字不影响功能的使用。

代码如下:

// 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 官方的 Github 缓存 HTML 例子,具体出处:https://github.com/cloudflare/worker-examples/tree/master/examples/edge-cache-html

官方使用的是 MIT 协议开源的,好像是要用代码得保留 MIT 的协议说明来着?因为我是直接写文章复制的代码不知道该怎么添加合适,所以就贴一下这个协议说明的网址吧:https://opensource.org/licenses/MIT

官方给的代码实际上是可以直接用的,不需要怎么修改,不过还是有几个需要注意的地方,我贴出来说明一下吧。

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/
};

上面代码是在完整代码最开头的部分,完整代码里已经有说明了,这个是只有 API 清除缓存的方法才需要自己设置的,因为我们用的是 KV 的方法,所以这一部分的代码可以不修改或者直接删除了,都是没有影响的。

const DEFAULT_BYPASS_COOKIES = [
  "wp-",
  "wordpress",
  "comment_",
  "woocommerce_"
];

这串代码就在上面 API 设置部分代码的后面,作用的话就是设置哪些 Cookies 可以绕过缓存直接到源站获取页面。这部分代码官方的例子里已经有添加几个常用的 Cookies 了,一般不需要我们自己再改,如果你有特别需求的话,可以按照代码的格式自己修改。

三、给 Workers 脚本绑定 Workers KV

上面添加好脚本并保存好后,回到添加脚本的页面就可以看到自己设置好的脚本了,之后就是点击脚本进行绑定 KV 的设置了,如下图:

按上面的图设置好以后,点击“Save”保存就搞定了。

这里需要注意的地方有两点,最后这张图里面的 Variable name 这一项,必须要填写 EDGE_CACHE,因为这是 Workers 脚本代码里会调用到的,不能乱改,其次是 KV namespace 这里,选择的是我们前面添加的 KV 命名空间,不一定要跟我截图的一样,你自己创建的随便哪个命名空间都是可以的。

四、添加 Route 并绑定 Workers 脚本

上面的都设置好之后,就可以动手开始使用了。先返回到 Cloueflare Workers 功能的主界面,根据下图点击添加 Route。

之后会弹出设置 Route 的页面,如下图:

给大家解释一下上图各个设置的含义:

  • Route:填写的是域名规则,也就是设置什么样的域名会匹配这条脚本代码,像我们用 Workers 来缓存 WordPress 页面的话,为了全部页面都能匹配到,填写“你自己的域名/*”这个样子就可以了,比如我上图的写法。如果你想自定义下,可以看官方文档的写法说明:https://developers.cloudflare.com/workers/platform/routes#matching-behavior
  • Worker:选择要用的脚本,这里我们选择前面创建的那个脚本就可以了
  • Failure mode for this route:点开“Request limit failure mode”这个选项的箭头会看到额外的选项,是让你选择当 Workers 免费额度用超之后怎么处理的。Fail closed (block)是停止代码,并返回错误页面,Fail open (proceed)是停止代码,但是不返回错误页面而是直接把请求交给源服务器。对于我们用来缓存页面来说,第二个选项是比较好的,不然等你额度超了之后,别人打开你的博客文章就都是看到的 Cloudflare 错误页面了。

五、安装 WordPress 插件

按照上面的步骤设置完毕之后,缓存功能就开始正常工作了,不过目前只能缓存成功,还没办法在更新修改文章之后清除缓存,所以我们需要再给 WordPress 安装一个插件来触发自动清除缓存的功能。

这个插件没有上架 WordPress 的插件中心,所以只能自己本地下载好之后手动安装了,怎么手动安装就不用我说了吧?

插件下载地址(项目文件里那个 zip 文件):https://github.com/cloudflare/worker-examples/tree/master/examples/edge-cache-html/WordPress%20Plugin

强迫症的个人备份,不想到上面下可以下我的备份(备份于:20201130):

要是上面两个链接都不想下载或者打不开的话,你也可以直接复制下面的插件代码,保存为 PHP 文件并打包为 zip 文件,之后一样可以上传到 WordPress 手动安装:

<?php
/*
Plugin Name:  Cloudflare Page Cache
Plugin URI:   https://github.com/cloudflare/worker-examples/tree/master/examples/edge-cache-html
Description:  Cache HTML pages on the Cloudflare CDN when used with the page cache Worker.
Version:      1.4
Author:       Patrick Meenan
Author URI:   https://www.cloudflare.com/
License:      GPLv2 or later
License URI:  http://www.gnu.org/licenses/gpl-2.0.html
Text Domain:  cloudflare-page-cache
Domain Path:  /languages
*/
defined( 'ABSPATH' ) or die( 'No script kiddies please!' );

// Callbacks that something changed
function cloudflare_page_cache_init_action() {
	static $done = false;
	if ( $done ) {
		return;
	}
	$done = true;
	
	// Add the edge-cache headers
  if (!is_user_logged_in() ) {
    header( 'x-HTML-Edge-Cache: cache,bypass-cookies=wp-|wordpress|comment_|woocommerce_' );
  } else {
    header( 'x-HTML-Edge-Cache: nocache' );
	}

	// Post ID is received
	add_action( 'wp_trash_post', 'cloudflare_page_cache_purge1', 0 );
	add_action( 'publish_post', 'cloudflare_page_cache_purge1', 0 );
	add_action( 'edit_post', 'cloudflare_page_cache_purge1', 0 );
	add_action( 'delete_post', 'cloudflare_page_cache_purge1', 0 );
	add_action( 'publish_phone', 'cloudflare_page_cache_purge1', 0 );
	// Coment ID is received
	add_action( 'trackback_post', 'cloudflare_page_cache_purge2', 99 );
	add_action( 'pingback_post', 'cloudflare_page_cache_purge2', 99 );
	add_action( 'comment_post', 'cloudflare_page_cache_purge2', 99 );
	add_action( 'edit_comment', 'cloudflare_page_cache_purge2', 99 );
	add_action( 'wp_set_comment_status', 'cloudflare_page_cache_purge2', 99, 2 );
	// No post_id is available
	add_action( 'switch_theme', 'cloudflare_page_cache_purge1', 99 );
	add_action( 'edit_user_profile_update', 'cloudflare_page_cache_purge1', 99 );
	add_action( 'wp_update_nav_menu', 'cloudflare_page_cache_purge0' );
	add_action( 'clean_post_cache', 'cloudflare_page_cache_purge1' );
	add_action( 'transition_post_status', 'cloudflare_page_cache_post_transition', 10, 3 );
}
add_action( 'init', 'cloudflare_page_cache_init_action' );

// Add the response header to purge the cache. send_headers isn't always called
// so set it immediately when something changes.
function cloudflare_page_cache_purge() {
  static $purged = false;
  if (!$purged) {
    $purged = true;
    header( 'x-HTML-Edge-Cache: purgeall' );
  }
}

function cloudflare_page_cache_purge0() {
  cloudflare_page_cache_purge();
}
function cloudflare_page_cache_purge1( $param1 ) {
  cloudflare_page_cache_purge();
}
function cloudflare_page_cache_purge2( $param1, $param2="" ) {
  cloudflare_page_cache_purge();
}
function cloudflare_page_cache_post_transition( $new_status, $old_status, $post ) {
  if ( $new_status != $old_status ) {
    cloudflare_page_cache_purge();
  }
}

代码我一个字都没改,作者、出处和开源协议之类的里面都写了,不放心的可以自己去比对~

插件安装好之后,也不需要设置什么,这个插件没有设置界面,安装好启用就可以了。

简单的给大家分析下这个插件是怎么配合 Workers 脚本实现缓存网页和自动清除网页缓存的吧,其实看上面不多的代码也能知道了,实际上插件干的事情不多,也就是添加了几组动作,在发布/删除/修改文章,有新评论/评论被编辑,或者是更改 WordPress 主题时触发有关代码,将插件默认添加的响应头 x-HTML-Edge-Cache 由缓存状态变更为需要清除缓存的 purgeall,然后 Workers 那边的脚本里由相关代码会检测 x-HTML-Edge-Cache 这个响应头,当检测到某个页面的响应头变更了需要清除缓存时,就会在 Cloudflare 里清除对应页面的缓存了。

六、检查功能是否有正常生效

按照上面的设置弄好之后,所以功能应该都是在正常工作了的,然后我教大家怎么来进行检测验证,因为这个缓存功能是对已登录用户没有用的(你总不想自己也看到的是缓存页面吧?),所以最好是借助一下网页测速之类的网站来进行检测,比如:https://gtmetrix.com

不需要登录注册,使用这个网站测试一下自己的博客,测试完成之后切换到“Waterfall”这一项,查看自己的网站地址(注意是自己网站的 HTML 页面地址,不是其它静态资源),在响应头也就是 Headers 这项里面,如果能找到下面这样的响应头,就是缓存功能在正常工作了:

x-html-edge-cache:cache,bypass-cookies=wp-|wordpress|comment_|woocommerce_
x-html-edge-cache-status:Hit
x-html-edge-cache-version:0

这几个响应头简单解释下意思:

  • x-html-edge-cache:显示页面缓存是否需要更新和具体的缓存规则
  • x-html-edge-cache-status:显示页面缓存状态,显示 hit 就是缓存命中成功了
  • x-html-edge-cache-version:缓存版本,当网站每次清除缓存时,这个数字就会发生变化

从上面这三项返回的数据,我们就可以判断缓存是不是在正常工作了,如果还要看自动清除缓存功能是不是正常的话,只需要在更新文章之类的操作之后,再用这个网站测试一下,查看 x-html-edge-cache-version 这里的数字是不是和上次测试的不一样,如果是的话就是缓存被清除过了。

结束

好啦,说到这里就差不多了。其实教程没什么难的,基本上就是照着官方的教程说明操作一遍就完事了,只不过我还额外补充了点东西,好让大家能更好理解。

最后感谢 Cloudflare 的官方例子,具体地址在这里:https://github.com/cloudflare/worker-examples/tree/master/examples/edge-cache-html

如果你对 Workers 感兴趣的话,官方还有给出其它 Workers 用法的例子,也可以看看:https://github.com/cloudflare/worker-examples

最后的最后,也祝 Cloudflare 10 周年快乐,希望以后能带给我们更多优化网站的功能,当然不收钱最好,嘿嘿嘿~

PS:找不到比较满意的配图了,就随便用了个 NASA 的图,出处:https://unsplash.com/photos/Q1p7bh3SHj8

文章标题:[WordPress]利用 Cloudflare Workers 来缓存博客的 HTML 网页(写于:20201130)
本文作者:希卡米
链接:https://hikami.moe/master/program/3706.html

如非文内特别说明,博客内作品均默认采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。
知识共享许可协议

评论

  1. 已编辑
    2年前
    2022-6-09 20:46:26

    今天趁热打铁,将大佬教程进行了复现。因为CloudFlare的后台其实有比较大的变化,可能我的教程对小白来说也有一些参考价值吧!我对于测试有效性方面也进行了更加细致的描述。总之,谢谢大佬的教程了!WordPress博客的CDN工作算是完成了一大块了。详见教程:Docker系列 WordPress系列 通过Cloudflare Workers加速WordPress博客。欢迎指教ヾ(≧∇≦*)ゝ

    • 希卡米
      博主
      Bensz
      2年前
      2022-6-09 23:04:01

      好的呀,我都好久没折腾 Worker 了,正如你前面分享的那篇文章所说,估计我的坏掉了,回头还得学习下你的文章重新设置下∠( ᐛ 」∠)_

  2. 2年前
    2022-6-09 9:25:17

    喔 我知道了!原来不能看某个图片 要看html这个网址(~ ̄▽ ̄)~ 我的结果如下,应该是生效了:

    
    age: 698
    alt-svc: h3=":443"; ma=86400, h3-29=":443"; ma=86400
    cache-control: no-store, no-cache, must-revalidate
    cf-cache-status: HIT
    cf-ray: 7186016f7afc982b-SJC
    content-encoding: br
    content-type: text/html; charset=UTF-8
    date: Thu, 09 Jun 2022 01:13:35 GMT
    expect-ct: max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct"
    expires: Thu, 19 Nov 1981 08:52:00 GMT
    last-modified: Thu, 09 Jun 2022 01:01:57 GMT
    link: <https://blognas.hwb0307.com/wp-json/>; rel="https://api.w.org/", <https://blognas.hwb0307.com/wp-json/wp/v2/posts/873>; rel="alternate"; type="application/json", <https://blognas.hwb0307.com/?p=873>; rel=shortlink
    nel: {"success_fraction":0,"report_to":"cf-nel","max_age":604800}
    pragma: no-cache
    report-to: {"endpoints":[{"url":"https:\/\/a.nel.cloudflare.com\/report\/v3?s=exmp%2FzbAXtqI2XFjZ6g3iAMsgb%2BqNB5HHaLCPBXHoEcLbHKKIkDa7evDKWihbq5NYUaPyf73TkNet%2FAMpv9vxafsgqs5jlOw4gNnIKXQ4X78G2uaVsS%2BoWTxTgMbAL9RUROezMWe"}],"group":"cf-nel","max_age":604800}
    server: cloudflare
    vary: Accept-Encoding,Cookie
    x-html-edge-cache: cache,bypass-cookies=wp-|wordpress|comment_|woocommerce_
    x-html-edge-cache-status: Hit
    x-html-edge-cache-version: 65
    x-powered-by: PHP/7.4.29
    x-served-by: blognas.hwb0307.com
    
  3. 2年前
    2022-6-07 11:12:58

    另外,我在worker的后台看到这样的报告:


    查看图片
    image-20220607111203377


    所以这是成功了?

  4. 2年前
    2022-6-07 10:41:20

    大佬,我是结合https://sleele.com/2021/03/26/edge-cache-html-via-cloudflare-workers/和您的文章一起看的。我对于“添加Route并绑定Workers脚本”这里十分费解。
    因为现在CloudFlare的后台和您写文章时已经大不相同,所以我也不知道自己设置的对不对:


    查看图片
    image-20220607103911537


    Route功能好像已经内置到Worker的触发器里面了。但我也不知道要怎么启用,似乎没有提供相关的选项。我检查了自己网站请求时的header,似乎并没有类似sleele大佬的成功记录:


    查看图片



    看看是什么情况?谢谢哈

    • 希卡米
      博主
      Bensz
      2年前
      2022-6-08 11:46:10

      你这个不是没问题嘛,我看你网站也是有缓存的呀~
      PS:友链加了哦,网站的背景图真好看|´・ω・)ノ

      • 希卡米
        2年前
        2022-6-08 11:57:21

        谢谢互链!我这个图片是转载别人的 我自己的网站好像没成功。。( ̄△ ̄;)

        • 希卡米
          博主
          Bensz
          2年前
          2022-6-09 3:39:35

          我看是有缓存的了,你可以试试清理下你浏览器的缓存或者强制刷新下页面再看看 header。

          • 希卡米
            2年前
            2022-6-09 7:58:50

            我清缓存后看还是一样的 比如我看一张图片:

            accept-ranges: bytes
            age: 1638
            alt-svc: h3=":443"; ma=86400, h3-29=":443"; ma=86400
            cache-control: max-age=14400
            cf-cache-status: HIT
            cf-ray: 718589c4beea8ef9-HKG
            content-length: 43236
            content-type: image/jpeg
            date: Wed, 08 Jun 2022 23:51:54 GMT
            etag: "a8e4-5e0632a998a63"
            expect-ct: max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct"
            last-modified: Wed, 01 Jun 2022 13:50:04 GMT
            nel: {"success_fraction":0,"report_to":"cf-nel","max_age":604800}
            report-to: {"endpoints":[{"url":"https:\/\/a.nel.cloudflare.com\/report\/v3?s=lgVA2uDLbEufkEpWungH54ElRETBWNlhjhlNH5yb6sh11VFUD1Ir1kOtetSlZLdF%2BMxi2FbCrHnbYeMz8AGSv9mxMjKz14JEESrxLeyV9pAuPYgju6uGYMkKVf9p5tfCHt4CbBZi"}],"group":"cf-nel","max_age":604800}
            server: cloudflare
            vary: Accept-Encoding
            x-served-by: blognas.hwb0307.com

            cf-cache-status: HIT应该只能说明在Cloudflare缓存成功,任何开了小黄云的网站应该都会有这个吧?这应该不能说明Workers生效了吧

  5. 3年前
    2021-9-11 16:54:53

    开启CF Workers,请问会否对原有的缓存插件有影响?

    • 萌茶
      皮皮
      3年前
      2021-9-11 19:07:41

      没什么影响,用了这个就相当于是在缓存插件之上又多了一层缓存,用户访问时先看到的是 Cloudflare 的缓存,Cloudflare 访问源站时拉取的是缓存插件缓存过的页面。

  6. 3年前
    2021-1-01 0:20:25

    新年快乐~2021年要开开心心吖ヾ(≧∇≦*)ゝ

    • 萌茶
      Vinking
      3年前
      2021-1-01 1:34:28

      哇!谢谢!希望新的一年大家都能健健康康开开心心的!୧(๑•̀⌄•́๑)૭

发送评论 编辑评论

|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇