Skip to content

Claude/cloudflare workers deployment#17

Open
BayPhillips wants to merge 14 commits intomainfrom
claude/cloudflare-workers-deployment-plan-sKIm9
Open

Claude/cloudflare workers deployment#17
BayPhillips wants to merge 14 commits intomainfrom
claude/cloudflare-workers-deployment-plan-sKIm9

Conversation

@BayPhillips
Copy link
Copy Markdown
Owner

No description provided.

Comprehensive migration plan for moving the Next.js blog and embedded
Sanity Studio from Vercel to Cloudflare Workers using @opennextjs/cloudflare.
Covers OpenNext adapter setup, image optimization, Sanity Studio
compatibility, Workers VPC for private networking, bundle size management,
CI/CD pipeline, DNS cutover, and rollback strategy.

https://claude.ai/code/session_01BWiNneRSNTNqphGMqE16o4
- Install @opennextjs/cloudflare and wrangler
- Remove @vercel/analytics, @vercel/speed-insights, and vercel CLI
- Create wrangler.jsonc with nodejs_compat, R2 cache, and Images bindings
- Create open-next.config.ts with R2 incremental cache
- Remove output: 'standalone' from next.config.js (OpenNext handles bundling)
- Replace Vercel analytics with Cloudflare Web Analytics beacon in layout
- Add preview/deploy/upload scripts to package.json
- Add .open-next to .gitignore

https://claude.ai/code/session_01BWiNneRSNTNqphGMqE16o4
- Add deploy-cloudflare.yml GitHub Actions workflow with test, deploy, and
  preview jobs
- Production deploys on push to main after tests pass
- PR builds run OpenNext build for validation
- Remove vercel.json (no longer needed for Cloudflare deployment)

https://claude.ai/code/session_01BWiNneRSNTNqphGMqE16o4
@vercel
Copy link
Copy Markdown
Contributor

vercel bot commented Feb 11, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
next-blog Ready Ready Preview, Comment Feb 18, 2026 4:03am

@claude
Copy link
Copy Markdown

claude bot commented Feb 11, 2026

Code Review

I've identified 4 issues that need attention before deploying this Cloudflare Workers migration:


1. CLAUDE.md Violation: Removed Standalone Output Mode

File: next.config.js (line 3)

The removal of output: 'standalone' contradicts the documented build configuration in CLAUDE.md:105:

Output: Standalone mode (output: 'standalone' in next.config.js)

While this change appears intentional for the Cloudflare Workers migration, CLAUDE.md should be updated to reflect this architectural change.


2. Empty Sanity Project ID

File: wrangler.jsonc (line 25)

NEXT_PUBLIC_SANITY_PROJECT_ID is set to an empty string. This will break all Sanity integration because:

  • Next.js inlines NEXT_PUBLIC_* variables at build time (not runtime)
  • When @opennextjs/cloudflare build runs next build, the empty string will be baked into the bundle
  • The assertValue() check in sanity/lib/api.ts:6-12 only rejects undefined, so empty string passes
  • All Sanity API calls will fail with broken URLs like https://cdn.sanity.io/images//production/...

Fix: Replace the empty string with your actual Sanity project ID.


3. Missing Environment Variables in GitHub Workflow

File: .github/workflows/deploy-cloudflare.yml (lines 42-46, 62-66)

The build steps in both deploy and preview jobs are missing required environment variables. NEXT_PUBLIC_* variables are inlined by Next.js at build time, but the workflow only provides CLOUDFLARE_API_TOKEN and CLOUDFLARE_ACCOUNT_ID.

Why this matters:

  • The wrangler.jsonc vars are only available at Worker runtime, not during build
  • Without these values during next build, the build will fail or produce a broken bundle

Missing variables:

  • NEXT_PUBLIC_SANITY_PROJECT_ID
  • NEXT_PUBLIC_SANITY_DATASET
  • NEXT_PUBLIC_SANITY_PERSPECTIVE
  • SANITY_API_READ_TOKEN

Fix: Add these variables to the workflow env blocks, sourced from GitHub secrets and repository variables.


4. Empty Cloudflare Analytics Token

File: app/(blog)/layout.tsx (line 110)

The Cloudflare Web Analytics beacon token is hardcoded as an empty string. This means every visitor downloads beacon.min.js but no analytics data can be sent.

Fix: Either add your actual Cloudflare Web Analytics token or remove the script block until you have one.


Summary

