Cloudflare Pages + GitHub Setup Guide¶
Project: arlyn.io company website Date: March 2026
Overview¶
This document outlines the steps taken to connect a private GitHub repository (hosted under the ArlynLabs organization) to Cloudflare Pages, enabling automatic deployment of the static site on every push to main.
Prerequisites¶
- A Cloudflare account with the target domain (
arlyn.io) already registered - A GitHub personal account that owns the target GitHub organization
- The GitHub organization created (
ArlynLabs) - The target repository created (
ArlynLabs/arlyn-website, private)
Step 1: Create the GitHub Repository¶
Create a private repository in the GitHub organization to serve as the source for the site.
- Organization: ArlynLabs
- Repository name: arlyn-website
- Visibility: Private
- Branch:
main
Push an initial commit (e.g. a README.md) to initialize the main branch before connecting Cloudflare.
Step 2: Create a GitHub Personal Access Token (PAT)¶
A fine-grained PAT is used to allow programmatic access to the repository (for automated commits and pushes).
- Log into GitHub as your personal account (org owner)
- Go to Settings → Developer settings → Personal access tokens → Fine-grained tokens
- Click Generate new token with the following settings:
- Token name: e.g.
Claude Cowork - Resource owner: Leave as your personal account (
d-arlynlabs) — since you own the org, your personal account already has full access to org repos, and no org approval step is required - Repository access: Only select repositories →
arlyn-website - Permissions:
- Contents: Read and write
- Metadata: Read-only (auto-selected)
- Copy the token immediately (shown only once)
- Save the token to
.secrets/github_patin the project workspace
Note on resource owner and org approval: If you set the resource owner to the organization (ArlynLabs) instead of your personal account, GitHub will require an org admin to approve the token before it activates. The approval page is at ArlynLabs org settings → Personal access tokens → Pending requests. Setting the personal account as resource owner (as done here) avoids this extra step entirely, since the personal account inherits full access to the org's repos as its owner.
Step 3: Configure Git for Automated Commits¶
Configure git in the automation environment to use the PAT for authentication.
git config --global user.name "d-arlyn"
git config --global user.email "d@arlyn.dev"
git config --global credential.helper store
echo "https://x-token-auth:<PAT>@github.com" > ~/.git-credentials
chmod 600 ~/.git-credentials
A reusable setup script is stored at .claude/setup.sh in the workspace. Run this at the start of each session to restore git access, since the automation environment resets between sessions.
Step 4: Install the Cloudflare GitHub App on the Organization¶
How the GitHub–Cloudflare connection works¶
Cloudflare Pages integrates with GitHub through a GitHub App — a first-party integration that lives on the GitHub side and grants Cloudflare permission to read your repository and post deployment status back to pull requests. Think of it as GitHub saying "I trust Cloudflare to watch this repo."
Once installed, the flow works like this:
You push to GitHub → GitHub notifies Cloudflare (via the App) → Cloudflare pulls the code and deploys it
The GitHub App lives in your GitHub organization settings (under Installed GitHub Apps), not inside Cloudflare. Cloudflare just holds a reference to the installation so it knows which repo to watch.
Installation steps¶
The installation is initiated from the Cloudflare side, then completed on GitHub:
- In the Cloudflare dashboard, go to Workers & Pages → Create application → Pages → Get started → Import an existing Git repository
- Click Connect GitHub — Cloudflare redirects you to GitHub to install its app
- GitHub asks "Where do you want to install Cloudflare Workers and Pages?" — select ArlynLabs
- On the next GitHub screen, choose Only select repositories and pick
arlyn-website(rather than granting access to all org repos) - Confirm the permissions GitHub lists:
- Read access to metadata
- Read and write access to administration, checks, code, deployments, and pull requests
- Click Install & Authorize — GitHub installs the app on ArlynLabs and redirects back to Cloudflare
What ends up where¶
| What | Where it lives | Purpose |
|---|---|---|
| Cloudflare GitHub App | GitHub → ArlynLabs org → Settings → GitHub Apps | Grants Cloudflare access to the repo |
| Cloudflare Pages project | Cloudflare dashboard → Workers & Pages | Defines build settings and holds deployment history |
| The link between them | Configured when creating the Pages project (Step 6) | Tells Cloudflare which repo and branch to watch |
Known issue: browser UI loop¶
After the app is installed, returning to the Cloudflare "Connect GitHub" flow lands back on GitHub's app selection screen, which now only shows a Configure link for ArlynLabs (since the app is already installed) rather than completing the OAuth handshake back to Cloudflare. This prevents the browser UI from finishing the Pages project setup.
Solution: Create the Pages project directly via the Cloudflare API instead (see Step 6), which links to GitHub using the existing app installation without needing the browser OAuth flow to complete.
Step 5: Create a Cloudflare API Token¶
A Cloudflare API token is required to create and manage the Pages project programmatically.
- In the Cloudflare dashboard, go to My Profile → API Tokens → Create Token
- Choose Create Custom Token
- Configure with the following settings:
- Token name:
Cloudflare Pages Deploy - Permissions: Account → Cloudflare Pages → Edit
- Account Resources: All accounts
- Click Continue to summary, then Create Token
- Copy the token immediately (shown only once)
- Save the token to
.secrets/cloudflare_api_tokenin the project workspace
Step 6: Create the Cloudflare Pages Project via API¶
The Pages project was created using the Cloudflare REST API, linking it directly to the GitHub repository.
curl -X POST "https://api.cloudflare.com/client/v4/accounts/<ACCOUNT_ID>/pages/projects" \
-H "Authorization: Bearer <CF_API_TOKEN>" \
-H "Content-Type: application/json" \
--data '{
"name": "arlyn-website",
"production_branch": "main",
"source": {
"type": "github",
"config": {
"owner": "ArlynLabs",
"repo_name": "arlyn-website",
"production_branch": "main",
"pr_comments_enabled": true,
"deployments_enabled": true
}
},
"build_config": {
"build_command": "",
"destination_dir": "/",
"root_dir": ""
}
}'
Result:
- Project name: arlyn-website
- Pages URL: arlyn-website.pages.dev
- Account ID: e3874665bc48a0819bc5be1dfbdf454d
- No build command is needed — the site is plain static HTML/CSS/JS
Step 7: Trigger the Initial Deployment¶
After creating the project, trigger the first deployment manually via API:
curl -X POST "https://api.cloudflare.com/client/v4/accounts/<ACCOUNT_ID>/pages/projects/arlyn-website/deployments" \
-H "Authorization: Bearer <CF_API_TOKEN>" \
-H "Content-Type: application/json" \
--data '{"branch": "main"}'
From this point forward, every push to main on ArlynLabs/arlyn-website will automatically trigger a new deployment.
Deployment Workflow (Ongoing)¶
cd /path/to/arlyn-website
# make changes to site files
git add .
git commit -m "describe changes"
git push
# Cloudflare Pages auto-deploys within ~1 minute
Preview deployments are also created automatically for non-main branches.
Step 8: Connect the Custom Domain (arlyn.io)¶
Add the custom domain via the Cloudflare API:
curl -X POST "https://api.cloudflare.com/client/v4/accounts/<ACCOUNT_ID>/pages/projects/arlyn-website/domains" \
-H "Authorization: Bearer <CF_API_TOKEN>" \
-H "Content-Type: application/json" \
--data '{"name": "arlyn.io"}'
Then add the DNS record in the Cloudflare dashboard (DNS → Records for arlyn.io):
| Type | Name | Target | Proxy |
|---|---|---|---|
| CNAME | @ (root) | arlyn-website.pages.dev | Proxied (orange cloud) |
Note: Using
@as the CNAME name works because Cloudflare flattens CNAME records at the apex domain — a feature not available on standard DNS providers.
Once the record is saved, return to Pages → Custom Domains and click Check DNS records. The domain status should update to Active with SSL enabled within about 30 seconds.
Result: arlyn.io resolves to the live Cloudflare Pages deployment with SSL provided by Google CA.
Step 9: Redirect pages.dev to the Custom Domain¶
Cloudflare Pages permanently serves the site at both arlyn-website.pages.dev and arlyn.io. The .pages.dev URL is not a preview — it always reflects the live production deployment. Left unaddressed, search engines could index it as a duplicate of the canonical domain.
To prevent this, a _worker.js file is added to the repo root. Cloudflare Pages automatically picks this up and runs it as an edge worker on every request, allowing hostname-based logic before assets are served.
// _worker.js
export default {
async fetch(request, env) {
const url = new URL(request.url);
// Redirect pages.dev URL to the canonical domain
if (url.hostname.endsWith('.pages.dev')) {
return Response.redirect(
`https://arlyn.io${url.pathname}${url.search}`,
301
);
}
// Serve static assets normally for all other requests
return env.ASSETS.fetch(request);
}
};
Any request to arlyn-website.pages.dev receives a 301 redirect to the equivalent path on arlyn.io. Requests to arlyn.io are unaffected — env.ASSETS.fetch(request) serves the static files as normal.
Cost: This worker runs only on
.pages.devtraffic, which is negligible in practice. Cloudflare's free tier includes 100,000 Worker requests per day — far more than needed here.
Key References¶
| Item | Value |
|---|---|
| GitHub org | ArlynLabs |
| Repository | ArlynLabs/arlyn-website |
| Cloudflare Pages URL | arlyn-website.pages.dev |
| Cloudflare Account ID | e3874665bc48a0819bc5be1dfbdf454d |
| PAT location | .secrets/github_pat |
| CF API token location | .secrets/cloudflare_api_token |
| Session setup script | .claude/setup.sh |