Reverse-Engineering the Cloudflare Pages Deployment API

I was recently exploring options on how to deploy a WordPress site as a static site when I found this article. The official documentation for Cloudflare Pages suggests that users export a static snapshot of their WordPress site with a plugin, and manually upload that (.zip) file in the Cloudflare dashboard…

So I started reading the source code of the Cloudflare Pages CLI Wrangler to see how “hackable” the deployment API was… and I found that it’s entirely possible to do deployments outside of the CLI tool. I prototyped my own deployment tool and created this high-level overview of the workflow:

  1. Get a Cloudflare Account ID, API Token & Pages Project Name (can be created/found in the Cloudflare dashboard)
  2. Create a list of all routes that the site will “serve”. Each item should have the following properties:
    • path – The pathname of the file (include /index.html for pages)
    • contentType – The mime type of the file
    • body – A base64 encoded string of the file contents
    • hash – A hash of the (body + path) from above using a hashing algorithm (I used MD5, Cloudflare uses blake3)
    • length – The length of the file in bytes (before base64 encoding)
  3. Create an upload JWT:
    • GET - v4/accounts/$accountId/pages/projects/$projectName/upload-token
    • Include the API token in the header as described here.
    • This token should be valid for 300 seconds (unless they change that expiration)
  4. Upload “buckets” with up to 50mb of files:
    • POST - v4/pages/assets/upload
    • Use the upload token generated from the previous step (get a new token if it expired)
    • The body will be a JSON array with each item having the following properties:
      • key – File hash
      • value – Base64 file contents
      • metadata – Object with 1 property: contentType – which will be the mime type
      • base64true
  5. Tell Cloudflare that new files were uploaded:
    • POST - v4/pages/assets/upsert-hashes
    • Use the upload token from before
    • The body is a JSON object with 1 property: hashes which is a string array with all of the new file hashes that were uploaded
  6. Create a deployment:
    • POST - v4/accounts/$accountId/pages/projects/$projectName/deployments
    • Use the regular API token here and upload the body as multipart/form-data
    • The body is form data with 1 property: manifest – a JSON object with the following properties:
      • [path] – File hash
    • This part is the most finicky, the server returns a 500 if the proper content disposition & boundaries aren’t set.

That’s it 😅.

There are a few other things that I glossed over like:

  • Checking for hashes that are already uploaded
  • Uploading custom redirects, headers, routes & Worker Functions
  • Creating ‘branch’ deployments to preview changes

But this is would be a good starting point for any developer looking to integrate their project with Cloudflare Pages in a non-normal way. There are a lot of cool possibilities for integration here, like ISR (where only the files that changed are uploaded) & exposing an API layer with Worker Functions, etc. I ended up creating a WordPress plugin that automates this process as well as statically rendering the site (more on this in a later post).