---
title: "How to set up Quartz with Cloudflare Pages"
slug: "set-up-quartz-with-cloudflare-pages"
category: "howto"
tags: ["howto", "quartz", "cloudflare", "deployment", "tutorial", "ops", "ssg"]
status: "stable"
last_updated: 2026-05-14
summary: "Fork Quartz, push to GitHub, connect Cloudflare Pages, configure baseUrl, and point a custom domain, all without touching GitHub Pages."
related: ["[[tooling/quartz]]", "[[ops/cloudflare]]", "[[ops/namecheap-dns]]", "[[howto/deploy-quartz-site]]", "[[howto/run-a-quartz-static-site-locally]]", "[[comparisons/vercel-vs-cloudflare]]"]
---

## Overview

Cloudflare Pages is a zero-config CDN-backed host that gives Quartz better global latency than GitHub Pages and free analytics via Cloudflare Web Analytics. This guide forks Quartz, connects the repo to Cloudflare Pages, configures `baseUrl`, and wires a custom domain. It takes under 30 minutes on a fresh Cloudflare account. Compare the trade-offs in [[comparisons/vercel-vs-cloudflare]].

## Prerequisites

- A Cloudflare account. Free tier is enough for a content site.
- A GitHub account. Cloudflare Pages deploys from GitHub or GitLab.
- Node 22 installed locally. Check with `node -v`.
- A custom domain whose DNS is on Cloudflare (or that you can transfer). See [[ops/namecheap-dns]] if the domain is on Namecheap.
- Local Quartz running. See [[howto/run-a-quartz-static-site-locally]] if not.

## Steps

### 1. Fork Quartz and push your content

```bash
git clone https://github.com/jackyzha0/quartz.git mysite
cd mysite
npm ci
```

Drop your markdown into `content/`. Every page needs YAML frontmatter with at minimum `title`, `slug`, `category`, and `status`. See [[tooling/quartz]] for the full plugin set.

Push the repo to your own GitHub account, default branch `main`.

### 2. Set `baseUrl` in `quartz.config.ts`

Edit `quartz.config.ts`. Set `baseUrl` to the bare domain with no protocol and no trailing slash.

```ts
configuration: {
  pageTitle: "My Site",
  baseUrl: "yourdomain.com",
  // ...
}
```

Remove `Plugin.CNAME()` from the emitters list. That plugin is specific to GitHub Pages and not needed here. Cloudflare Pages handles the custom domain at the CDN layer.

### 3. Build locally and confirm no errors

```bash
npx quartz build
# should print "Done processing X files" with no errors
```

Commit everything and push to `main`.

### 4. Connect Cloudflare Pages

1. In the Cloudflare dashboard, open Pages, then click "Create a project."
2. Select "Connect to Git" and authorize the GitHub app.
3. Pick your repository.
4. On the "Set up builds and deployments" screen:
   - **Framework preset**: None (Quartz is not in the preset list).
   - **Build command**: `npm ci && npx quartz build`
   - **Build output directory**: `public`
   - **Node version**: `22` (set under Environment Variables as `NODE_VERSION=22`).
5. Click "Save and Deploy."

Cloudflare runs the first build. Check the build log under "Deployments" and confirm the last line is "Deployment complete."

### 5. Set the custom domain

In your Cloudflare Pages project, open Settings, then Custom Domains. Click "Set up a custom domain" and enter `yourdomain.com`.

If the domain's DNS is already on Cloudflare, the CNAME is added automatically. If not, Cloudflare shows the CNAME record to add at your registrar. Wait for DNS propagation (usually under five minutes when already on Cloudflare), then click "Activate domain."

Cloudflare Pages provisions a Let's Encrypt certificate automatically. HTTPS is enforced by default.

### 6. Set SSL/TLS mode to Full (strict)

In the Cloudflare dashboard, go to your domain, then SSL/TLS, then Overview. Set the mode to "Full (strict)." This ensures the connection between Cloudflare and the origin is also verified. See [[ops/cloudflare]] for the full TLS configuration playbook.

### 7. Confirm the deployment URL

Cloudflare Pages assigns a preview URL like `mysite.pages.dev`. This URL stays live for all deployments and is useful for testing PRs. Add it to your `quartz.config.ts` `baseUrl` only if the custom domain fails DNS verification; otherwise keep the custom domain as `baseUrl`.

## Verify it worked

```bash
# 1. HTTPS responds with 200
curl -sI https://yourdomain.com | head -1
# expected: HTTP/2 200

# 2. The build output directory is correct
curl -s https://yourdomain.com | grep -c "<html"
# expected: 1

# 3. No mixed-content warnings (check the browser console after loading the page)
# 4. Cloudflare caches the response
curl -sI https://yourdomain.com | grep cf-cache-status
# expected: cf-cache-status: HIT (on second request)
```

## Common errors

- Build fails with "node: not found." Set `NODE_VERSION=22` in the Pages project's Environment Variables section.
- `baseUrl` mismatch causes broken links. Confirm `baseUrl` is the bare domain without `https://` or a trailing slash.
- `Plugin.CNAME()` writes a `CNAME` file that causes 404s on Cloudflare. Remove it from the emitters list; it is only needed for GitHub Pages.
- Custom domain stuck on "Pending." The CNAME was added but DNS has not propagated. Use `dig CNAME yourdomain.com` to confirm, then wait up to 30 minutes.
- Pages project builds from the wrong branch. In Settings, Builds and deployments, confirm "Production branch" is set to `main`.

## Related

- [[tooling/quartz]]
- [[ops/cloudflare]]
- [[ops/namecheap-dns]]
- [[howto/deploy-quartz-site]]
- [[howto/run-a-quartz-static-site-locally]]
- [[comparisons/vercel-vs-cloudflare]]
- [[howto/ship-llms-txt]]
