Cloudflare Pages lets you deploy static sites and Single Page Applications (SPAs) with zero configuration, but it’s actually a distributed build system that pushes your code to the edge before anyone even requests a page.
Let’s see it in action. Imagine you have a simple React app. You push it to a GitHub repository. Cloudflare Pages hooks into that repo, sees the package.json, and knows it needs to run npm run build.
# Example .gitignore
node_modules/
build/
dist/
.env
// Example package.json
{
"name": "my-react-app",
"version": "0.1.0",
"private": true,
"dependencies": {
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-scripts": "5.0.1"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
]
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
}
When you connect your GitHub repo to Cloudflare Pages, you’ll set up a build configuration. For a React app, it’s typically:
- Build command:
npm run build - Build output directory:
build(ordistfor some frameworks) - Environment variables: If your app needs API keys or other secrets, you can set them here. Cloudflare Pages injects these as environment variables during the build process.
Once the build command finishes successfully, Cloudflare Pages takes the contents of your specified output directory and deploys them to its global network of edge servers. This means that when a user requests your site, they are served directly from the edge location closest to them, resulting in incredibly fast load times.
The real magic for SPAs is how Cloudflare Pages handles routing. Standard web servers typically serve index.html only for requests to the root path (/). However, SPAs rely on client-side routing, meaning that requests to paths like /about or /users/123 should also serve the index.html file, allowing the JavaScript framework to handle the routing.
Cloudflare Pages achieves this with a simple configuration file, _redirects, placed in your project’s public directory or the root of your build output.
# Example _redirects file
/* /index.html 200
This single line tells Cloudflare Pages: "For any request path (/*), serve the index.html file, and return a 200 OK status code." This is crucial because it ensures that even if a user directly bookmarks or shares a deep link within your SPA, the index.html is served, and your JavaScript can then correctly render the requested page. Without this, deep links would result in 404 errors.
The build process itself is distributed. Cloudflare runs your build command on their infrastructure, not on your local machine or a separate CI server. This means you don’t need to manage build agents or worry about their dependencies. Cloudflare handles the environment, installing Node.js, your project’s dependencies, and executing your build script.
For more complex projects, you might need to specify a custom Node.js version or install additional system dependencies. This is done via a package.json or a wrangler.toml (if you’re using Workers alongside Pages). For example, to use Node.js 18:
// Example package.json snippet for Node version
"engines": {
"node": "18.x"
}
Cloudflare Pages integrates seamlessly with Git. When you push changes to your connected branch (e.g., main or master), Pages automatically triggers a new build and deployment. You can also configure it to deploy specific branches to different subdomains or custom domains, allowing for staging environments or feature branch previews.
The system is designed to be highly available and performant. Your static assets are cached aggressively at the edge, and the _redirects rule ensures that your SPA’s client-side routing works flawlessly, even for direct URL access. You can also leverage Cloudflare Workers to add dynamic functionality to your static sites, like form handling, API integrations, or authentication, all without needing a traditional backend server.
What most people overlook is the power of the _redirects file for more than just SPA routing. You can use it for simple redirects (e.g., old-page.html /new-page.html 301) or even rewrite URLs to serve content from different locations within your build output, enabling complex routing logic directly at the edge before any requests even hit your application’s JavaScript.