Deploy a Laravel Jigsaw Site to Cloudflare Pages
Slow Poke Rodríguez • July 11, 2021
Updated July 2, 2026
I've tried a few different static site generators and I really like Laravel Jigsaw. It's not a huge leap for someone who has already been working with PHP and Laravel more specifically.
I started this post in 2021 and never finished it. Things have changed quite a bit! Back then you connected your GitHub repo to Cloudflare Pages, set PHP_VERSION=7.4 in the project variables, gave it a build command, and Cloudflare built the site for you on every push.
That doesn't work anymore. Jigsaw builds your site with PHP, and modern Jigsaw needs a modern PHP — version 1.8 pulls in Symfony components that require PHP 8.4 or newer. Cloudflare's build environment topped out at PHP 7.4 in its v1 build image, and the current build images dropped PHP entirely. There is no value you can put in PHP_VERSION that fixes this. You can't build it at Cloudflare now.
But you can still build the site in GitHub Actions, where you can have any PHP version you want, then push the finished files to Cloudflare Pages with wrangler. You still get the ease and freebie-ness of Cloudflare pages though!
What you'll set up
- A Cloudflare API token so GitHub can deploy for you
- Two secrets in your GitHub repo
- One workflow file
- A couple of settings on the Pages project
1. Create the API token
Go to dash.cloudflare.com/profile/api-tokens (under your profile, not any specific project) and create a custom token with a single permission:
Account → Cloudflare Pages → Edit
Copy the token immediately — Cloudflare only shows it once. While you're in the dashboard, also grab your Account ID.
2. Add the GitHub secrets
In your repo: Settings → Secrets and variables → Actions, add:
CLOUDFLARE_API_TOKEN— the token you just createdCLOUDFLARE_ACCOUNT_ID— your account ID
3. The workflow
Create .github/workflows/deploy.yml:
name: Build and Deploy to Cloudflare Pages
on:
push:
branches: [master, main]
pull_request:
branches: [master, main]
jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v6
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: '8.5'
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version-file: '.nvmrc'
- name: Cache Composer dependencies
uses: actions/cache@v5
with:
path: vendor
key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
- name: Cache NPM dependencies
uses: actions/cache@v5
with:
path: node_modules
key: ${{ runner.os }}-npm-${{ hashFiles('**/package-lock.json') }}
- name: Install PHP dependencies
run: composer install --no-interaction --prefer-dist --optimize-autoloader
- name: Install NPM dependencies
run: npm ci
- name: Build site
run: npm run build
- name: Deploy to Cloudflare Pages
uses: cloudflare/wrangler-action@v4
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
gitHubToken: ${{ secrets.GITHUB_TOKEN }}
command: pages deploy build_production --project-name=YOUR-PROJECT-NAME --branch=${{ github.head_ref || github.ref_name }}
Swap in your own Pages project name. On the current Jigsaw blog template, npm run build runs Vite, and Vite's Jigsaw plugin runs the production site build — one command does both. If you're on the older Laravel Mix template, you'll want ./vendor/bin/jigsaw build production followed by npm run prod instead.
The --branch can be useful: it pushes to your production branch deploy to your real domain, and pull request builds land on preview URLs like my-branch.my-project.pages.dev, so you can look at a change before it goes live.
4. Settings on the Pages project
If your Pages project is brand new, it's less troubl than what I ran into upgrading this site. If it's an existing project that was connected to your git repo (like mine, set up in the golden age of 2021), two things:
- Disable automatic deployments — Branch control → edit → turn off automatic deployments. Otherwise Cloudflare launches its own build on every push, right next to your working one. Failed builds never publish, so it's harmless, but the red X's pile up.
- Delete
PHP_VERSIONfrom the project variables if it's still there.
You do not need to disconnect the repo — a git-connected project will accept wrangler deploys.
Possible problem: your 404 page might not work
This one was live on my site for years without me noticing. Jigsaw's pretty URLs build your 404 page as 404/index.html, but Cloudflare Pages only recognizes a root-level 404.html — without it, Pages serves your homepage with a 200 status for every URL that doesn't exist. Fix it with front matter:
---
permalink: 404.html
---
at the top of source/404.blade.php, and unknown URLs start returning honest 404s.
That's it. Push to master, GitHub builds it in about a minute, and Cloudflare serves it. Only took me five years to finish writing it.