These issues will prevent the application from functioning correctly when deployed. The most critical are:

  1. Empty Sanity project ID - breaks all CMS functionality
  2. Missing build env vars - will cause build failures or broken bundles
  3. Empty analytics token - minor issue but wastes resources

Please address these before merging.

@BayPhillips
Copy link
Copy Markdown
Owner Author

@claude review code

@claude
Copy link
Copy Markdown

claude bot commented Feb 13, 2026

Claude finished @BayPhillips's task in 55s —— View job


Code Review

  • Gather context
  • Review changed files
  • Post findings

This PR migrates the deployment from Vercel to Cloudflare Workers using the @opennextjs/cloudflare adapter. The overall approach is sound but there are several issues that need to be fixed before this can work correctly.


1. Critical: Empty Sanity Project ID

File: wrangler.jsonc line 25

"NEXT_PUBLIC_SANITY_PROJECT_ID": "",

NEXT_PUBLIC_* variables are inlined by Next.js at build time, not injected at runtime by Wrangler. The value in wrangler.jsonc vars will never be available during next build. The build step must receive these as actual environment variables. This will cause all Sanity content to fail silently.

Fix this →


2. Critical: Missing Build-Time Environment Variables in CI Workflow

File: .github/workflows/deploy-cloudflare.yml lines 42–46, 62–66

The build commands in the deploy and preview jobs only set:

env:
  CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
  CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}

Missing (all required at next build time):

  • NEXT_PUBLIC_SANITY_PROJECT_ID
  • NEXT_PUBLIC_SANITY_DATASET
  • NEXT_PUBLIC_SANITY_PERSPECTIVE
  • SANITY_API_READ_TOKEN

Without these, the CI build will fail or produce a bundle that can't fetch any CMS content. These need to be stored as GitHub Actions secrets/variables and referenced in the workflow env block.


3. Critical: NEXT_PUBLIC_SANITY_PERSPECTIVE Set to previewDrafts in Production

File: wrangler.jsonc line 27

"NEXT_PUBLIC_SANITY_PERSPECTIVE": "previewDrafts",

This will expose unpublished draft content to all public visitors. For a production deployment, this should be "published". The previewDrafts perspective is only appropriate when draft mode is enabled via a secret token.


4. Cloudflare Analytics Token Now Populated

File: app/(blog)/layout.tsx line 110

The token is now set (previous review flagged it as empty). This looks correct. Note that the beacon token is public by design and safe to include in source.


5. global_fetch_strictly_public Compatibility Flag May Break Sanity API Calls

File: wrangler.jsonc line 6

"compatibility_flags": ["nodejs_compat", "global_fetch_strictly_public"]

The global_fetch_strictly_public flag restricts fetch() to only public internet addresses, blocking requests to private IPs/localhost. While this won't affect production Sanity API calls (api.sanity.io), it's worth confirming no internal service calls are made. For the Sanity Visual Editing or preview URL secret flows, this should still work fine.


6. open-next.config.ts Uses R2 Cache but R2 Bucket May Not Exist

File: open-next.config.ts + wrangler.jsonc lines 16–21

The config enables R2 incremental cache:

incrementalCache: r2IncrementalCache,

And references a bucket named "bayphillips-cache". If this bucket hasn't been created in Cloudflare yet (wrangler r2 bucket create bayphillips-cache), the Worker will fail at runtime on any page that uses ISR/caching. Make sure this bucket is provisioned before deploying.


7. Vercel Deployment Will Still Attempt to Build

The Vercel integration is still connected (see the failed Vercel deployment comment). Deleting vercel.json alone doesn't disconnect the Vercel GitHub integration — it will keep attempting to build on every push. You'll want to either disconnect the Vercel project from GitHub in the Vercel dashboard, or add a vercel.json that ignores all branches to suppress these builds.


8. Minor: CLAUDE.md Not Updated for Architectural Change

File: CLAUDE.md (line referencing output: 'standalone')

The removal of output: 'standalone' from next.config.js (correctly done per the code comment) is not reflected in CLAUDE.md, which still states:

Output: Standalone mode (output: 'standalone' in next.config.js)


Summary

# Severity Issue
1 🔴 Critical Empty NEXT_PUBLIC_SANITY_PROJECT_ID in wrangler.jsonc
2 🔴 Critical Missing build-time env vars in CI workflow
3 🔴 Critical previewDrafts perspective set in production config
4 ✅ Fixed Cloudflare Analytics token is now populated
5 🟡 Warning global_fetch_strictly_public flag — verify no private fetches
6 🟡 Warning R2 bucket must be created before deploying
7 🟡 Warning Vercel integration still active, will keep failing
8 ⚪ Minor CLAUDE.md not updated to reflect removed standalone output

