Cloudflare is where I deploy everything. Pages for static and full-stack apps, Workers for server-side logic at the edge, D1 for SQLite databases, R2 for object storage. It is a cohesive platform that lets you ship fast without managing infrastructure. This guide covers the full deployment workflow from account setup to production monitoring.
ACCOUNT SETUP AND THE WRANGLER CLI
Start by creating a free account at dash.cloudflare.com. The free tier is genuinely useful — you get 100,000 Worker requests per day, unlimited Pages deployments, and 10 GB of R2 storage. That is enough to ship real projects before you ever need to pay.
Once your account is created, install Wrangler, Cloudflare's official CLI. You can install it globally with npm install -g wrangler, or pin it as a dev dependency in package.json at wrangler version 3 or later. Pinning it to your project keeps your team on the same version and avoids surprises from global upgrades.
Authenticate by running wrangler login, which opens a browser OAuth flow and stores a token locally. Confirm it worked with wrangler whoami, which prints your account name and email.
DEPLOYING TO CLOUDFLARE PAGES
Pages is the right home for React, Remix, Next.js, SvelteKit, Astro, or any static site. You get preview deployments on every branch, automatic HTTPS, and a global CDN out of the box.
The easiest setup is through git integration. In the dashboard, go to Workers and Pages, click Create application, then Pages, then Connect to Git. Authorize GitHub or GitLab, select your repository, and configure your build command (usually npm run build) and build output directory (usually dist, build, or .next depending on your framework). Cloudflare auto-detects most frameworks and pre-fills these values. Every push to any branch gets a unique preview URL, and merges to main deploy to production automatically.
If you prefer a CI pipeline or do not want git integration, you can deploy directly with Wrangler. After running your build locally, use wrangler pages deploy ./dist with the --project-name flag set to your app name. The first run creates the project; subsequent runs are fast because Cloudflare diffs your assets and only uploads what changed.
For full-stack apps, Pages Functions let you add server-side logic without a separate Worker. You create a functions directory at your project root, and any TypeScript file inside it becomes a route handler. A file at functions/api/hello.ts handles requests to /api/hello. You can use dynamic path segments, access D1 database bindings, and return standard Response objects. This is a clean way to colocate your frontend and backend without operating two separate services.
CLOUDFLARE WORKERS
Workers run JavaScript at Cloudflare's edge across over 300 locations worldwide. Cold start time is effectively zero because Workers use V8 isolates rather than containers. You pay for CPU time, not idle time.
Scaffold a new Worker project using npm create cloudflare@latest with the worker type flag. This generates a wrangler.toml configuration file and a TypeScript entry point. The most important field in wrangler.toml is compatibility_date, which you should pin to the date you created the project. This controls which platform APIs are available and protects you from unexpected breaking changes when Cloudflare updates the runtime.
Your Worker's entry point exports a default object with a fetch method. That method receives the incoming Request, your Env bindings, and an ExecutionContext. You return a Response. Routing is done manually — check the URL pathname and dispatch to handler functions. For anything beyond a few routes, consider a lightweight router library.
Run wrangler dev to start a local development server at localhost:8787. It uses the actual V8 isolate runtime, so behavior matches production closely. D1, KV, and R2 bindings proxy to your account resources, or you can use the --local flag for fully offline emulations.
Deploy to production with wrangler deploy. Your Worker is live at your workers.dev subdomain within seconds. To route a Worker to a custom domain path, add a routes section to wrangler.toml with a pattern like api.example.com/* and your zone name.
ENVIRONMENT VARIABLES AND SECRETS
Non-sensitive configuration values go in the vars section of wrangler.toml as key-value pairs. These are accessible via the env argument in your Worker. Values like API_BASE_URL or ENVIRONMENT belong here.
Secrets like API keys and database credentials must never go in wrangler.toml because that file gets committed to version control. Instead, use wrangler secret put followed by the variable name. Wrangler will prompt you interactively for the value. In CI environments like GitHub Actions, you can pipe the secret value directly from an environment variable using echo into wrangler secret put. Secrets are encrypted at rest and only decrypted inside the Worker at runtime — they are never exposed in logs or the dashboard.
After adding or changing bindings, run wrangler types to regenerate your worker-configuration.d.ts file. This gives you a fully typed Env interface with autocomplete for every binding in your editor.
D1 DATABASES
D1 is Cloudflare's managed SQLite. It is serverless, scales to zero, uses standard SQL, and integrates directly with Workers and Pages Functions through bindings. It is my default choice for relational data when I do not want to operate a Postgres instance.
Create a database with wrangler d1 create followed by your database name. Wrangler prints the binding configuration to paste into your wrangler.toml, including the database ID. You set the binding name to something like DB, and that name becomes how you access it in your Worker via env.DB.
Run migrations with wrangler d1 execute. Pass the --local flag to run against the local emulation used by wrangler dev, and omit it to run against your production database. Keep migration files as plain SQL files in a migrations directory and apply them in order.
Querying uses a prepared statement API. Call env.DB.prepare with your SQL string, chain .bind to pass parameters, then call .first for a single row, .all for multiple rows, or .run for writes. Always use parameterized queries. Never interpolate user input directly into SQL strings.
R2 OBJECT STORAGE
R2 is S3-compatible object storage with zero egress fees. That last part matters — at scale, egress fees on AWS S3 can easily become your largest infrastructure cost. R2 eliminates that entirely.
Create a bucket with wrangler r2 bucket create and add the binding to your wrangler.toml under the r2_buckets section. Set the binding name to something like BUCKET and you access it via env.BUCKET in your Worker.
Uploads use env.BUCKET.put with the object key, the body (which can be a ReadableStream, ArrayBuffer, or string), and optional HTTP metadata like content type. Downloads use env.BUCKET.get, which returns null if the object does not exist. Check for null before streaming the response body. Set Cache-Control headers on R2 responses to leverage Cloudflare's CDN caching in front of your bucket.
For assets you want publicly accessible without going through a Worker on every request, enable public access in the R2 dashboard. Cloudflare will serve them directly from the CDN using your bucket's public URL.
CUSTOM DOMAINS AND SSL
Cloudflare handles SSL automatically. You never need to provision or renew certificates.
For Pages projects, go to Settings, then Custom domains, and click Set up a custom domain. Enter your domain and add the CNAME record shown. If your DNS is already managed on Cloudflare, the record is added automatically.
For Workers, you have two options. The first is route-based routing where you add a routes section to wrangler.toml with a pattern like api.example.com/* and the zone_name for your domain. The second is through the dashboard under Workers and Pages, your worker's Settings, then Domains and Routes, where you can add a custom domain directly.
Always set your SSL/TLS mode to Full (strict) in the dashboard. This encrypts traffic end-to-end and validates the origin certificate. The Flexible mode leaves the backend connection unencrypted and should never be used in production.
PERFORMANCE MONITORING
Every Worker has a Metrics tab in the dashboard showing request count, CPU time, error rate, and P50/P95/P99 latency. Check it after every significant deployment.
For real-time debugging, wrangler tail streams live request logs to your terminal. It shows status codes, durations, and anything you log with console.log. You can filter to errors only with the --status error flag. This is the fastest way to diagnose a production incident without waiting for aggregate metrics to update.
Enable structured observability by adding an observability section to your wrangler.toml with enabled set to true. Log structured JSON objects using console.log so your logs are filterable in the dashboard by event type, user ID, or any other field you include.
For Pages projects, enable Web Analytics from the Speed section of the dashboard. It is privacy-first with no cookies and no fingerprinting, takes about 30 seconds to set up, and gives you pageviews, top pages, and referrers without adding a third-party tracking script to your site.
WRAPPING UP
The Cloudflare platform's biggest advantage is coherence. Pages, Workers, D1, R2, and KV all work together behind a single CLI and a single wrangler.toml. Once you have shipped a few projects this way, the workflow becomes automatic: wrangler dev locally, wrangler deploy to production, wrangler tail when something breaks. It is the fastest path from code to globally distributed infrastructure I have found.
Back to Guides
GuideBeginner
Complete Guide to Cloudflare Deployment
Everything you need to know about deploying applications to Cloudflare Pages and Workers.
January 25, 202430 min read
CloudflareDeploymentDevOps