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.
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.
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.
| Tool | Role in this example |
|---|---|
| Git | Tracks changes and branches on your computer. |
| GitHub | Stores feathertail-glider and hosts pull requests. GitHub Pages is not enabled. |
| Cloudflare Workers & Pages | Dashboard area where you select and manage the Pages project. |
| Cloudflare Pages | Fetches Git commits, publishes public/, creates preview URLs and serves the gallery. |
| Pages Functions / Workers runtime | Optional server-side JavaScript for /api/health. Static images can be served without a Function. |
| Cloudflare DNS + TLS | Connects the custom hostname and provides HTTPS after domain validation. |
| Wrangler | Cloudflare 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"]
View Mermaid source
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"]Start with the right accounts and boundaries
- Have an editor, terminal and Git available. Run
git --version. On macOS,xcode-select --installinstalls command line tools if Git is missing. - 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.
- 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 isfastigiata.com, distinct from Flying Fox’s documentedfastigiatalab.com. - 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.
- For the later Wrangler exercise, install a Node.js release supported by current Wrangler, including npm. Check
node --versionandnpm --versionagainst 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.
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.
mkdir feathertail-glider
cd feathertail-glider
mkdir -p public/images
pwdfeathertail-glider/
.gitignore
README.md
public/
index.html
about.html
404.html
images/
forest-1200.webpCreate 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.
<!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.
.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:
python3 -m http.server 8000 --bind 127.0.0.1 --directory publicOpen 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.
Connect the local site to GitHub
- In GitHub, choose New repository. Select your owner account, enter
feathertail-glider, and choose Private for this example. - Create an empty repository: leave README, .gitignore and licence initialization off because local files already exist.
- Copy the new repository’s HTTPS URL. In the commands below, replace
YOUR_GITHUB_USERNAMEwith the actual owner. Run them from the localfeathertail-gliderdirectory.
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 mainThe 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.
Create the Pages Git integration
- Select the correct Cloudflare account and open Workers & Pages.
- Choose Create application → Pages → Connect to Git. Dashboard wording may shift; choose the Pages Git import flow.
- Select GitHub, authorize the Cloudflare GitHub application, and grant access to the selected
feathertail-gliderrepository. If it is missing, review that app’s repository access in GitHub settings. - Select the repository and begin setup. Enter the configuration below.
| Setting | Example value |
|---|---|
| Project name | feathertail-glider (if available) |
| Production branch | main |
| Framework preset | None |
| Build command | exit 0 |
| Build output directory | public |
| Root directory (advanced) | Leave blank: repository root |
| Environment variables | None 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.
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.
- Load the gallery and About page directly, then refresh each.
- Open
/images/forest-1200.webpdirectly and confirm it is an image. - Visit
/missing-pageand confirm the custom not-found response. - Inspect at phone width and test keyboard links.
Checkpoint: the Cloudflare-assigned hostname serves the right commit before a custom domain is attached.
Reference: Cloudflare · Serving Pages.
Attach images.fastigiata.com
- Open Workers & Pages → feathertail-glider → Custom domains → Set up a custom domain.
- Enter
images.fastigiata.comand continue. Associate the hostname with the Pages project before creating a CNAME. - If
fastigiata.comis 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. - If an images A, AAAA or CNAME record already exists, identify its current service before replacing it. Leave apex, www and mail records alone.
- Wait for the project to show the domain active and HTTPS ready, then open the custom URL.
| DNS field | Expected value |
|---|---|
| Type | CNAME |
| Name / host | images (some providers require images.fastigiata.com) |
| Target | feathertail-glider.pages.dev — use the actual assigned hostname |
| TTL | Auto or provider default |
| Cloudflare proxy | Use 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"]View Mermaid source
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"]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.
git switch -c add-health-function
mkdir -p functions/api
npm init -y
npm install --save-dev wranglerCreate functions/api/health.js beside public/, not inside it:
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:
{
"version": 1,
"include": ["/api/health"],
"exclude": []
}Reference: Cloudflare · Function routing.
npx wrangler pages dev publicOpen 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.
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-functionFollow 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.
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.
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- In GitHub, open a pull request from the new branch into main. Describe the visible change and what you checked.
- 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.
- Merge the pull request when the preview is satisfactory. Pages detects the main commit and starts a production deployment.
- 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.
Troubleshoot and roll back
| Symptom | Check and next action |
|---|---|
| Repository missing in Cloudflare | Check the selected GitHub account and installed Cloudflare application’s repository permissions. |
| Successful deploy, but gallery is 404 | Confirm output directory public and committed public/index.html. Verify the deployed branch and commit. |
| Broken image | Compare exact filename case and URL. Ensure Git contains the image file, not just an untracked local export. |
| Custom domain fails, pages.dev works | Check Pages domain association, DNS target and validation status. Review conflicting records, CAA restrictions and existing hostname rules. |
| API route returns 404 | Confirm functions/api/health.js is at repository root and _routes.json includes /api/health. Rebuild after committing both files. |
| Old production content | Check deployment status and commit, then browser caching and custom Cloudflare cache rules. |
| Deployment fails | Read 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.