Issues 1–3 will prevent the app from functioning correctly in production. Please address these before merging.

@BayPhillips
Copy link
Copy Markdown
Owner Author

@claude fix known issues

@claude
Copy link
Copy Markdown

claude bot commented Feb 13, 2026

Claude finished @BayPhillips's task in 2m 14s —— View job


Fixed Known Issues

  • Gather context and read relevant files
  • Fix previewDraftspublished in wrangler.jsonc
  • Update CLAUDE.md for hosting change and standalone output removal
  • CI workflow env vars — cannot fix (GitHub App lacks workflow file permissions)
  • Empty NEXT_PUBLIC_SANITY_PROJECT_IDcannot fix (requires your actual Sanity project ID)

What was fixed (commit 08345ba)

wrangler.jsonc — Fixed NEXT_PUBLIC_SANITY_PERSPECTIVE from "previewDrafts" to "published". This was a critical issue that would have exposed unpublished draft content to all public visitors.

CLAUDE.md — Updated to reflect:

  • Hosting changed from Vercel to Cloudflare Workers
  • Removed reference to output: 'standalone' mode

Requires manual action

Two critical issues remain that require secrets I don't have access to:

1. NEXT_PUBLIC_SANITY_PROJECT_ID in wrangler.jsonc line 25
Fill in your actual Sanity project ID. Note that NEXT_PUBLIC_* vars are inlined at build time by Next.js — the value in wrangler.jsonc vars is only available at Worker runtime, not during next build. You need to pass this as an environment variable during the build step.

2. CI workflow missing build-time env vars (.github/workflows/deploy-cloudflare.yml)
I cannot modify workflow files. You'll need to manually add these to the env: block of both deploy and preview build steps:

env:
  CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
  CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
  NEXT_PUBLIC_SANITY_PROJECT_ID: ${{ vars.NEXT_PUBLIC_SANITY_PROJECT_ID }}
  NEXT_PUBLIC_SANITY_DATASET: ${{ vars.NEXT_PUBLIC_SANITY_DATASET }}
  NEXT_PUBLIC_SANITY_PERSPECTIVE: ${{ vars.NEXT_PUBLIC_SANITY_PERSPECTIVE }}
  SANITY_API_READ_TOKEN: ${{ secrets.SANITY_API_READ_TOKEN }}

Store NEXT_PUBLIC_SANITY_* as repository variables and SANITY_API_READ_TOKEN as a repository secret in GitHub → Settings → Secrets and variables → Actions.
|

…loyment

- Fix NEXT_PUBLIC_SANITY_PERSPECTIVE from previewDrafts to published in wrangler.jsonc

- Update CLAUDE.md to reflect Cloudflare Workers hosting (was Vercel)

- Update CLAUDE.md build configuration to remove standalone mode reference

Co-authored-by: Bay Phillips <BayPhillips@users.noreply.github.com>
@BayPhillips
Copy link
Copy Markdown
Owner Author

@claude review

@claude
Copy link
Copy Markdown

claude bot commented Feb 13, 2026

Claude finished @BayPhillips's task in 52s —— View job


Code Review

  • Gather context
  • Review changed files
  • Post findings

This PR migrates deployment from Vercel to Cloudflare Workers. Previous critical issues have been addressed. Here's the current state:

Previously Fixed ✅

  • NEXT_PUBLIC_SANITY_PERSPECTIVE changed from "previewDrafts" to "published"
  • NEXT_PUBLIC_SANITY_PROJECT_ID is now populated (mxq13g5m)
  • CI workflow now includes all required build-time env vars in both deploy and preview jobs
  • CLAUDE.md updated for Cloudflare hosting
  • Vercel analytics/speed-insights removed and replaced with Cloudflare beacon

Remaining Issues

1. preview job doesn't actually deploy a preview

.github/workflows/deploy-cloudflare.yml lines 55–66

The preview job only runs opennextjs-cloudflare build — it never deploys anywhere. On PRs you get a build check but no live preview URL. Consider using wrangler versions upload to deploy to a preview environment, or just dropping the preview job if preview deploys aren't needed yet.

2. test job is missing build-time env vars

.github/workflows/deploy-cloudflare.yml lines 12–21

The test job runs npm test without any environment variables. If any test imports from sanity/lib/api.ts, which calls assertValue(process.env.NEXT_PUBLIC_SANITY_PROJECT_ID, ...), those tests will fail in CI. Looking at the test file changes, the Sanity fetch tests mock the client, so this may not be a problem in practice — but worth verifying.

