本文最后更新于 20 天前,如有失效请评论区留言。
底部日志部分可查看最新测试情况。
前言
目前我的个人博客托管在国外VPS上,正常情况下国内用户访问是比较慢的。一般的,如果博客是在国内已备案的机器上,只要设置内容分发网络(Content delivery network, CDN)即可大大加速国内各地用户访问博客网站的速度,并且极大地减少源站的访问压力。虽然国内CDN流量一般是要收费的,但对于小站来说没有多少流量,花不了什么钱。对于VPS在国外、域名托管在Cloudflare的小伙伴来说,免费的CloudFlare CDN本来是一个不错的选择,无奈其免费服务无法根据cookie区分访客提供针对性服务,导致国内用户在访问的时候可能会访问一个离他们很远的CloudFlare泛节点,速度甚至比直接访问源站还要慢,因此很多人都戏称CloudFlare CDN是“反向优化”、“众生平等”。在较早的时候,七牛云等CloudFlare Partner的CDN是一个不错的选择,可惜在博主建站的时候CloudFlare Partner基本上已经无法正常使用。更难受的是,为非ICP备案的国外VPS优化国内线路的第三方CDN往往收费极其昂贵,基本上都是面向企业级用户,不是我这种小站长用得起的。难道用国外VPS的小伙伴就没有一种免费好用的网站加速方案吗?
一个偶然的机会,我了解到了CloudFlare Workers这个东西。CloudFlare Workers通过用户自定义规则,利用Worers KV构建无服务器应用程序并在全球范围内即时响应,可以获得较低延迟的访问。结合《抛弃Cloudflare Page Rule,拥抱Workers–自定义CDN缓存策略》、《[WordPress]利用 Cloudflare Workers 来缓存博客的 HTML 网页》两篇教程的内容,我测试了一下,发现可以成功,并且确实大大地降低了国内用户访问博客的TTFB(至少我自己测试的时候是这样的)。我将两位大佬的教程结合自己的经验糅合了一下,并基于最新的CloudFlare后台界面的相关操作进行教程编写,以方便小白用户食用(大佬们的教程都写得比较晦涩)。下面,我们就来看看如何利用CloudFlare Workers加速WordPress网站!
原理
- 编写一个Worker:大佬们已经写好了脚本,我们照搬过来就行。本文所提供的Worker的作用就是根据cookie来区分访客并针对性提供访问内容,并且不会缓存已登录或已评论用户的信息。
- 添加一个Workers KV:据介绍,其作用是“在 Cloudflare 网络中存储应用程序数据,并从 Workers 访问键值对”。我个人的理解是,KV就是一种可以免费使用的储存方案,只是免费用户每天有请求数量的限制(10w/天)。
- Cloudflare Page Cache:WordPress插件,可以根据你后台的文章、页面等更新自动在CloudFlare中缓存HTML页面。主要是配合Worker使用。
- 禁用Cloudflare Page Rule:由于我们已经有Worker来定义缓存规则,所以可以禁止Cloudflare Page Rule缓存,不然Cloudflare Page Cache会将用户信息也缓存上去。
创建KV空间
首先,我们要创建一个KV空间,这个操作不难:
创建好后,你可以进去看一下,里面是没有任何条目的。因为我们还没有开始缓存,所以这里还没有数据。
设置Worker脚本
首先,我们从Workers——概述——创建服务
里进去并创建一个Workers:
你可以改个服务的名称,然后点创建服务即可:
下面,直接快速编辑这个Worker:
将它默认的规则全部删除,并填入下面大佬们写好的规则:
(~ ̄▽ ̄)~ 一串很长的代码 (~ ̄▽ ̄)~
// 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.
// Default cookie prefixes for bypass
const DEFAULT_BYPASS_COOKIES = [
"wp-",
"wordpress",
"comment_",
"woocommerce_"
];
// URL paths to bypass the cache (each pattern is a regex)
const BYPASS_URL_PATTERNS = [
/\/wp-admin\/.*/,
/\/wp-adminlogin\/.*/
];
/**
* 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;
// Bypass the cache for all requests to a URL that matches any of the URL path bypass patterns
const url = new URL(request.url);
const path = url.pathname + url.search;
if (BYPASS_URL_PATTERNS.length) {
for (let pattern of BYPASS_URL_PATTERNS) {
if (path.match(pattern)) {
bypassCache = true;
break;
}
}
}
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);
}
效果图如下,最后记得点下方的保存并部署
:
它不会自动跳转回worker设置界面的,自己回去就好。就是上图左上角的←silent***
的链接。
Worker脚本绑定KV空间
我们进入Worker脚本 ,在设置
中进行KV空间的绑定:
然后填写下列内容:
注意,EDGE_CACHE
是Worker脚本里的一个变量名称
,所以你不可以更改。KV命名空间
就下拉列表并选择你自己刚刚创建好的KV空间即可。
在Worker中添加Route
Route即路由、路径之意,就是你希望Worker缓存什么路径。一般都是缓存博客根目录,比如blognas.hwb0307.com/*
。当然,更加合理的规则由Worker脚本进行定义,我们就简单地将这个路径加入到Worker中即可。
我们重新进入Worker:
在触发器
里添加新路由,按画面提示做相应的选择即可:
到这里,CloudFlare的设置就基本完成了。
安装Cloudflare Page Cache插件
由于这个插件未在官方频道上传,所以我们要将Cloudflare Page Cache下载下来,然后通过Zip在后台上传并安装插件:
安装成功后记得启用
。不用启动自动更新。这个插件启用后没有界面的,保证其处于启用状态即可。它的作用就是每次你的博客内容(文章、页面、说说等)发生变化时将内容推送至CloudFlare。不过,据说Cloudflare Page Cache
每次更新都是重置全部缓存,这个特性感觉不太智能。你也可以在Chrome浏览器的Header的x-html-edge-cache-version参数观察到版本号的变化。虽然实际使用过程中用户体验基本不受影响,但Cloudflare Page Cache
对VPS带宽的影响还需要持续观察。
禁止Page规则
进入域名管理界面,从规则——页面规则
这里进入,创建页面规则。这里主要是为了禁用Page,从而将缓存规则完全由Worker进行定义。
按下面的内容填写:
检查是否生效
完成一系列复杂的设置,下一步肯定是测试一下这些设置有没有生效。测试时不可以处于登陆状态(如果你使用WP Super Cache,登陆状态时会返回源站数据),并且将上网代理关闭以使用国内原生的网络环境。
打开一个无痕窗口并登陆自己的博客,按下图指示进行操作。记得要将Disable cache
打勾,这样我们就不会因为本地缓存的存在而影响对网页元素的加载时间的测试。
我们注意到图片中的这两项
x-html-edge-cache-status: Hit # Hit就说明生效了
x-html-edge-cache-version: 66 # 缓存的版本号
这与后台的KV空间的信息是一一对应的:
可能需要一段时间才会成功缓存,如果刚刚开始不成功可以等等再测试。如果实在没法成功,也可以评论区留言交流。
如果生效了,如果查看效果呢?在无痕窗口的Timing里进行查询:
我用登陆状态进行测试,此时我是直接访问源站,可见访客访问我博客的时候,TTFB得到了巨大的改善(源站1.11s缓存197.72ms):
这是日常使用时的一次测试(广东深圳电信)。访客一般是非登陆用户,即看右边那一列的数据即可:
当然,一次数据不是很严谨。大家可以刷新多次,或者在不同的时间段进行测试。据我观察,Cloudflare Worker对本站访客的浏览性能优化很大,特别是访客使用了PROXY浏览。
Cloudflare v20241421版本的注意事项
因为Cloudflare是一个经常更新的平台,所以这里特定添加一些说明。不过,尽管应用的位置发生变化,但只要Cloudflare还能支持这些功能,本教程依然是有效的:
- 包含支持自定义JS脚本的Worker功能
- 支持KV存储
首先,KV和Worker的位置有所变化,这是最新版本的位置:
现在会提供一些模板给开发者,我们选那些支持JS脚本的模板即可:
这是我2024-12-21时Worker的设置界面的一些参数:
如果你也是差不多这样,但还是没有办法成功Cache,可以从以下几个方面排查:
- 脚本是否有填写错误。一般来说直接copy就好;
- 是否成功安装并启用WP Super Cache插件。因为需要使用它生成压缩html;
- 是否成功安装并启用Cloudflare Page Cache插件。因为需要使用它将压缩html推送至Cloudflare的Worker-KV系统;
- 使用无痕浏览器,退出登陆;
- 等待一段时间再测试;
如果这些都不行,估计只能远程看一下了。可以加苯苯的tg群进一步讨论。
小结
本文所提供的WordPress加速策略主要是将WP Super Cache
预缓存好的HTML页面通过Worker
规则进行缓存,这其实已经解决了个人站长对CDN的大部分需求。对于主题资源的CDN,如Argon,可以选择开启jsDelivr的缓存(fastly目录在国内可用)或者利用DogeCloud之类的自建主题缓存;也可以直接用源站资源通过Worker进行缓存。无论哪种方案,对国内用户也足够友好了。当然,由于访问次数的日限制,免费的Worker策略对于较大的网站来说是不太具有可行性的。如果你运营着一个比较大的网站且市场主要在国内,还是乖乖地使用国内备案的域名和VPS,上国内厂商的CDN;或者考虑CloudFlare Worker的付费服务。
另外,博客中的Chevereto图片暂时无解。因为也无法通过*hwb0307.com/*
的规则解决,因为Chevereto并没有后台的WordPress插件支持,这样使用只能初始化第1次缓存。只能暂时使用默认的CloudFlare CDN缓存。
目前Workers方案在我博客里处于测试阶段,以后会进一步反馈使用体验!有什么问题欢迎留言或加群讨论!
日志
2024-12-21
2022-06-17
昨天发现Cloudflare有一个Argo
的付费功能,和ip优先的功能很相似:
其子功能Smart Routing
和Tunnel
的相关介绍如下:
Smart Routing
Argo 的智能路由算法使用实时网络情报通过最快的 Cloudflare 网络路径来路由流量,同时保持开放、安全的连接以消除连接设置所导致的延迟。
Tunnel
使用安装在源服务器基础结构(包括容器或虚拟机)中的轻量级后台程序,Cloudflare Tunnel 可以在最近的 Cloudflare 数据中心与应用程序的源服务器之间创建一个加密隧道,而无需打开公共入站端口。
这看着真香啊,就是要钱( ̄△ ̄;)。感觉还蛮贵的,但企业用户肯定爽得不行。
2022-06-11
经过测试,由于免费CloudFlare CDN只有两个备选ip,其实怎么优化效果都是类似的。Workers还是要结合CloudFlare Partner使用才可具有最佳性能。CloudFlare Partner
在免费加速的过程中估计是绕不开的。不过最近看了一下CloudFlare的用户计划,发现IP优选已经是企业级应用了:
估计是这个功能太香了,CloudFlare已经在Partner阶段测试完,现在要开始盈利了。估计个人用户很难白嫖到这个IP优选的功能了。
扩展阅读
---------------
完结,撒花!如果您点一下广告,可以养活苯苯😍😍😍
现在cloudflare更新了很多好多页面都变化了,能否抽空更新一下呀~
我刚刚上去看了一下,其实很多东西没有实质性的变化,比如KV空间、Worker、Route等。未来我会更新一下教程,但并不会很快推出 ~ 谢谢提醒哈!
我在新版尽量按照原来的步骤操作了一下,但是最终只显示x-html-edge-cache:
cache,bypass-cookies=wp-|wordpress|comment|woocommerce
x-html-edge-cache-status:
Miss, Cached
x-html-edge-cache-version:
0
这一行代码,请问这是什么原因呀~该怎么解决/
方便的话可以公布下网址我看看
已经解决了,感谢博主的秒回复
问题出在第六步第一行添加域名时不能按照cf的提示在域名前面加*/,删掉就好了
OK,我也简单地更新了下教程,作为一个过渡。 我觉得应该还是够的 ~ 毕竟要折腾Worker的人多少都有一些布署应用的经验,本教程已经是足够使用了。 另外,之前写文章的时候,Cloudflare的R2存储还没推出。它其实是解决Chevereto的边缘存储的一个良好方案,而且实现起来不难。感兴趣你也可以试试。 有其它问题再多多交流!
对于Cloudflare我只能说他是我心中最美的赛博菩萨!他太强了,而且免费的东西太多啦,很难让人不爱!
我今天把图床从chevereto转移到了cloudflare R2,发现R2自带的自选位置功能就相当于图片的优选ip功能,现在我关闭了cloudflare对图片的缓存,直接让浏览器加载R2上的图片链接,感觉打开图片的时间起码比以前少了一半,你有兴趣也可以尝试一下,我就是忽然想起你在这篇文章里提到过图片通过cf缓存以后慢的问题。
好的,谢谢提醒 (~ ̄▽ ̄)~
我看到这个教程,R2也可以通过wordpress插件的方式使用: A Guide to Using Cloudflare R2 with WordPress – Jack Whitworth 有时间我测试一下相关的方案
我其实主图床还是chevereto,因为我要使用它的水印功能,只是每次我更新chevereto的图片以后,会通过rclone同步更新到R2,所以我不直接使用wordpress插件对接R2。
今天测试了这种方法,3秒就能打开,已经完全可以接受了。。本来我是用动态重定向,将所有国内非CDN地址访问博客的请求都重定向到国内域名的,1秒就能打开,但是毕竟要花云主机和买cdn流量的钱,平均下来1个月20多,是笔巨款。。可惜我已经买了2年的云主机,暂时没法用这种方式了,就留作备用吧,已经不错了。
确实是可以的,我目前用的就是这个方案
大佬,这个代码贴进去有报错
图片咋贴,,,
查看图片
有空我看看~最近也有人试这个方法 当时没问题
谢谢捏
你好,Bensz。为什么我按照你的教程做后
x-html-edge-cache-status: Miss,Cached
查看图片
我该如何解决
我看的时候是成功的,如图:
查看图片
。 你可以试试: 1. 以访客身份访问。 2. 使用无痕浏览器。 3. 查看页面而不是动态资源或者非wordpress的静态资源。
试了,还是x-html-edge-cache-status: Miss,Cached
可能是我电脑问题吧
感谢分享,来晚了感觉嫖不到免费的优选了
还好啦 现在我的网站也挺快了
可以试试这个 Cloudflare 优选 https://cf.vvhan.com/
大佬,你好,看了你这文章,有点问题关于Page规则的,能不能增加一个怎么配置的图
原文这个图写得很清楚啦(
查看图片
),您还有什么疑问不?可否具体一些?
就是点创建规则后,又弹出一个界面,让我选择创建什么规则。
我懂你的意思了,哈哈!如图:
查看图片