aRustyDev / aRustyDev/mdbook-htmx

docs(examples): Cloudflare Pages Static Deployment

Open
#37 0 comments 0 reactions 1 assignee Claimed by @aRustyDev View on GitHub
documentation
Dominant language
Rust
Stars
0
Forks
1
PR merge metrics
No merged PRs in 30d

Description

# Cloudflare Pages Static Deployment

Deploy mdbook-htmx output as a static site on Cloudflare Pages.

## Implementation

The Cloudflare deployment configuration lives in the mdbook-htmx repository:

```
mdbook-htmx/
├── wrangler.jsonc # Wrangler configuration
├── workers/
│ ├── index.ts # Main worker with HTMX routing
│ ├── auth.ts # Auth utilities (Phase 3)
│ ├── search.ts # Search handler (Phase 4)
│ ├── analytics.ts # Analytics (Phase 5)
│ ├── package.json
│ └── tsconfig.json
├── migrations/
│ └── 0001_sessions.sql # D1 schema (Phase 3)
└── .github/workflows/
├── test-deployments.yml
└── deploy-cloudflare.yml
```

**Production URL:** `https://mdbook-htmx.arusty.dev`

See [ADR-0016](../adr/0016-implementation-phasing-strategy.md) for phase-by-phase additions.

## Overview

This is the simplest deployment option—pure static files served from Cloudflare's edge network. Ideal for public documentation without authentication requirements.

## Prerequisites

- Cloudflare account
- Wrangler CLI (`npm install -g wrangler`)
- mdbook-htmx built output

## Project Structure

```
my-docs/
├── book.toml
├── src/
│ └── ... (markdown files)
├── book/ # mdbook-htmx output
│ ├── index.html
│ ├── manifest.json
│ ├── search-index.json
│ └── assets/
└── wrangler.toml # Pages config
```

## Configuration

### book.toml

```toml
[book]
title = "My Documentation"
authors = ["Your Name"]
language = "en"

[output.htmx]
# Base URL for assets (Cloudflare Pages domain)
base_url = "https://docs.example.com"

# Enable asset hashing for cache busting
asset_hashing = true

# Prerender all pages for static deployment
prerender = true

# Disable server-side features
[output.htmx.search]
mode = "client" # Client-side search only

[output.htmx.authn]
enabled = false # No authentication
```

### wrangler.toml

```toml
name = "my-docs"
compatibility_date = "2024-01-01"
pages_build_output_dir = "book"

# Optional: Custom headers
[[headers]]
for = "/*"
[headers.values]
X-Frame-Options = "DENY"
X-Content-Type-Options = "nosniff"
Referrer-Policy = "strict-origin-when-cross-origin"

# Cache static assets aggressively
[[headers]]
for = "/assets/*"
[headers.values]
Cache-Control = "public, max-age=31536000, immutable"
```

## Build Script

```bash
#!/bin/bash
# build.sh

set -e

echo "Building documentation..."
mdbook build

echo "Optimizing assets..."
# Optional: Compress images, minify CSS/JS
# npx postcss book/assets/css/main.css -o book/assets/css/main.min.css

echo "Build complete!"
```

## GitHub Actions Deployment

```yaml
# .github/workflows/deploy.yml
name: Deploy to Cloudflare Pages

on:
push:
branches: [main]
pull_request:
branches: [main]

jobs:
deploy:
runs-on: ubuntu-latest
permissions:
contents: read
deployments: write

steps:
- uses: actions/checkout@v4

- name: Install mdbook
run: |
curl -sSL https://github.com/rust-lang/mdBook/releases/download/v0.4.37/mdbook-v0.4.37-x86_64-unknown-linux-gnu.tar.gz | tar -xz
chmod +x mdbook
sudo mv mdbook /usr/local/bin/

- name: Install mdbook-htmx
run: |
cargo install mdbook-htmx

- name: Build documentation
run: mdbook build

- name: Deploy to Cloudflare Pages
uses: cloudflare/pages-action@v1
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
projectName: my-docs
directory: book
gitHubToken: ${{ secrets.GITHUB_TOKEN }}
branch: ${{ github.head_ref || github.ref_name }}
```

## Custom Domain Setup

1. In Cloudflare Dashboard → Pages → your-project → Custom domains
2. Add domain: `docs.example.com`
3. DNS will be configured automatically if domain is on Cloudflare

## Redirects

Create `book/_redirects` for URL handling:

```
# Redirect old paths
/old-page /new-page 301

# SPA fallback (if needed)
/* /index.html 200
```

## Headers

Create `book/_headers` for custom response headers:

```
# All pages
/*
X-Frame-Options: DENY
X-Content-Type-Options: nosniff
Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'

# Cache static assets
/assets/*
Cache-Control: public, max-age=31536000, immutable

# Don't cache HTML
/*.html
Cache-Control: public, max-age=0, must-revalidate
```

## Performance Optimization

### Enable Brotli Compression

Cloudflare automatically compresses responses. Ensure it's enabled:

1. Dashboard → your-domain → Speed → Optimization
2. Enable "Brotli"

### Image Optimization

Use Cloudflare Image Resizing for dynamic image optimization:

```html
Diagram
```

## Monitoring

### Web Analytics

Enable Cloudflare Web Analytics (privacy-friendly):

```html

```

### Page Views in Dashboard

View analytics at: Dashboard → Pages → your-project → Analytics

## Cost

- **Free tier**: Unlimited requests, 500 builds/month
- **Pro**: $20/month for advanced features

## Limitations

- No server-side rendering
- No authentication (use cf-workers-d1 for auth)
- No server-side search (client-side only)
- Build output limited to 25,000 files, 25 MB per file

## Next Steps

- Add [authentication with Workers](./cf-workers-d1.md)
- Set up [Meilisearch for server-side search](./meilisearch-cf-tunnel.md)
- Configure [KV caching](./cf-workers-kv-cache.md)

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.