3. jest.config.ts — config merging change

jest.config.ts line 8

The config changed from:

const config: Config = {
  ...createJestConfig(),  // spreads Next.js defaults inline

to:

export default createJestConfig(customConfig)  // passes custom config to Next.js

This is actually the correct approach per next/jest docs. The spread approach could override Next.js-specific transforms with incorrect values. This is a good fix.

4. tsconfig.json excludes test directories

tsconfig.json lines 45–47

"exclude": ["node_modules", "jest.config.ts", "__tests__", "tests"]

Excluding __tests__ from tsconfig means TypeScript won't type-check your test files during tsc or IDE usage. Test files losing type safety is a tradeoff — consider whether this is intentional. If the goal was just to avoid build errors, you may prefer keeping the type-checking and using jest's own tsconfig instead.

5. R2 bucket must be provisioned manually

wrangler.jsonc lines 16–21 + open-next.config.ts

The R2 incremental cache is configured but bayphillips-cache bucket creation is not part of any automated setup. If the bucket doesn't exist when the Worker first runs, ISR/cached pages will fail at runtime. Make sure to run:

wrangler r2 bucket create bayphillips-cache

before the first deployment. A note in CLOUDFLARE_DEPLOYMENT_PLAN.md or README would help future deployments.

6. Cloudflare Images binding may not be provisioned

wrangler.jsonc lines 11–13

"images": { "binding": "IMAGES" }

Cloudflare Images is a paid add-on (different from R2). If this account doesn't have Cloudflare Images enabled, the Worker will fail to bind and won't deploy. Verify the account has Cloudflare Images, or remove this binding if image optimization isn't actively used yet.

7. NEXT_PUBLIC_SANITY_PROJECT_ID is hardcoded in wrangler.jsonc

wrangler.jsonc line 25

The Sanity project ID is now in the vars section of wrangler.jsonc (committed to source), and also expected in GitHub Actions via ${{ vars.NEXT_PUBLIC_SANITY_PROJECT_ID }}. The wrangler.jsonc value is the runtime fallback — but since NEXT_PUBLIC_* vars are inlined at build time, the wrangler.jsonc value is never actually read by the Next.js app at runtime. This is fine architecturally (the CI env vars are what matter), but the hardcoded project ID in source is slightly redundant. Not a blocking issue.


Summary

# Severity Status Issue
✅ Fixed Empty NEXT_PUBLIC_SANITY_PROJECT_ID
✅ Fixed previewDrafts in production
✅ Fixed Missing build-time env vars in CI
1 🟡 Warning Open preview job doesn't deploy anywhere
2 🟡 Warning Open test job missing env vars (likely harmless)
3 ✅ Good jest.config.ts merging now done correctly
4 🟡 Warning Open __tests__ excluded from tsconfig
5 🔴 Critical Open R2 bucket must be created before first deploy
6 🔴 Critical Open Cloudflare Images binding needs account-level provisioning
7 ⚪ Minor Open NEXT_PUBLIC_SANITY_PROJECT_ID hardcoded in wrangler.jsonc

Issues 5 and 6 are infrastructure prerequisites that will prevent the Worker from starting if not addressed. Everything else is either already fixed or low priority.

Deploy PR-specific workers (bayphillips-blog-pr-{number}) and post
preview URLs as comments on pull requests. Automatically clean up
preview workers when PRs are closed.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Remove app/(sanity)/ route group (studio now deployed separately via npx sanity deploy)
- Fix R2 bucket binding name (INCREMENTAL_CACHE → NEXT_INC_CACHE_R2_BUCKET)
- Add deployment.appId to sanity.cli.ts to avoid interactive prompts

This reduces the Cloudflare Worker bundle from >3MB to ~7MB (under the limit),
enabling deployment on the free tier without upgrading.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Feb 14, 2026

🚀 Preview Deployment

Status URL
✅ Deployed https://bayphillips-blog-pr-17.bay-phillips.workers.dev

Worker: bayphillips-blog-pr-17
Commit: c90c6a3e4181ca3c564c2cb2e0ba08c2635a3d00

This preview will be automatically cleaned up when the PR is closed.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Preview workers are created with a unique name, so they don't inherit
secrets from the main worker. This adds a step to set the secret
after deploying the preview worker.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Remove legacyBehavior/passHref from Link, use NavigationMenuLink asChild pattern
- Destructure arrowHidden from props to prevent it being spread to DOM

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants