Cloudflare maintenance page using Cloudflare Workers and Gitlab-ci
Description:
The objective is to be able to put a Cloudflare managed domain into maintenance mode in order to safely perform updates by avoiding client requests using Cloudflare Workers technology combined with Gitlab-ci.
Implementation Options:
- First Option: use Wrangler (Workers Cli) to automatically manage the entire process.
- Advantage: pro solution, not susceptible to errors due to human interventions.
- Disvantage: requires more resources on Gitlab-ci and takes longer to run.
- Second Option: configure (manually or not) the requirements and then handle the process through simple API calls.
- Advantage: quick and simple solution, almost no resources needed on Gitlab-ci.
- Disvantage: requires manual configuration and is susceptible to errors due to human interventions.
First Option – How To:
- Project Structure:
.
|-- .gitlab-ci.yml
|-- README.md
|-- wrangler.toml # Wrangler configuration.
|-- src/
| |-- worker.js # Maintenance Page script generation.
- .gitlab-ci.yml:
# Cloudflare Maintenance Page using Cloudflare Workers Example.
# List of stages for jobs, and their order of execution
stages:
- build
# This job could be splitted into Maintenance On / Off
# 1. Installs Wrangler (Workers CLI)
# 2. Creates a new Worker called "cf-worker"
# 3. Deploy the Worker using the src/worker.js page template
# 4. Pause in order to manually check if site is on Maintenance Mode
# 5. Delete the Worker returning site to Normal Mode
build-job:
image: node:18-alpine
stage: build
script:
- npm install -g wrangler
- npm create cloudflare@2 -- cf-worker
- wrangler deploy src/worker.js
- sleep 30
- wrangler delete src/worker.js
- wrangler.toml:
name = "cf-worker"
compatibility_date = "2023-08-29"
workers_dev = false
route = { pattern = "example.com/*", zone_name = "example.com" }
- src/worker.js:
export default {
async fetch(request) {
const html = `<!DOCTYPE html>
<body>
<h1>Site on Maintenance!</h1>
<p>This markup was generated by a Cloudflare Worker.</p>
</body>`;
return new Response(html, {
headers: {
"content-type": "text/html;charset=UTF-8",
},
});
},
};
Second Option – How To:
- Access Cloudflare Account and get Zone ID of the selected domain.
- Create an API Token for the selected domain with the required permissions to handle Workers.
- Account: Cloudflare Pages: Edit
- Account: Workers RD2 Storage: Edit
- Account: Workers Tail: Read
- Account: Workers KV Storage: Edit
- Account: Workers Scripts: Edit
- Account: Account Settings: Read
- User: Memberships: Read
- User: User Details: Read
- Zone: Workers Routes: Edit
- Account Resources: Include: Account
- Zone Resources: Include: Specific Zone: example.com
- Create a maintenance subdomain for the selected domain (mp.example.com).
- Create the Worker with the code to generate the maintenance page.
- Add a new route associated with the created worker using the maintenance subdomain as pattern.
- Get the Route ID of that route and save it to a variable.
Getting Zone ID:
curl --url https://api.cloudflare.com/client/v4/zones/"$ZONE_ID"/workers/routes \ --header "Authorization: Bearer $API_TOKEN"
Use an API call to modify that route and put the main domain instead on maintenance mode:
# Maintenance Mode On
curl --request PUT \
--url https://api.cloudflare.com/client/v4/zones/"$ZONE_ID"/workers/routes/"$ROUTE_ID" \
--header "Authorization: Bearer $API_TOKEN" \
--header "Content-Type: application/json" \
--data '{"pattern":"example.com/*","script":"cf-worker"}'
Use an API call to modify that route and put back the subdomain on maintenance mode:
# Maintenance Mode Off
curl --request PUT \
--url https://api.cloudflare.com/client/v4/zones/"$ZONE_ID"/workers/routes/"$ROUTE_ID" \
--header "Authorization: Bearer $API_TOKEN" \
--header "Content-Type: application/json" \
--data '{"pattern":"mp.example.com/*","script":"cf-worker"}'
References:

