iTranslated by AI
Switching Cloudflare Settings Based on Time
I received a request to change Cloudflare's Custom Pages (WAF block pages) depending on the time of day.
My initial ideas were:
- Terraform or Pulumi
- Workers Cron Triggers
I will try option 2.
Preparation
Deciding the Cloudflare Configuration Strategy
Custom Pages (WAF) settings can be configured at either the Account level or the Zone level.
I will use the Zone level.
WAF Configuration
In the WAF rule actions, I will configure it to use Custom Pages when blocking.

Custom Pages Configuration

Looking at the Help documentation from the dashboard, the behavior is to fetch the block page materials from the specified URL. Therefore, if you change the content at the same URL, a re-fetch is required.
Page Switching Strategy
I will switch the block pages at 0 minutes and 30 minutes past every hour.
The following two methods seem feasible, and I will proceed with method 1:
- Specify different URLs
- Specify the same URL and replace the content by time
Confirming the Cloudflare API Endpoint
Finding the API Endpoint
The strategy is decided.
Next, I need to know the API to change Custom Pages (WAF).
Which API endpoint to use is displayed when you click API on the dashboard.

Clicking Full API documentation will take you to the Cloudflare API, where you can easily reach the endpoint documentation.
You can also find it directly on the Cloudflare API page by using the search bar.
You can find Custom pages for a zone.

Knowing the API Endpoint
I will read the documentation for the reached endpoint.

Since I am trying to create a request, I will focus on the Request section on the page.
I need to prepare the Required items in Path Parameters and Body in advance.

However, there are parts where I don't know exactly what to enter.
What are identifier and state?
Trying out the API Endpoint and Digging Deeper
If the documentation lacks details or you want to know specific content, it is a good idea to put a temporary setting in the dashboard once and check the return value via API.

In this case, since I don't know what to put in the path identifier, I will GET from the List endpoint.

It seems I should set identifier to waf_block and state to customized.
Creating a Token for the Cloudflare API Endpoint
I will create a Token with permissions to operate the found API endpoint.
Select Custom Token.

Grant Edit permissions for Custom Pages.

Copy the created Token.

Workers Implementation
Now, let's build it.
Creating the Project
npm create cloudflare@latest
The type is Scheduled Worker (Cron Trigger).

output
using create-cloudflare version 2.22.0
╭ Create an application with Cloudflare Step 1 of 3
│
├ In which directory do you want to create your application?
│ dir ./cf-config-test
│
├ What type of application do you want to create?
│ type Scheduled Worker (Cron Trigger)
│
├ Do you want to use TypeScript?
│ no typescript
│
├ Copying template files
│ files copied to project directory
│
├ Updating name in package.json
│ updated package.json
│
├ Installing dependencies
│ installed via npm install
│
╰ Application created
╭ Configuring your application for Cloudflare Step 2 of 3
│
├ Retrieving current workerd compatibility date
│ compatibility date 2024-07-12
│
├ Do you want to use git for version control?
│ no git
│
╰ Application configured
╭ Deploy with Cloudflare Step 3 of 3
│
├ Do you want to deploy your application?
│ no deploy via npm run deploy
│
├ APPLICATION CREATED Deploy your application with npm run deploy
│
│ Navigate to the new directory cd cf-config-test
│ Run the development server npm run start
│ Deploy your application npm run deploy
│ Read the documentation https://developers.cloudflare.com/workers
│ Stuck? Join us at https://discord.cloudflare.com
│
╰ See you again soon!
Preparing Variables
Since I will hit the Cloudflare API from Cron Triggers, I will register sensitive information as secrets so they can be called as variables.
- Authentication token
- Zone ID
I will target these.
cd cf-config-test/
npx wrangler secret put CUSTOM_PAGE_RW
npx wrangler secret put ZONE_ID
output
⛅️ wrangler 3.64.0
✔ Select an account › 0_Example
✔ Enter a secret value: … ****************************************
🌀 Creating the secret for the Worker "cf-config-test"
✨ Success! Uploaded secret CUSTOM_PAGE_RW
wrangler toml
I will configure the trigger times to fire at 0 and 30 minutes past every hour using triggers.
name = "cf-config-test"
main = "src/index.js"
compatibility_date = "2024-07-12"
[triggers]
crons = ["0 * * * *", "30 * * * *"]
src/index.js
I will specify different block pages for the conditions specified in triggers.
Since I have multiple Cron Triggers specified, I referred to this.
export default {
async scheduled(event, env, ctx) {
const apiToken = env.CUSTOM_PAGE_RW;
const zoneApi = "https://api.cloudflare.com/client/v4/zones/";
const zoneId = env.ZONE_ID;
const pageId = "waf_block";
const apiEp = zoneApi + zoneId + "/custom_pages/" + pageId;
async function callApi(blockpage) {
const body = {
url: blockpage,
state: "customized"
};
const init = {
body: JSON.stringify(body),
method: "PUT",
headers: {
"content-type": "application/json",
"authorization": "Bearer " + apiToken
}
};
try {
const response = await fetch(apiEp, init);
console.log(response.status);
if (!response.ok) {
const errorJson = await response.json();
console.log(errorJson);
}
return response.status;
} catch (error) {
console.error(`error: ${error}`);
}
}
switch (event.cron) {
case "0 * * * *":
ctx.waitUntil(callApi("https://block.oymk.work/first.html/"));
break;
case "30 * * * *":
ctx.waitUntil(callApi("https://block.oymk.work/second.html"));
break;
}
console.log("cron processed");
},
};
Deployment
Deploy the project.
npm run deploy
output
⛅️ wrangler 3.64.0
Total Upload: 1.31 KiB / gzip: 0.61 KiB
Uploaded cf-config-test (2.03 sec)
Published cf-config-test (0.93 sec)
schedule: 0 * * * *
schedule: 30 * * * *
Current Deployment ID:
Current Version ID:
Local testing is described here.
Results
I was able to confirm that it switches according to the time.

Troubleshooting
Initially, the settings were not being applied correctly.
I thought about logging to isolate the issue.
Looking at the API documentation, the Body is also JSON when a 4xx error occurs.

I inserted console.log() along the way to check the operation.
npx wrangler tail
It was failing because the block page Body was less than 100 characters.
Failed 4xx
(log) cron processed
(log) 400
(log) {
result: null,
success: false,
errors: [
{
code: 1203,
message: 'Your custom page must be larger than 100 characters.'
}
],
messages: []
}
Once the cause is known, it can be resolved.
Successful 200
"30 * * * *" @ 7/16/2024, 9:30:45 PM - Ok
(log) cron processed
(log) 200
That's all.
Cron Triggers can likely be used to change settings according to time for other configuration items as well.
Discussion