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: