Flying Fox Project · Publishing field guide

A local gallery.
A repeatable publishing pipeline.

Build a new digital image gallery called feathertail-glider, keep its source in GitHub, and publish it through Cloudflare Pages at images.fastigiata.com. Follow the journey from an empty folder to the next reviewed release.

Worked example onlyGitHub → Cloudflare PagesOptional Workers runtimeDocumentation checked 9 September 2026

This is a setup guide, not a deployed gallery. The repository, Cloudflare project and DNS records described here are hypothetical. Commands are examples to run later in the new site’s directory.

The proposed system

Know what each tool does

Flying Fox’s README describes local authoring, GitHub review and Cloudflare delivery. This example follows that pattern with an explicit public/ publishing folder; it is not an audit of Flying Fox’s live Cloudflare configuration.

ToolRole in this example
GitTracks changes and branches on your computer.
GitHubStores feathertail-glider and hosts pull requests. GitHub Pages is not enabled.
Cloudflare Workers & PagesDashboard area where you select and manage the Pages project.
Cloudflare PagesFetches Git commits, publishes public/, creates preview URLs and serves the gallery.
Pages Functions / Workers runtimeOptional server-side JavaScript for /api/health. Static images can be served without a Function.
Cloudflare DNS + TLSConnects the custom hostname and provides HTTPS after domain validation.
WranglerCloudflare CLI used here to test Pages and Functions locally. Git integration performs production publishing.

Cloudflare now identifies Workers as its primary application platform and supports static assets there too. This walkthrough deliberately uses the Pages Git integration requested here. If the creation screen offers a Worker deploy command, select the Pages flow before continuing.

Reference: Cloudflare · Platform choices.

flowchart TD
 accTitle: Gallery publishing pipeline
 accDescr: Local changes move through GitHub previews and review to the production Pages deployment.
 A["New local folder: feathertail-glider"] --> B["Git commit and push"]
 B --> C["GitHub feature branch and pull request"]
 C --> D["Cloudflare Pages preview"]
 D --> E["Review gallery and checks"]
 E --> F["Merge into main"]
 F --> G["Pages publishes public and optional Functions"]
 G --> H["Production pages.dev hostname"]
 G --> I["images.fastigiata.com via DNS and HTTPS"]
The first push of main establishes the initial deployment. Subsequent work uses branches and previews.
View Mermaid source
Mermaid
flowchart TD
 accTitle: Gallery publishing pipeline
 accDescr: Local changes move through GitHub previews and review to the production Pages deployment.
 A["New local folder: feathertail-glider"] --> B["Git commit and push"]
 B --> C["GitHub feature branch and pull request"]
 C --> D["Cloudflare Pages preview"]
 D --> E["Review gallery and checks"]
 E --> F["Merge into main"]
 F --> G["Pages publishes public and optional Functions"]
 G --> H["Production pages.dev hostname"]
 G --> I["images.fastigiata.com via DNS and HTTPS"]
Step 1 · Prerequisites

Start with the right accounts and boundaries

  1. Have an editor, terminal and Git available. Run git --version. On macOS, xcode-select --install installs command line tools if Git is missing.
  2. Sign into your GitHub account and the Cloudflare account that will own the new project. You need permission to create a repository and a Pages application.
  3. Confirm who manages DNS for fastigiata.com. This guide assumes the domain is yours and its Cloudflare zone is already active; that has not been verified. The requested domain is fastigiata.com, distinct from Flying Fox’s documented fastigiatalab.com.
  4. If DNS is hosted elsewhere, the subdomain can use an external CNAME; see step 6. A registrar transfer is not needed. Moving the whole domain to Cloudflare DNS is a separate task requiring review of existing web and mail records.
  5. For the later Wrangler exercise, install a Node.js release supported by current Wrangler, including npm. Check node --version and npm --version against its installation guide.

Example decisions: plain HTML, a small public gallery, a private source repository, production branch main, and only approved image exports in Git. A private repository does not make its published gallery private.

Reference: Cloudflare · Install Wrangler.

Step 2 · Your computer

Create the gallery files

Choose a parent folder outside flying-fox. In a terminal opened in that parent folder, these commands create a separate project. Open the resulting folder as its own editor workspace.

Terminal · new project
mkdir feathertail-glider
cd feathertail-glider
mkdir -p public/images
pwd
Initial project layout
feathertail-glider/
  .gitignore
  README.md
  public/
    index.html
    about.html
    404.html
    images/
      forest-1200.webp

Create each text file in your editor. Export one photograph as public/images/forest-1200.webp at 1200 × 800 pixels, or adjust the filename, dimensions and description below to match your real export. Keep original camera files outside this repository.

public/index.html
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Feathertail Glider · Image Gallery</title>
  <style>
    body { max-width:70rem; margin:auto; padding:1.5rem;
      background:#161618; color:#d4d4d4; font:1rem/1.6 system-ui; }
    a { color:#abc8dc; } figure { margin:2rem 0; }
    img { display:block; max-width:100%; height:auto; border-radius:1rem; }
  </style>
</head>
<body>
  <nav aria-label="Main"><a href="/">Gallery</a> · <a href="/about.html">About</a></nav>
  <main>
    <h1>Feathertail Glider</h1>
    <p>A collection of forest moments.</p>
    <figure>
      <img src="/images/forest-1200.webp" width="1200" height="800"
        alt="Sunlight filtering through tall eucalyptus trees">
      <figcaption>Forest light · Photography by Stuart Anderson</figcaption>
    </figure>
  </main>
</body>
</html>

For about.html, copy the document structure, use an “About the gallery” title and heading, and describe the collection and image reuse terms. For 404.html, use “Page not found”, a short explanation and a link to /. A top-level 404 page also makes the intended multi-page behaviour explicit.

.gitignore
.DS_Store
node_modules/
.wrangler/
.env
.env.*
.dev.vars
.dev.vars.*

In README.md, record the gallery’s purpose, public/ output folder and intended hostname. Preview from the repository root:

Local static preview
python3 -m http.server 8000 --bind 127.0.0.1 --directory public

Open http://127.0.0.1:8000. Check the image, About link and mobile layout. Stop the server with Control-C. This Python server checks static files; the later Wrangler preview tests Cloudflare routing and Functions.

Checkpoint: the gallery works locally and every file intended for public delivery is inside public/.

Reference: Cloudflare · Static HTML.

Step 3 · Source control

Connect the local site to GitHub

  1. In GitHub, choose New repository. Select your owner account, enter feathertail-glider, and choose Private for this example.
  2. Create an empty repository: leave README, .gitignore and licence initialization off because local files already exist.
  3. Copy the new repository’s HTTPS URL. In the commands below, replace YOUR_GITHUB_USERNAME with the actual owner. Run them from the local feathertail-glider directory.
Initialize and publish the first commit
git init -b main
git config user.name "YOUR NAME"
git config user.email "YOUR VERIFIED OR GITHUB NOREPLY EMAIL"
git add .gitignore README.md public
git diff --cached --stat
git commit -m "Create initial image gallery"
git remote add origin https://github.com/YOUR_GITHUB_USERNAME/feathertail-glider.git
git remote -v
git push -u origin main

The two identity settings apply only to this repository. Authenticate using your configured Git credential manager or GitHub CLI browser login; a GitHub account password is not used for HTTPS Git pushes. Never paste a token into the remote URL or a committed file.

Checkpoint: refresh GitHub. The main branch contains public/index.html and your image. Leave GitHub’s own Pages hosting disabled.

Reference: GitHub · Add local code.

Step 4 · Cloudflare dashboard

Create the Pages Git integration

  1. Select the correct Cloudflare account and open Workers & Pages.
  2. Choose Create application → Pages → Connect to Git. Dashboard wording may shift; choose the Pages Git import flow.
  3. Select GitHub, authorize the Cloudflare GitHub application, and grant access to the selected feathertail-glider repository. If it is missing, review that app’s repository access in GitHub settings.
  4. Select the repository and begin setup. Enter the configuration below.
SettingExample value
Project namefeathertail-glider (if available)
Production branchmain
Framework presetNone
Build commandexit 0
Build output directorypublic
Root directory (advanced)Leave blank: repository root
Environment variablesNone for this example

exit 0 is a successful no-op: the HTML is already built. Keep the root at the repository level so later functions/ is discoverable. Only public/ supplies static assets. Select Save and Deploy.

Reference: Cloudflare · Git integration setup.

Reference: Cloudflare · Static HTML build command.

Step 5 · Deployment verification

Prove the pages.dev site first

Open the project’s Deployments tab. Inspect the production deployment and its build log. Confirm the commit matches the first GitHub commit and the result is successful.

Open the exact URL Cloudflare provides, for example https://feathertail-glider.pages.dev. This hostname depends on project-name availability. Use the actual assigned hostname in every later DNS example.

Checkpoint: the Cloudflare-assigned hostname serves the right commit before a custom domain is attached.

Reference: Cloudflare · Serving Pages.

Step 6 · DNS and HTTPS

Attach images.fastigiata.com

  1. Open Workers & Pages → feathertail-glider → Custom domains → Set up a custom domain.
  2. Enter images.fastigiata.com and continue. Associate the hostname with the Pages project before creating a CNAME.
  3. If fastigiata.com is an active zone in the same account, review the DNS record Cloudflare proposes and confirm the setup. If DNS is external, add the requested CNAME at that DNS provider.
  4. If an images A, AAAA or CNAME record already exists, identify its current service before replacing it. Leave apex, www and mail records alone.
  5. Wait for the project to show the domain active and HTTPS ready, then open the custom URL.
DNS fieldExpected value
TypeCNAME
Name / hostimages (some providers require images.fastigiata.com)
Targetfeathertail-glider.pages.dev — use the actual assigned hostname
TTLAuto or provider default
Cloudflare proxyUse the Pages wizard’s generated setting; an external provider has no Cloudflare proxy toggle

No scheme or path belongs in the CNAME target. DNS maps the hostname; it does not redirect the browser’s address bar. Check the image and About page again over HTTPS.

Reference: Cloudflare · Custom domains.

flowchart TD
 accTitle: Gallery request routing
 accDescr: A custom hostname reaches Pages; static paths serve assets while the optional API route runs a Function.
 A["Visitor opens images.fastigiata.com"] --> B["DNS and HTTPS"]
 B --> C["Cloudflare Pages project"]
 C --> D{"Requested path"}
 D -- "/ or /images/*" --> E["Static files from public"]
 D -- "/api/health (optional)" --> F["Pages Function on Workers runtime"]
 F --> G["JSON response"]
Pages handles the custom hostname. The optional Function shares that project and hostname.
View Mermaid source
Mermaid
flowchart TD
 accTitle: Gallery request routing
 accDescr: A custom hostname reaches Pages; static paths serve assets while the optional API route runs a Function.
 A["Visitor opens images.fastigiata.com"] --> B["DNS and HTTPS"]
 B --> C["Cloudflare Pages project"]
 C --> D{"Requested path"}
 D -- "/ or /images/*" --> E["Static files from public"]
 D -- "/api/health (optional)" --> F["Pages Function on Workers runtime"]
 F --> G["JSON response"]
Step 7 · Optional server-side capability

Add a Pages Function on Workers

The gallery is complete as a static site. To learn the Workers part of the platform, add a small health endpoint on a new branch. Pages Functions runs on the Workers runtime and is deployed with the Pages project; a separate Worker application or Worker route is unnecessary for this example.

Terminal · from the new gallery repository
git switch -c add-health-function
mkdir -p functions/api
npm init -y
npm install --save-dev wrangler

Create functions/api/health.js beside public/, not inside it:

functions/api/health.js
export function onRequestGet() {
  return Response.json({
    ok: true,
    site: "feathertail-glider",
    service: "gallery"
  });
}

Reference: Cloudflare · Create a Pages Function.

Create public/_routes.json to restrict Function invocation to this endpoint while the rest of the gallery uses static delivery:

public/_routes.json
{
  "version": 1,
  "include": ["/api/health"],
  "exclude": []
}

Reference: Cloudflare · Function routing.

Test locally with the Cloudflare runtime
npx wrangler pages dev public

Open the local URL printed by Wrangler (normally http://localhost:8788), then /api/health. Expect JSON with ok: true. Check the image as well. Local testing does not require a deployment command or production API token. Stop with Control-C.

Reference: Cloudflare · Local Pages development.

Commit and push the optional function
git add functions public/_routes.json package.json package-lock.json
git diff --cached
git commit -m "Add gallery health endpoint"
git push -u origin add-health-function

Follow the preview review in step 8 before merging. The Git integration bundles the Function and static assets together. This dashboard-configured example does not need wrangler.jsonc, wrangler deploy, an R2 binding or secrets. Function usage is subject to Workers limits and pricing.

Reference: Cloudflare · Functions pricing.

Step 8 · Everyday publishing

Preview, review, merge, verify

For later gallery changes, start with an up-to-date main branch and create a feature branch. The optional Function branch above can go straight to the pull-request stage.

Example · adding another photograph
git switch main
git pull --ff-only
git switch -c add-coastal-gallery
# Edit public/index.html and add your exported images in the editor.
git add public
git diff --cached --stat
git commit -m "Add coastal gallery photographs"
git push -u origin add-coastal-gallery
  1. In GitHub, open a pull request from the new branch into main. Describe the visible change and what you checked.
  2. Find the Pages preview link in the pull request or the Cloudflare project’s Deployments tab. Check mobile layout, captions, image URLs and any API endpoint. A preview deployment leaves production unchanged.
  3. Merge the pull request when the preview is satisfactory. Pages detects the main commit and starts a production deployment.
  4. Confirm deployment success and matching commit, then check images.fastigiata.com. A merge does not prove deployment success.

Previews are publicly reachable by default. Cloudflare Access can protect previews, but preview protection does not automatically protect the production custom domain.

Reference: Cloudflare · Preview deployments.

Build and deployment checks are not HTML, accessibility or image-quality tests. This example uses manual review. If you later add GitHub Actions checks, configure available branch protection or rulesets to require them before merging; a separate Actions job does not automatically gate Pages deployment.

Step 9 · Maintain the pipeline

Troubleshoot and roll back

SymptomCheck and next action
Repository missing in CloudflareCheck the selected GitHub account and installed Cloudflare application’s repository permissions.
Successful deploy, but gallery is 404Confirm output directory public and committed public/index.html. Verify the deployed branch and commit.
Broken imageCompare exact filename case and URL. Ensure Git contains the image file, not just an untracked local export.
Custom domain fails, pages.dev worksCheck Pages domain association, DNS target and validation status. Review conflicting records, CAA restrictions and existing hostname rules.
API route returns 404Confirm functions/api/health.js is at repository root and _routes.json includes /api/health. Rebuild after committing both files.
Old production contentCheck deployment status and commit, then browser caching and custom Cloudflare cache rules.
Deployment failsRead the first meaningful build-log error. Fix in Git and push; do not assume the failed commit replaced the last successful deployment.

For an urgent recovery, open Workers & Pages → project → Deployments, locate an earlier successful production deployment and use its rollback action. Preview deployments cannot be used as production rollback targets. Verify the custom hostname after rollback.

A dashboard rollback does not change Git. Revert the faulty change in a new branch and merge the corrective pull request so the next deployment preserves the fix. For a single faulty commit, git revert COMMIT_SHA creates an undo commit; merge commits need deliberate parent selection.

Reference: Cloudflare · Rollbacks.

Keep an image gallery manageable

Use web-sized exports, descriptive filenames, dimensions and useful alt text. Add lazy loading to below-the-fold images. Keep RAW originals and private metadata out of public exports. Pages limits individual assets to 25 MiB; check current file-count and build limits as the collection grows. R2 or Cloudflare Images can be a later storage design, with separate configuration and costs.

Reference: Cloudflare · Pages limits.

Your finished setup record

Record the repository URL, production branch, assigned pages.dev hostname, custom domain, build settings and a successful commit ID in the new repository’s README. That gives the next publishing session a concrete reference.