Home Data Center Series Cloudflare Tutorial (VII) Introduction to CF Worker Functions and Practical Operation, Verification and Research on Related Technical Principles of Implementing "Beggar Version APO for WordPress" Function to Accelerate Website Access Based on Worker

Preface

Part 7 of the CF series of tutorials, the time is finally ripe to write a CF worker tutorial.

Why do I say that? The reason why I leave CF worker to be written later is becauseIn order for a certain type of traffic to be finally accepted and processed by the corresponding worker, the prerequisite is that the traffic cannot be hit and processed by the rules of the node function module located before the worker in the CF traffic sequence, and the functions implemented by different types of workers may require the coordination of the rules of different node function modules: for example, if the worker that optimizes http access is to be able to process access requests normally, the access request must not be hit by the cache rules of the Cache Rules located before the worker in the traffic sequence. Otherwise, the Cache Rules will directly use the content in the CDN to respond to the access request. The consequence of this is that the access request cannot reach the worker at all, and naturally cannot be optimized for access by the worker; at the same time, the access request cannot be hit and processed by other node function modules, for example, it cannot be hit by the rules in the WAF rule whose processing behavior is blocking.

In the description of the green font part above, if I don’t explain clearly in advance what are the concepts of "traffic series", "node function module", "Cache Rules", cache rule hit, "WAF" and "WAF rule whose processing behavior is blocking rule", most people will probably be confused after reading this description. However, this paragraph is the basic premise for any worker to work properly, and it cannot be avoided, not to mention that many concepts such as edge network will be involved later in the article.

In order to avoid involving too many concepts in the same article and making the logic appear disorganized, and at the same time, writing a worker tutorial requires so much prerequisite knowledge, I simply arranged the worker tutorial to be written later. Before that, I will start with the overall solution of CF and related basic concepts in the form of a series of tutorials, and slowly sort out the important node functions before the worker in the traffic sequence. Now, the prerequisite knowledge is basically clear, and I can finally start writing the worker tutorial.

Note 1: For other prerequisites, please refer to CF series tutorials from "I" to "VI", especially "II" (Cloudflare tutorial series for home data centers (Part 2) Introduction to the functions of each technical node in the CF overall solution traffic sequence) and "six" (Home Data Center Series CloudFlare Tutorial (VI) CF Cache Rules Function Introduction and Detailed Configuration Tutorial).

Note 2: In order to sort out some related concepts (some of which are my sudden ideas), this article will involve a lot of professional knowledge across multiple fields. Friends who are not interested can jump directly to the practical part at the end (I didn’t expect it to involve so much, but I just thought it through and then wrote it down. I wrote more and more without realizing it ~).

However, those friends who are really interested in technology may wish to read it. I believe they will gain something.

Introduction to CF worker related concepts

What is CF worker?

In simple terms, CF worker is a way provided by CF for users to run JavaScript code serverlessly (it can be simply understood as CF using its own edge network server's CPU and memory to help users run JS code and transmit the results back to users). In addition to running JS code normally to implement the functions written by the code itself (such as some unconventional forward proxies and reverse proxies, but these are not covered in this article), the most important thing is that some CF functions, such as edge cache, can be controlled through workers. This way of controlling edge cache through workers, compared with the original Free plan's method of roughly controlling CDN through Cache Rules (or page rules) (there are only two options of bypassing cache and meeting cache conditions, and at most a time control option of edge TTL and browser TTL), can provide more and more refined cache control, so it can provide faster website access speeds.

Note 1: In fact, even though the original Free plan can only roughly control CDN through Cache Rules (or page rules) and does not support advanced cookie-based targeting services or personalization functions, the basic functions of CF can still route user requests to the available data center closest to them through Anycast IP technology and its global network. It’s just that for well-known reasons, domestic visitors cannot enjoy it (foreign visitors have never said that there is negative optimization~), so this has nothing to do with CF.

Note 2: It is true that using worker to control edge cache can improve the access speed of the website, but the implementation principle is completely different from the commonly said preferred IP (I will talk about the relevant knowledge of preferred IP in the next section). As for the principle of acceleration, it is the same as another paid feature of CF: APO for WordPress, so I call it the "beggar version of APO for WordPress" feature.

Note 3: Since workers are originally built on edge network servers, they naturally have advantages. For example, JS code can be run on the CF edge network close to the user to optimize and customize HTTP requests and responses, improve application performance, reduce latency and enhance security, and ultimately achieve the goal of improving user access experience.

Note 4: In addition to JavaScript, workers also support Rust or other WebAssembly languages.

Note 5: For the Free plan, the free quota for workers is 100,000 requests per day.

Additional knowledge: IP selection

Clarification of the concept of "preferred IP"

1. First of all, it should be made clear that the term "preferred IP" is not the official name of CF, but a term used by some users and third-party service providers to describe CF. China NetworkThe informal name used for the (China Network) feature is probably because the feature improves access speed in mainland China by optimizing IP paths (mainly using the networks and data centers of domestic cloud partners), so users are accustomed to calling it "Preferred IP". This expression does not appear in CF's official documentation or product pages, so it can only be regarded as an informal folk term.

2. The "China Network" function is not available separately and requires an Enterprise plan to obtain it, and the price can only be "customized":
image.png

3. The official function corresponding to this function abroad isArgo Smart Routing, but still expensive:
image.png

In addition: In principle, the website access acceleration achieved in China through the preferred IP (access from mainland China) function and the website acceleration achieved by controlling edge caching through workers in this article are completely unrelated in terms of underlying logic (preferred IP is more widely applicable and more advanced, while the method described in this article is more restrictive and only suitable for HTML-type applications). However, given that some friends like to rely on the preferred IP function when mentioning website acceleration (for example, the statement that the preferred IP function is achieved through workers), I think it is better to explain the preferred IP function first.

Note: For the sake of everyone's reading habits, I will still retain the folk saying of preferred IP, but everyone should remember its true meaning.

How does the preferred IP (China network access) work?


The CF Enterprise Plan users' preferred IP function in China has always made the majority of friends who are used to free stuff salivate, but unfortunately, the Free Plan users can only watch from afar and cannot play with it. However, although they are envious of the preferred IP function, I believe that most of the friends only think about how to get it for free, but have not paid too much attention to the underlying technical principles of the preferred IP function (why does it sound a bit like talking about scumbags?).


CF's preferred IP function is to improve access speed and reduce latency by optimizing CF's internal network routing and selecting the best path and data center to process user requests. This function is transparent to users and does not change the IP address obtained when users resolve domain names, but it can significantly improve the performance and stability of the website. It is based on the following judgment criteria:

  1. Intelligent routing: Dynamically evaluate network conditions, including latency, bandwidth, and congestion, and select the fastest path to transmit data.
  2. Load Balancing: Based on the server load, requests are distributed to the server with the best performance to avoid single point overload.
  3. Location: Based on the user's geographic location, the request is routed to the nearest data center to reduce latency.
  4. Real-time monitoring: Continuously monitor network and server status and automatically adjust routing strategies to respond to network changes and emergencies.

The above description is more official and difficult to understand, so I will sort it out again from the user's perspective.

  1. The IP address of the user's resolved domain name:

• When users resolve a domain name that uses CF through DNS, they will get an IP address assigned by CF, usually the Anycast IP address of CF (Anycast IP technology appeared in 1989 and became widely popular after 1993. The Local DNS addresses of domestic operators are basically based on Anycast IP technology, but they are generally valid within a certain regional network, and at most they are valid domestically. The great thing about CF is that it is valid globally). This IP address is usually universally used and has no direct connection with the "preferred IP" function. For example: a domain name that supports the preferred IP function and a domain name that does not support the preferred IP function can be resolved to the same IP address. In other words, the "preferred IP" function has nothing to do with the resolved IP address. To be accurate, it should be more appropriate to say "preferred routing" or "routing optimization" (so the official function of CF)Argo Smart Routing" is the right name).


Note 1: Anycast IP technology requires BGP dynamic routing protocol as support, so if you have a large network that you can fully control (with independent ASN number, IPv4 or IPv6 public IP address segment), and it is connected to the external network through EBGP, then as long as you properly configure the routing (internal dynamic routing protocol + IBGP) within the controlled network, you can make some of your public IPs Anycast IP in the eyes of the external network.

Note 2: If you use a specific tunnel protocol (such as MPLS) in your network to create the best path between multiple exits (assuming that your large network has two exits and entrances, a normal data packet enters your network from exit 1, and then goes out from exit 2 according to the normal route, it will take 20 hops. However, if there is a tunnel based on MPLS technology connecting exits and entrances 1 and 2, and a special method such as a dedicated line can be used to make the packet from exit 1 go directly to exit 2, then the network nodes passed through will only be 2 hops), and then make it effective for the subsequent packet sequence corresponding to the target domain name "blog.tangwudi.com", then when the request to access "blog.tangwudi.com" enters from entrance 1, it will go out directly from this tunnel to exit 2. How many hops does this optimization reduce? What if the network you can control is spread all over the world (such as cloudflare)? This is only from the perspective of route optimization. What if you add data centers spread all over the world to return to the source nearby? So it is completely understandable that the preferred IP function is expensive.

Note 3: In theory, it would be easier to understand if I drew a picture here, but it's too tiring, so forget it.


  1. CF internal routing optimization:


• The preferred IP function is mainly to optimize CF's internal routing, making requests more efficient when transmitted in CF's global backbone network (China's network in China). It speeds up request processing, reduces latency and improves overall performance by selecting the best network path and data center.
• This optimization is transparent, that is, the IP address obtained by the user after resolving the domain name remains unchanged, and the only change is how CF processes and transmits these requests internally. For example, if it is a website of a domestic CF enterprise package user, although the IP resolved by its domain name is the same as the IP resolved by the domain name of other Free plan users, visitors to the enterprise user website can use this IP to directly enter the CF edge network from the data center of CF's domestic partner (currently JD Cloud):

image.png

The so-called 30 data centers located in mainland China are the data centers of its current domestic partner (JD Cloud):
image.png

Visitors of Free plan users can only enter the CF edge network from the Western United States data center. This gap is not small.

  1. CF's preferred IP function in China:


• In areas where CF does not directly control the network (such as in China. CF abroad basically builds its own backbone network and directly extends it to various countries and cities and directly connects with various operators), the IP optimization function may be subject to some restrictions, especially when the partner's network infrastructure or routing strategy cannot fully support CF's optimization needs.
• Due to these dependencies, the effect of CF's provision of preferred IP in these regions is likely not as significant as in the global backbone areas it controls (but it is still much faster than accessing Free plan user websites from US data centers).

Note: In fact, in addition to the routing optimization mentioned above, the preferred IP function also needs to be coordinated with many key technical components, such as "protocol layer optimization", "TLS optimization", "intelligent DNS resolution", "application layer routing", "real-time performance monitoring", etc. The technical content is at least as high as three or four floors.


Many friends who use the Free plan use the software to manually test to find the "best self-selected IP" (in fact, this should be accurately described as "finding the best IP in the test results at that time"), but this method has significant disadvantages:

  1. Manual operation is complicated and time-consuming: Manual testing and selecting the best IP requires a lot of time and effort, especially when frequent testing and adjustments are required. Users need to constantly monitor network conditions and test to ensure that the selected IP address is always the best.
  2. Lack of real-time performance:Network conditions change dynamically (the same IP may be the best at this moment, but it may not be the best a few minutes later), and the results of manual testing may quickly become outdated and fail to reflect the current network status in a timely manner. This means that even if an optimal IP is selected, it may quickly lose its advantage.
  3. Lack of global perspective:Testing of individual users is usually limited to their geographical location and network environment, and cannot fully reflect the best IP selection under different regions and network conditions. CF's preferred IP function can optimize based on global network conditions.

When I first started learning to build a blog in the second half of last year, I used a registered domain name to build it (I was still very naive and simple at that time, and I didn’t know how deep the pit of building a personal blog in China was. Those who are interested in this can refer to the article:Home Data Center Series Independent Personal Blog Building and Pitfall Avoidance Guide), I tried to use CF to customize the host name + manually select the best IP. At that time, it felt unstable. It seemed okay at the beginning, but it became slower after a while. This forced me to pay attention to the changes in access speed frequently, which was very tiring. Later, I directly used Tencent Cloud's CDN for domestic access.


Why can worker-controlled edge caching achieve better access performance than CF regular CDN?

This is mainly because workers have the following key features:

  1. Flexible programming capabilities:

Workers allow developers to write custom JavaScript code to handle requests. This flexibility means you can write logic to select and route traffic to different IP addresses to achieve better results.

  1. Edge computing advantages:

CF workers run on edge nodes distributed around the world, and can quickly respond to and process user requests. With workers, you can dynamically select the best IP address based on real-time network conditions, geographic location, server load, etc.

  1. Bypass basic function restrictions:

While the default feature set in the Free plan may not support some advanced routing and load balancing features, workers provide a programming interface that enables you to implement these features yourself. For example, with workers, you can write logic to detect network conditions and select the best path without relying on the default settings of the basic plan.

  1. Custom HTTP request processing:

Using workers, you can intercept and modify HTTP requests and responses. This allows you to forward requests to different target IP addresses based on custom preferred IP logic, thereby achieving better performance and reliability.

Therefore, we cannot say that CF only provides regular CDN functions in the Free plan. The accurate description should be: in the Free plan, it quietly provides more sophisticated edge cache functions in a relatively implicit, difficult to be discovered, technically demanding, and limited manner (worker). Of course, this cannot be compared with the preferred IP function, but it can only be said to be better than the cache effect achieved by Cache Rules (or page rules).

Additional knowledge: worker KV (Key-Value)

I mentioned earlier in the article that "workers can be simply understood as CF using its own edge network server's CPU and memory to help users run JS code and pass the results back to users." This raises a question. Some codes do not require caching, persistent storage, session management, etc., so they can be run directly in the memory of the CF edge server, for example:

  1. HTTP request handling: Process, modify and respond to HTTP requests.
  2. Edge computing: Execute logic at the edge node before the request reaches the origin server.
  3. API Gateway: Routing and processing API requests.
  4. Static content serving: Serve static files or generate dynamic content.
  5. Request forwarding: Forward requests to other servers for load balancing, etc.

However, the running of some codes requires caching, persistent storage, session management, etc., which cannot be achieved by relying solely on the CF edge server memory. Therefore, to meet these requirements, a supporting function of the worker is needed: KV (Key-Value, key-value storage) to assist in the implementation (similar to the docker run command using the -v parameter to mount external storage to achieve persistence of specific files or folder contents).

CF Worker KV is a fully managed key-value storage service provided by CF, designed specifically for CF workers. It allows developers to store and access data on globally distributed CF edge servers, which can be used by workers, allowing you to execute more complex logic and process requests at the edge.

Some key features include:

  1. Global Location: Data is stored on edge servers in multiple locations around the world, enabling low-latency access.
  2. Fast reading and writing: Provides fast read and write access and is suitable for applications that handle high throughput and low latency requirements.
  3. Scalability: Supports large-scale data storage and high concurrent access, suitable for processing a large number of concurrent requests.
  4. version control: Supports version control and transaction operations, making data updates more secure and controllable.
  5. safety: Data is encrypted during transmission and storage to ensure data security and privacy protection.

As it happens, using workers to implement the "beggar version of APO for WordPress" function requires KV to cooperate in completing the edge cache operation.

Note: CF worker KV has some usage restrictions in the Free plan, including:

  1. Storage Limits: In the Free plan, the total capacity of KV storage is limited to 1 GB. This means that the total size of key-value pairs you can store cannot exceed 1 GB.
  2. Read times: The Free plan has a limit of 100,000 read operations per month. After this limit, additional read operations will be charged.
  3. Number of writes: The Free plan has a limit of 1,000 write operations per month. After this limit, additional write operations will be charged.
  4. List Operations: Listing operations (listing keys) are limited to 1,000 per month.
  5. performance: The KV data of the Free plan is synchronized to all data centers around the world at a slower speed and with higher latency. Premium plans (such as Pro or Enterprise) provide faster synchronization speeds and lower latency.
  6. API request rate: In the Free plan, the allowed rate of API requests per second is lower, while the premium plans allow a higher rate.

In general, these limitations make the Free plan of KV only suitable for small project development and testing, but it is enough for personal blogs.

Use CF worker to implement the function of "beggar version APO for WordPress" for WordPress

Background: APO For WordPress and Edge Cache HTML

CF officially launched the Automatic Platform Optimization (APO) for WordPress feature on October 1, 2020. This feature is designed to optimize the performance of WordPress websites, reduce loading time, and improve user experience through CF's global content delivery network (CDN). APO automatically caches and optimizes the content of WordPress websites, including HTML, JavaScript, CSS, and Images, thereby speeding up page loading and reducing server load. This optimization, as mentioned in the CF official blog, is actually accomplished through workers:
image.png

APO for WordPress itself is a paid service ($5/month), which is still relatively expensive for most personal webmasters (after all, most personal webmasters nowadays do it for fun, and they save as much as possible, and their traffic is usually not large, so paying is a bit uneconomical). However, APO for WordPress can use workers for optimization, so for users of the Free plan, can they also use workers to optimize the access to WordPress sites?

The answer is yes, thanks to a JS script demonstration template that CF officially released before: edge-cache-html.js
image.png

This JS script can be used with content management systems that support the x-HTML-Edge-Cache header (such as WordPress) through plug-ins, or it can be used directly with most static sites to use edge caching.For non-logged in usersEdge caches HTML, so if you want to use the worker to implement the beggar version of APO function later, you need to modify it based on the JS demonstration script as a template. In addition, CF also provides a matching plug-in for wordpress (to support the x-HTML-Edge-Cache header). Isn't it considerate?

Note 1: APO for For WordPress also adopts the plug-in + worker model, but the plug-in is the official plug-in cloudflare, and the worker is automatically effective in the system background, without manual settings and intervention.

Note 2: To use this JS script, you must use the EDGE_CACHE variable to bind the key/value namespace to this Worker script. This variable can be seen in the following code. You can pay attention to it and it will be used later.

Note 3: The following code is only for WordPress. For the JS code for general static websites, please refer to the second to last chapter.

The following is a modified script comment version for WordPress sites. Compared with the official template, I made some adjustments to the variables and optimized it for WordPress cacheable content (I have already talked about which WordPress content needs to skip caching in the sixth tutorial series, so I will not repeat it here):

// 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.

// 定义需要处理的缓存头部信息
const CACHE_HEADERS = ['Cache-Control', 'Expires', 'Pragma'];

// 默认的绕过缓存的 Cookie 前缀
const DEFAULT_BYPASS_COOKIES = [
    "wp-",
    "wordpress",
    "_logged_in_",
    "comment_",
    "woocommerce_"
];

// 使用正则表达式定义需要绕过缓存的URL
const BYPASS_URL_PATTERNS = [
    /\/wp-admin\/.*/,
    /\/wp-adminlogin\/.*/,
    /\/wp-login\/.*/,
    /\/wp-comment\/.*/,
    /xmlrpc.*/,
    /preview=true.*/,
    /[?&]s=.*/
];

// 监听 fetch 事件,处理每个请求
addEventListener("fetch", event => {
    // 在发生异常时继续传递事件
    event.passThroughOnException();
    // 处理请求
    event.respondWith(handleRequest(event));
});

// 主请求处理函数
async function handleRequest(event) {
    const { request } = event;

    // 如果请求应绕过缓存,则直接获取请求
    if (shouldBypassRequest(request)) {
        return fetch(request);
    }

    // 获取缓存的响应
    let { response, cacheVer, status } = await getCachedResponse(request);

    // 如果没有缓存响应,则获取并缓存响应
    if (!response) {
        response = await fetchAndCache(request, cacheVer, event);
    } else {
        status = 'HIT';
        // 更新缓存(如果需要)
        event.waitUntil(updateCacheIfNeeded(request, cacheVer, event));
    }

    // 增强响应头
    return enhanceResponse(response, status, cacheVer);
}

// 判断请求是否应绕过缓存
function shouldBypassRequest(request) {
    const accept = request.headers.get('Accept');
    const isImage = accept && accept.includes('image/*');
    return isImage || !isConfigured() || isUpstreamCachePresent(request);
}

// 检查配置是否完整
function isConfigured() {
    return typeof <font color="#dd0000">HTML、JavaScript、CSS 和图像</font>EDGE_CACHE !== 'undefined' || (
        CLOUDFLARE_API.email.length && CLOUDFLARE_API.key.length && CLOUDFLARE_API.zone.length
    );
}

// 判断请求头中是否有上游缓存信息
function isUpstreamCachePresent(request) {
    return request.headers.get('x-HTML-Edge-Cache') !== null;
}

// 获取缓存的响应
async function getCachedResponse(request) {
    if (!shouldCacheRequest(request)) {
        return { response: null, cacheVer: null, status: 'Miss' };
    }

    const cacheVer = await getCurrentCacheVersion(null);
    const cacheKeyRequest = generateCacheRequest(request, cacheVer);
    const cache = caches.default;

    let response = await cache.match(cacheKeyRequest);
    if (response) {
        response = new Response(response.body, response);
        if (shouldBypassEdgeCache(request, response)) {
            return { response: null, cacheVer, status: 'Bypass Cookie' };
        }
        response = cleanupCacheHeaders(response);
        return { response, cacheVer, status: 'Hit' };
    }

    return { response: null, cacheVer, status: 'Miss' };
}

// 判断请求是否应缓存
function shouldCacheRequest(request) {
    const accept = request.headers.get('Accept');
    return request.method === 'GET' && accept && accept.includes('text/html');
}

// 清理缓存头部信息
function cleanupCacheHeaders(response) {
    CACHE_HEADERS.forEach(header => {
        const value = response.headers.get(x-HTML-Edge-Cache-Header-${header}); if (value) { response.headers.set(header, value); response.headers.delete(x-HTML-Edge-Cache-Header-${header});
        }
    });
    return response;
}

// 获取并缓存响应
async function fetchAndCache(request, cacheVer, event) {
    const response = await fetch(modifyRequest(request));
    if (shouldCacheResponse(request, response)) {
        const status = await cacheResponse(cacheVer, request, response, event);
        response.headers.set('x-HTML-Edge-Cache-Status', status);
    }
    return response;
}

// 修改请求头部以包含缓存支持信息
function modifyRequest(request) {
    const newRequest = new Request(request);
    newRequest.headers.set('x-HTML-Edge-Cache', 'supports=cache|purgeall|bypass-cookies');
    return newRequest;
}

// 判断响应是否应缓存
function shouldCacheResponse(request, response) {
    const accept = request.headers.get('Accept');
    return request.method === 'GET' && response.status === 200 && accept && accept.includes('text/html');
}

// 缓存响应
async function cacheResponse(cacheVer, request, response, event) {
    const cache = caches.default;
    const cacheKeyRequest = generateCacheRequest(request, cacheVer);
    const clonedResponse = new Response(response.body, response);

    CACHE_HEADERS.forEach(header => {
        const value = clonedResponse.headers.get(header);
        if (value) {
            clonedResponse.headers.set(x-HTML-Edge-Cache-Header-${header}, value);
            clonedResponse.headers.delete(header);
        }
    });

    clonedResponse.headers.set('Cache-Control', 'public; max-age=315360000');
    event.waitUntil(cache.put(cacheKeyRequest, clonedResponse));
    return "Cached";
}

// 根据需要更新缓存
async function updateCacheIfNeeded(request, cacheVer, event) {
    if (shouldCacheRequest(request)) {
        const response = await fetch(modifyRequest(request));
        if (shouldCacheResponse(request, response)) {
            await cacheResponse(cacheVer, request, response, event);
        }
    }
}

// 增强响应头部信息
function enhanceResponse(response, status, cacheVer) {
    const newResponse = new Response(response.body, response);
    if (status) {
        newResponse.headers.set('x-HTML-Edge-Cache-Status', status);
    }
    if (cacheVer !== null) {
        newResponse.headers.set('x-HTML-Edge-Cache-Version', cacheVer.toString());
    }
    return newResponse;
}

// 获取当前缓存版本
async function getCurrentCacheVersion(cacheVer) {
    if (cacheVer === null) {
        if (typeof EDGE_CACHE !== 'undefined') {
            cacheVer = await EDGE_CACHE.get('html_cache_version') || 0;
            await EDGE_CACHE.put('html_cache_version', cacheVer.toString());
        } else {
            cacheVer = -1;
        }
    }
    return cacheVer;
}

// 生成缓存请求
function generateCacheRequest(request, cacheVer) {
    const url = new URL(request.url);
    url.searchParams.append('cf_edge_cache_ver', cacheVer);
    return new Request(url.toString());
}

// 判断是否应绕过边缘缓存
function shouldBypassEdgeCache(request, response) {
    const url = new URL(request.url);
    if (BYPASS_URL_PATTERNS.some(pattern => pattern.test(url.pathname + url.search))) {
        return true;
    }

    const options = getResponseOptions(response);
    const bypassCookies = options ? options.bypassCookies : DEFAULT_BYPASS_COOKIES;
    const cookieHeader = request.headers.get('cookie');
    if (cookieHeader) {
        return bypassCookies.some(prefix => cookieHeader.split(';').some(cookie => cookie.trim().startsWith(prefix)));
    }
    return false;
}

// 获取响应选项
function getResponseOptions(response) {
    const header = response.headers.get('x-HTML-Edge-Cache');
    if (!header) return null;

    const options = {
        purge: header.includes('purgeall'),
        cache: header.includes('cache'),
        bypassCookies: header.split(',')
            .filter(command => command.trim().startsWith('bypass-cookies'))
            .flatMap(command => command.split('=')[1].split('|').map(cookie => cookie.trim()))
    };

    return options;
}

注释说明

    •   缓存头部常量:定义需要处理的缓存头部信息。
    •   默认绕过缓存的 Cookie 前缀:定义默认的绕过缓存的 Cookie 前缀列表。
    •   绕过缓存的 URL 模式:定义需要绕过缓存的 URL 模式。
    •   事件监听器:监听 fetch 事件,并处理每个请求。
    •   主请求处理函数:处理每个请求,判断是否需要绕过缓存,并获取或缓存响应。
    •   绕过缓存判断:判断请求是否需要绕过缓存。
    •   配置检查:检查配置是否完整。
    •   上游缓存检查:检查请求头中是否包含上游缓存信息。
    •   获取缓存响应:获取缓存中的响应,如果没有缓存响应则返回空。
    •   缓存请求判断:判断请求是否应缓存。
    •   清理缓存头部信息:清理响应中的缓存头部信息。
    •   获取并缓存响应:获取并缓存响应。
    •   修改请求头部:修改请求头部以包含缓存支持信息。
    •   缓存响应判断:判断响应是否应缓存。
    •   缓存响应:将响应缓存到边缘缓存中。
    •   根据需要更新缓存:根据需要更新缓存内容。
    •   增强响应头部信息:增强响应头部信息,添加缓存状态和版本信息。
    •   获取当前缓存版本:获取当前的缓存版本号。
    •   生成缓存请求:生成缓存请求,附加缓存版本号。
    •   判断是否绕过边缘缓存:判断是否应绕过边缘缓存,根据 URL 模式和 Cookie 前缀。
    •   获取响应选项:获取响应中的选项信息,包括缓存和绕

The following is a pure code version without comments, which will be used when creating a worker later (it seems that there is a problem with the format after converting to HTML, and an error will be reported if it is copied directly to the worker. You can try it first (if there is a problem, you can directly let chatgpt optimize the format problem). I also provide my own download address here, you can directly download the txt file, the link is as follows:Unrivaled file sharing, access password: "blog.tangwudi.com"):

// IMPORTANT: Ensure that a Key/Value Namespace (EDGE_CACHE) is bound to this worker script.
// This KV store is recommended as it allows purging only HTML instead of the entire cache.

const CACHE_HEADERS = ['Cache-Control', 'Expires', 'Pragma'];

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

const BYPASS_URL_PATTERNS = [
    /\/wp-admin\/.*/, 
    /\/wp-login\/.*/, 
    /xmlrpc.*/, 
    /preview=true.*/, 
    /[?&]s=.*/
];

addEventListener("fetch", event => {
    event.passThroughOnException();
    event.respondWith(handleRequest(event));
});

async function handleRequest(event) {
    const { request } = event;

    // Check if the request should bypass the cache
    if (shouldBypassRequest(request)) {
        return fetch(request);
    }

    // Attempt to get the cached response
    let { response, cacheVer, status } = await getCachedResponse(request);

    if (!response) {
        response = await fetchAndCache(request, cacheVer, event);
    } else {
        status = 'HIT';
        event.waitUntil(updateCacheIfNeeded(request, cacheVer, event));
    }

    return enhanceResponse(response, status, cacheVer);
}

function shouldBypassRequest(request) {
    const accept = request.headers.get('Accept');
    const isImage = accept && accept.includes('image/*');

    return isImage || !isConfigured() || isUpstreamCachePresent(request);
}

function isConfigured() {
    return typeof EDGE_CACHE !== 'undefined'; // Ensure KV is configured
}

function isUpstreamCachePresent(request) {
    return request.headers.get('x-HTML-Edge-Cache') !== null;
}

async function getCachedResponse(request) {
    if (!shouldCacheRequest(request)) {
        return { response: null, cacheVer: null, status: 'Miss' };
    }

    const cacheVer = await getCurrentCacheVersion();
    const cacheKeyRequest = generateCacheRequest(request, cacheVer);
    const cache = caches.default;

    let response = await cache.match(cacheKeyRequest);
    if (response) {
        response = new Response(response.body, response);

        if (shouldBypassEdgeCache(request, response)) {
            return { response: null, cacheVer, status: 'Bypass Cookie' };
        }

        response = cleanupCacheHeaders(response);
        return { response, cacheVer, status: 'Hit' };
    }

    return { response: null, cacheVer, status: 'Miss' };
}

function shouldCacheRequest(request) {
    const accept = request.headers.get('Accept');
    return request.method === 'GET' && accept && accept.includes('text/html');
}

function cleanupCacheHeaders(response) {
    CACHE_HEADERS.forEach(header => {
        const value = response.headers.get(x-HTML-Edge-Cache-Header-${header}); if (value) { response.headers.set(header, value); response.headers.delete(x-HTML-Edge-Cache-Header-${header});
        }
    });
    return response;
}

async function fetchAndCache(request, cacheVer, event) {
    const response = await fetch(modifyRequest(request));
    if (shouldCacheResponse(request, response)) {
        const status = await cacheResponse(cacheVer, request, response, event);
        response.headers.set('x-HTML-Edge-Cache-Status', status);
    }
    return response;
}

function modifyRequest(request) {
    const newRequest = new Request(request);
    newRequest.headers.set('x-HTML-Edge-Cache', 'supports=cache|purgeall|bypass-cookies');
    return newRequest;
}

function shouldCacheResponse(request, response) {
    const accept = request.headers.get('Accept');
    return request.method === 'GET' && response.status === 200 && accept && accept.includes('text/html');
}

async function cacheResponse(cacheVer, request, response, event) {
    const cache = caches.default;
    const cacheKeyRequest = generateCacheRequest(request, cacheVer);
    const clonedResponse = new Response(response.body, response);

    CACHE_HEADERS.forEach(header => {
        const value = clonedResponse.headers.get(header);
        if (value) {
            clonedResponse.headers.set(x-HTML-Edge-Cache-Header-${header}, value);
            clonedResponse.headers.delete(header);
        }
    });

    clonedResponse.headers.set('Cache-Control', 'public; max-age=315360000'); // 10 years cache
    event.waitUntil(cache.put(cacheKeyRequest, clonedResponse));
    return "Cached";
}

async function updateCacheIfNeeded(request, cacheVer, event) {
    if (shouldCacheRequest(request)) {
        const response = await fetch(modifyRequest(request));
        if (shouldCacheResponse(request, response)) {
            await cacheResponse(cacheVer, request, response, event);
        }
    }
}

function enhanceResponse(response, status, cacheVer) {
    const newResponse = new Response(response.body, response);
    if (status) {
        newResponse.headers.set('x-HTML-Edge-Cache-Status', status);
    }
    if (cacheVer !== null) {
        newResponse.headers.set('x-HTML-Edge-Cache-Version', cacheVer.toString());
    }
    return newResponse;
}

async function getCurrentCacheVersion() {
    let cacheVer = 0;

    if (typeof EDGE_CACHE !== 'undefined') {
        cacheVer = await EDGE_CACHE.get('html_cache_version') || 0;
    } else {
        cacheVer = -1; // Fallback if KV isn't bound
    }

    return cacheVer;
}

function generateCacheRequest(request, cacheVer) {
    const url = new URL(request.url);
    url.searchParams.append('cf_edge_cache_ver', cacheVer);
    return new Request(url.toString());
}

function shouldBypassEdgeCache(request, response) {
    const url = new URL(request.url);
    if (BYPASS_URL_PATTERNS.some(pattern => pattern.test(url.pathname + url.search))) {
        return true;
    }

    const options = getResponseOptions(response);
    const bypassCookies = options ? options.bypassCookies : DEFAULT_BYPASS_COOKIES;
    const cookieHeader = request.headers.get('cookie');
    if (cookieHeader) {
        return bypassCookies.some(prefix => cookieHeader.split(';').some(cookie => cookie.trim().startsWith(prefix)));
    }
    return false;
}

function getResponseOptions(response) {
    const header = response.headers.get('x-HTML-Edge-Cache');
    if (!header) return null;

    const options = {
        purge: header.includes('purgeall'),
        cache: header.includes('cache'),
        bypassCookies: header.split(',')
            .filter(command => command.trim().startsWith('bypass-cookies'))
            .flatMap(command => command.split('=')[1].split('|').map(cookie => cookie.trim()))
    };

    return options;
}

CF Configuration Steps

Step 1: Create KV

As mentioned earlier, to use the worker to control the edge cache, the KV function is required, so the first step is to create a KV namespace. Follow the following process to create a KV space.

From "Workers and Pages" - "KV", click "Create Namespace":

image.png

Fill in the namespace name and click "Add":
image.png

Finish:
image.png

Step 2: Create a worker

From "Workers and Pages" - "Overview", click "Create":

image.png

Click "Create Worker":
image.png

Fill in the worker name and click "Deploy" in the lower right corner:
image.png

Then click "Edit Code" in the upper right red box:
image.png

Delete the default content in the red box on the left, then paste the previous uncommented version of the pure code into it, and finally click "Deploy" in the upper right corner:
image.png

Click "Save and Deploy":
image.png

Step 3: Bind the created worker to the KV space

From "Workers and Pages" - "Overview", click into the worker you just created:

image.png

In the upper left menu, in the "Settings" - "Variables" - "KV Namespace Binding" option, click "Add Binding":
image.png

Fill in the variable name:

EDGE_CACHE

Select the KV space created previously in the drop-down menu of the KV namespace, and then click "Deploy" in the lower right corner:
image.png

Step 4: Add worker routes

After creating KV and worker and completing the binding, you need to tell CF which traffic this worker and KV combination is used to handle. This requires using the worker's routing function (forwarding access traffic with a specific URL as the access target to a specific worker).

Note: Why is this feature called worker routing? Think about the definition of traditional routing: forwarding packets whose target IP belongs to a specific target network segment to a specific gateway, isn’t it similar?

In the upper left menu, in the "Settings" - "Trigger" - "Routing" option, click "Add Route":

image.png

image.png

Finish:
image.png


In addition, the worker has an option to customize the domain:

image.png

This feature allows you to bind a custom domain name (your hosted domain name on CF) to the worker, so that you can directly use your own domain name to access the worker script. It is usually suitable for using the worker to implement functions such as "xx proxy" and needs to be directly accessed.

Unlike worker routes and custom domains, the workers involved in worker routes do not need to be accessed directly. Instead, CF needs to transfer the corresponding access traffic to the worker for corresponding processing based on specific URL paths, query parameters and other conditions.

Why did I mention custom domains all of a sudden? Because although it is not used in this article, it is often encountered in other worker usage scenarios, and although the two options, worker routing and custom domains, have different uses, they have similarities: both provide control over how requests are processed, worker routing is based on path and conditional control, and worker custom domains are based on domain name control.


Step 5: Bypass the cache for requests that require worker processing

As I mentioned in the preface, in order for the worker to process the corresponding traffic, the prerequisite is that the traffic cannot be hit and processed by the rules of the node function module located before the worker in the traffic sequence. This article mainly refers to cache-related rules (of course, it also includes WAF, and there cannot be rules that block access requests). Therefore, to ensure that the target URL (in this article isblog.tangwudi.xyz/*) requests bypass the cache. The simplest way is to use page rules (page rules were briefly mentioned in the sixth part of the series tutorial when talking about Cache Rules, so I won’t go into details here). Configure it as follows:

image.png

image.png

At this point, the configuration on CF is complete.

Install the "Cloudflare Page Cache" plugin on WordPress

First download the plug-in, you can download it directly from the official address of github:GitHub official download addressIf you can't access GitHub, you can also download it from my blog's dedicated sharing site:Unrivaled file sharing(Access password: blog.tangwudi.com):

image.png

Then install and enable the plugin on WordPress without any settings:
image.png

image.png

Verify the cache effect

Verify that edge caching is effective

Reminder: Test without logging into WordPress, as the logged in state will bypass the cache.

Use Edge or Chrome browser, open the developer tools, select "Network", check "Disable cache", and then open the target URL, which is "blog.tangwudi.xyz" in this article:

image.png

The cache version number in the figure above can be viewed in the KV namespace corresponding to the CF console (when the content of the WordPress site changes, the plug-in will notify CF, and the edge cache version number will change):
image.png

image.png

Verify that the site page opening time is shortened

Check out the timings in the development tools for each of the three CF settings.
1. Worker-based beggar version of APO for WordPress
Waiting TTFB: 332.52 milliseconds:

image.png

2. Do not use any cache and return directly to the source
How long does it take if worker optimization is not used? To verify, turn on "development mode" on CF:
image.png

Revisithttps://blog.tangwudi.xyz:
image.png

Waiting TTFB: 2.74 seconds:
image.png

3. Use Cache Rules
So, how does worker caching compare to caching using Cache Rules?
Waiting TTFB: 1.14 seconds:
image.png

Direct return to the source without using cache: 2.74 seconds, using Cache Rules: 1.14 seconds, using the worker-based beggar version of APO for WordPress: 332.52 milliseconds. The beggar version of APO for WordPress wins, but Cache Rules' 1.14 seconds is much better than 2.74 seconds, so it can barely compete.

However, each person's source site location, back-to-source method, and broadband used are different, so the test results of the three methods are definitely different. In general, China Telecom and China Unicom are much better than China Mobile.

JS code for static sites

The code at the beginning of the article is only suitable for WordPress, and is not suitable for general static sites, so a code template suitable for static sites is also attached here.

JS code template suitable for most regular static sites

Mainly implement caching of HTML, JavaScript, CSS and images (excluding static sites that require login, especially those involving user sessions or personalized content):

const CACHE_HEADERS = ['Cache-Control', 'Expires', 'Pragma'];

const BYPASS_URL_PATTERNS = [
    /\/admin\/.*/,  // 绕过后台管理部分页面的缓存,可以根据需要修改或移除
];

addEventListener("fetch", event => {
    event.passThroughOnException();
    event.respondWith(handleRequest(event));
});

async function handleRequest(event) {
    const { request } = event;
    if (shouldBypassRequest(request)) {
        return fetch(request);
    }

    let { response, cacheVer, status } = await getCachedResponse(request);

    if (!response) {
        response = await fetchAndCache(request, cacheVer, event);
    } else {
        status = 'HIT';
        event.waitUntil(updateCacheIfNeeded(request, cacheVer, event));
    }

    return enhanceResponse(response, status, cacheVer);
}

function shouldBypassRequest(request) {
    const accept = request.headers.get('Accept');
    const isImage = accept && accept.includes('image/*');
    return isImage || isUpstreamCachePresent(request);
}

function isUpstreamCachePresent(request) {
    return request.headers.get('x-HTML-Edge-Cache') !== null;
}

async function getCachedResponse(request) {
    if (!shouldCacheRequest(request)) {
        return { response: null, cacheVer: null, status: 'Miss' };
    }

    const cacheVer = await getCurrentCacheVersion(null);
    const cacheKeyRequest = generateCacheRequest(request, cacheVer);
    const cache = caches.default;

    let response = await cache.match(cacheKeyRequest);
    if (response) {
        response = new Response(response.body, response);
        response = cleanupCacheHeaders(response);
        return { response, cacheVer, status: 'Hit' };
    }

    return { response: null, cacheVer, status: 'Miss' };
}

function shouldCacheRequest(request) {
    const accept = request.headers.get('Accept');
    return request.method === 'GET' && (
        accept.includes('text/html') ||
        accept.includes('application/javascript') ||
        accept.includes('text/css') ||
        accept.includes('image/*')
    );
}

function cleanupCacheHeaders(response) {
    CACHE_HEADERS.forEach(header => {
        const value = response.headers.get(x-HTML-Edge-Cache-Header-${header}); if (value) { response.headers.set(header, value); response.headers.delete(x-HTML-Edge-Cache-Header-${header});
        }
    });
    return response;
}

async function fetchAndCache(request, cacheVer, event) {
    const response = await fetch(modifyRequest(request));
    if (shouldCacheResponse(request, response)) {
        const status = await cacheResponse(cacheVer, request, response, event);
        response.headers.set('x-HTML-Edge-Cache-Status', status);
    }
    return response;
}

function modifyRequest(request) {
    const newRequest = new Request(request);
    newRequest.headers.set('x-HTML-Edge-Cache', 'supports=cache|purgeall');
    return newRequest;
}

function shouldCacheResponse(request, response) {
    const accept = request.headers.get('Accept');
    return request.method === 'GET' && response.status === 200 && (
        accept.includes('text/html') ||
        accept.includes('application/javascript') ||
        accept.includes('text/css') ||
        accept.includes('image/*')
    );
}

async function cacheResponse(cacheVer, request, response, event) {
    const cache = caches.default;
    const cacheKeyRequest = generateCacheRequest(request, cacheVer);
    const clonedResponse = new Response(response.body, response);

    CACHE_HEADERS.forEach(header => {
        const value = clonedResponse.headers.get(header);
        if (value) {
            clonedResponse.headers.set(x-HTML-Edge-Cache-Header-${header}, value);
            clonedResponse.headers.delete(header);
        }
    });

    clonedResponse.headers.set('Cache-Control', 'public; max-age=315360000');
    event.waitUntil(cache.put(cacheKeyRequest, clonedResponse));
    return "Cached";
}

async function updateCacheIfNeeded(request, cacheVer, event) {
    if (shouldCacheRequest(request)) {
        const response = await fetch(modifyRequest(request));
        if (shouldCacheResponse(request, response)) {
            await cacheResponse(cacheVer, request, response, event);
        }
    }
}

function enhanceResponse(response, status, cacheVer) {
    const newResponse = new Response(response.body, response);
    if (status) {
        newResponse.headers.set('x-HTML-Edge-Cache-Status', status);
    }
    if (cacheVer !== null) {
        newResponse.headers.set('x-HTML-Edge-Cache-Version', cacheVer.toString());
    }
    return newResponse;
}

async function getCurrentCacheVersion(cacheVer) {
    if (cacheVer === null) {
        cacheVer = -1;
    }
    return cacheVer;
}

function generateCacheRequest(request, cacheVer) {
    const url = new URL(request.url);
    url.searchParams.append('cf_edge_cache_ver', cacheVer);
    return new Request(url.toString());
}

JS code template suitable for static sites that require login, involve user sessions or personalized content

const CACHE_HEADERS = ['Cache-Control', 'Expires', 'Pragma'];

const BYPASS_URL_PATTERNS = [
    /\/admin\/.*/,
    /\/login\/.*/,
    /\/account\/.*/
];

const BYPASS_COOKIES = [
    'session_id',  // 假设使用这个Cookie来标识登录状态
    'auth_token'
];

addEventListener("fetch", event => {
    event.passThroughOnException();
    event.respondWith(handleRequest(event));
});

async function handleRequest(event) {
    const { request } = event;
    if (shouldBypassRequest(request)) {
        return fetch(request);
    }

    let { response, cacheVer, status } = await getCachedResponse(request);

    if (!response) {
        response = await fetchAndCache(request, cacheVer, event);
    } else {
        status = 'HIT';
        event.waitUntil(updateCacheIfNeeded(request, cacheVer, event));
    }

    return enhanceResponse(response, status, cacheVer);
}

function shouldBypassRequest(request) {
    const accept = request.headers.get('Accept');
    const isImage = accept && accept.includes('image/*');
    if (isImage || shouldBypassCookies(request)) {
        return true;
    }

    return BYPASS_URL_PATTERNS.some(pattern => pattern.test(request.url));
}

function shouldBypassCookies(request) {
    const cookieHeader = request.headers.get('Cookie');
    if (cookieHeader) {
        return BYPASS_COOKIES.some(cookie => cookieHeader.includes(cookie));
    }
    return false;
}

async function getCachedResponse(request) {
    if (!shouldCacheRequest(request)) {
        return { response: null, cacheVer: null, status: 'Miss' };
    }

    const cacheVer = await getCurrentCacheVersion(null);
    const cacheKeyRequest = generateCacheRequest(request, cacheVer);
    const cache = caches.default;

    let response = await cache.match(cacheKeyRequest);
    if (response) {
        response = new Response(response.body, response);
        response = cleanupCacheHeaders(response);
        return { response, cacheVer, status: 'Hit' };
    }

    return { response: null, cacheVer, status: 'Miss' };
}

function shouldCacheRequest(request) {
    const accept = request.headers.get('Accept');
    return request.method === 'GET' && (
        accept.includes('text/html') ||
        accept.includes('application/javascript') ||
        accept.includes('text/css') ||
        accept.includes('image/*')
    ) && !shouldBypassCookies(request);
}

function cleanupCacheHeaders(response) {
    CACHE_HEADERS.forEach(header => {
        const value = response.headers.get(x-HTML-Edge-Cache-Header-${header}); if (value) { response.headers.set(header, value); response.headers.delete(x-HTML-Edge-Cache-Header-${header});
        }
    });
    return response;
}

async function fetchAndCache(request, cacheVer, event) {
    const response = await fetch(modifyRequest(request));
    if (shouldCacheResponse(request, response)) {
        const status = await cacheResponse(cacheVer, request, response, event);
        response.headers.set('x-HTML-Edge-Cache-Status', status);
    }
    return response;
}

function modifyRequest(request) {
    const newRequest = new Request(request);
    newRequest.headers.set('x-HTML-Edge-Cache', 'supports=cache|purgeall');
    return newRequest;
}

function shouldCacheResponse(request, response) {
    const accept = request.headers.get('Accept');
    return request.method === 'GET' && response.status === 200 && (
        accept.includes('text/html') ||
        accept.includes('application/javascript') ||
        accept.includes('text/css') ||
        accept.includes('image/*')
    ) && !shouldBypassCookies(request);
}

async function cacheResponse(cacheVer, request, response, event) {
    const cache = caches.default;
    const cacheKeyRequest = generateCacheRequest(request, cacheVer);
    const clonedResponse = new Response(response.body, response);

    CACHE_HEADERS.forEach(header => {
        const value = clonedResponse.headers.get(header);
        if (value) {
            clonedResponse.headers.set(x-HTML-Edge-Cache-Header-${header}, value);
            clonedResponse.headers.delete(header);
        }
    });

    clonedResponse.headers.set('Cache-Control', 'public; max-age=315360000');
    event.waitUntil(cache.put(cacheKeyRequest, clonedResponse));
    return "Cached";
}

async function updateCacheIfNeeded(request, cacheVer, event) {
    if (shouldCacheRequest(request)) {
        const response = await fetch(modifyRequest(request));
        if (shouldCacheResponse(request, response)) {
            await cacheResponse(cacheVer, request, response, event);
        }
    }
}

function enhanceResponse(response, status, cacheVer) {
    const newResponse = new Response(response.body, response);
    if (status) {
        newResponse.headers.set('x-HTML-Edge-Cache-Status', status);
    }
    if (cacheVer !== null) {
        newResponse.headers.set('x-HTML-Edge-Cache-Version', cacheVer.toString());
    }
    return newResponse;
}

async function getCurrentCacheVersion(cacheVer) {
    if (cacheVer === null) {
        cacheVer = -1;
    }
    return cacheVer;
}

function generateCacheRequest(request, cacheVer) {
    const url = new URL(request.url);
    url.searchParams.append('cf_edge_cache_ver', cacheVer);
    return new Request(url.toString());
}

关键点解释:

    1.  BYPASS_COOKIES:定义了一组会话或身份验证相关的Cookie,如session_id或auth_token,如果请求中包含这些Cookie,则不进行缓存。
    2.  shouldBypassCookies:检查请求是否包含这些会话Cookie。如果包含,则跳过缓存,并直接从源服务器获取内容。
    3.  公共资源的缓存:对于不涉及用户个性化或会话的资源(如CSS、JS、图片),仍然可以进行缓存,以提升站点性能。

Notes on caching static sites using worker mode

For general static sites, there is no need to install a plug-in like WordPress to support x-HTML-Edge-Cache. As long as the file structure and path design of the site are reasonable, the cache strategy can be handled directly in the worker script. However, the following points need to be noted:

  1. URL path design: Make sure the URL path of static resources (HTML, CSS, JavaScript, images, etc.) is fixed and does not depend on dynamic parameters. This ensures a higher cache hit rate.

  2. Response header settings:Although you don't need to install a plugin like WordPress, on the server side, you can consider setting appropriate cache control headers (such as Cache-Control) to match the worker's caching strategy. If the static site uses a server (such as NGINX, Apache), you can manually configure these headers.

  3. Clear the cache: When static resources are updated, you can clean the cache through the API provided by Worker or the CF panel to ensure that users get the latest content (it is still not as convenient as using WordPress plug-ins to automatically update).

  4. Features that do not require special support: For static sites that do not rely on user sessions or personalized content, the basic caching logic of the worker script is sufficient and there is no need to install additional plugins to handle specific cache headers like in WordPress.

Summarize

The beggar version of APO for WordPress implemented by using worker, KV and Edge Cache HTML script can be said to be able to compete, but compared with the genuine APO function, there is a certain gap in access speed, compatibility, performance, etc. However, for friends who use the Free plan, it is also a blessing.

However, the biggest limitation of this method is that the worker in the Free plan has a free quota of 100,000 requests per day. This quota is actually enough for ordinary personal webmasters and can never be used up. However, the most feared thing is DDos attack. Once it happens, the 100,000 free requests will be used up in a short time, and then you can only directly return to the source (2.74 seconds). APO has no limit.

Of course, there is also an emergency solution: keep the relevant configuration of regular Cache Rules (see article:Home Data Center Series CloudFlare Tutorial (VI) CF Cache Rules Function Introduction and Detailed Configuration Tutorial), usually use page rules to bypass the cache (because page rules have a higher priority than Cache Rules in the traffic sequence, so as long as the page rules are hit to bypass the cache, the subsequent Cache Rules will not be hit). When the worker's free quota is consumed due to a DDos attack, just manually turn off the page rule switch, and the access traffic will hit the subsequent Cache Rules (Cache Rules have a higher priority than workers in the traffic sequence, 1.14 seconds, which is always better than 2.74 seconds of directly returning to the source), and wait until the next day, turn on the page rules again and you will be a hero again.

Another 1: Don’t think that DDos is far away from you. I was attacked again yesterday, although I didn’t feel anything:
image.png

Another 2: From this article, you should be able to see the importance of network basics, right? If the network basics are not solid, it is often impossible to analyze the problem in depth, let alone troubleshooting some strange problems. However, how many friends who are engaged in network-related work now still read the "TCP/IP Detailed Explanation Trilogy"?

Another 3: I am so tired of writing this article. I don't want to write academic articles involving so many technical points and details in the short term. I will just write some casual articles and take a break. Fortunately, this article about workers seems to be the most troublesome one in the CF series of tutorials. I won't have to worry about it in the future.

The content of the blog is original. Please indicate the source when reprinting! For more blog articles, you can go toSitemapUnderstand. The RSS address of the blog is:https://blog.tangwudi.com/feed, welcome to subscribe; if necessary, you can joinTelegram GroupDiscuss the problem together.

Comments

  1. Windows Chrome 128.0.0.0
    4 months ago
    2024-10-07 18:21:15

    ai prompts that there is a grammatical error. Correct writing method:
    x-HTML-Edge-Cache-Header-Put {header} into backticks (") and use{} refers to template variables.

    • Owner
      No name left
      Macintosh Chrome 129.0.0.0
      4 months ago
      2024-10-07 20:26:54

      This code has been optimized by chatgpt before. It seems that its ideas change every once in a while. This code is definitely working fine, I have verified it, but you can also let AI optimize it again, and finally make sure it works properly. With AI, you can optimize the code as you like, as long as it works, or you can directly use the official script on GitHub as a blueprint for AI to optimize.

  2. knbn
    Linux Edge 129.0.0.0
    4 months ago
    2024-10-04 9:07:32

    Hey, there is something wrong with your CF worker script. If you copy and paste it, there will be a lot of errors, and CF will not save it.

    • Owner
      knbn
      Macintosh Chrome 129.0.0.0
      4 months ago
      2024-10-04 9:12:01

      No, I have verified it. The test results in the article are all based on scripts.

    • Owner
      knbn
      Macintosh Chrome 129.0.0.0
      4 months ago
      2024-10-04 9:24:17

      It may be a format problem. There is no problem when I copy my local code directly to the worker, but the code on the website will give an error when I copy it. I will adjust it.

    • Owner
      knbn
      Macintosh Chrome 129.0.0.0
      4 months ago
      2024-10-04 9:54:05

      I temporarily provided the download address of the code in the article, and put the txt files of the wordpress version and html version of the code (also put it in the comments: the link is as follows:Unrivaled file sharing, access password: "blog.tangwudi.com"), you can download it directly, and I will study the format issue when I have time.

  3. Windows Chrome 118.0.0.0
    5 months ago
    2024-8-28 10:38:07

    The worker is on the edge node, and the cache is also on the edge node. Why is the worker faster than the cache when used as a cache?

    • Owner
      ere
      Macintosh Chrome 128.0.0.0
      5 months ago
      2024-9-09 6:12:19

      It's so troublesome now, I can't receive email notifications for comments~~I just saw it today. I said in the article "Why using worker to control edge cache can achieve better access effect than CF regular CDN" that the worker function has some privileges, breaking through some limitations of regular cache, and can get some privileges of paid function APO for free.

Send Comment Edit Comment


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