A Next.js 15+ application demonstrating Server-Side Rendering (SSR) and API routes deployed on Cloudflare Workers using OpenNext.
- ✅ Next.js 15+ with App Router
- ✅ Server-Side Rendering (SSR) with real-time data
- ✅ API Routes with full Node.js runtime support
- ✅ TypeScript for type safety
- ✅ Tailwind CSS for styling
- ✅ Cloudflare Workers deployment via OpenNext
- ✅ Full Node.js API compatibility (not Edge Runtime)
src/
├── app/
│ ├── api/
│ │ └── server-info/ # API route returning server metadata
│ │ └── route.ts
│ ├── ssr-demo/ # SSR demonstration page
│ │ ├── components/
│ │ │ └── ApiDataFetcher.tsx
│ │ └── page.tsx
│ ├── globals.css
│ ├── layout.tsx # Root layout component
│ └── page.tsx # Home page
└── types/
└── api.ts # TypeScript type definitions
- Node.js 20 or higher (required for @opennextjs/cloudflare)
- npm or yarn package manager
-
Install dependencies:
pnpm install
-
Start development server:
pnpm run dev
-
Open in browser:
http://localhost:3000
-
pnpm install --save-dev wrangler@latest -
pnpm install @opennextjs/cloudflare@latest -
Create a
wrangler.jsoncfile Doc Link -
Add an
open-next.config.tsfile -
Add a
.dev.varsfile with contentNEXTJS_ENV=development
You can also add the following lines to .gitignore
# Cloudflare
cloudflare-env.d.ts
/wrangler.toml.backup
/wrangler.jsonc.backup
- Update the package.json file
- Add Static Asset Caching
Add a public/_headers file, with the following headers at the least:
/_next/static/*
Cache-Control: public,max-age=31536000,immutable
-
Add caching with Cloudflare R2
-
Remove any export const runtime = "edge"; if present (just verify)
-
Add .open-next to .gitignore
-
Remove static
@cloudflare/next-on-pages(if necessary/just verify) -
Develop locally (add
initOpenNextCloudflareForDev()block innext.config.js) -
Deploy to Cloudflare Workers (via
pnpm run deploy; manual deploy vianpx wrangler deploy --config wrangler.jsonc)
👉🏼 CLOUDFLARE_API_TOKEN and CLOUDFLARE_ACCOUNT_ID are required at GH Actions for deployment
Goto https://dash.cloudflare.com/profile/api-tokens to generate a token for CLOUDFLARE_API_TOKEN
CLOUDFLARE_ACCOUNT_ID can be found in the URL of the dashboard (dashboard.cloudflare.com/[id]) or in CF Account Home > Overflow menu > Copy Account ID
Goto GH > Your Repo > Settings > Secrets > New repository secret > CLOUDFLARE_API_TOKEN / CLOUDFLARE_ACCOUNT_ID
✅ Put non-sensitive CF variables in wrangler.jsonc
✅ CF Secrets are set via wrangler secret put
✅ Ensure public variables start with NEXT_PUBLIC_
- Home (
/) - Landing page with navigation - SSR Demo (
/ssr-demo) - Server-side rendering demonstration - API Endpoint (
/api/server-info) - Server information API
Endpoint: GET /api/server-info
Response:
{
"success": true,
"data": {
"serverId": "uuid-string",
"serverTime": "2024-01-01T00:00:00.000Z",
"requestId": "uuid-string",
"environment": "development",
"timestamp": 1704067200000
},
"metadata": {
"requestTime": "2024-01-01T00:00:00.000Z",
"responseTime": "2024-01-01T00:00:00.000Z"
}
}-
Build and deploy:
pnpm run deploy
This command uses
@opennextjs/cloudflareto:- Build the Next.js application
- Transform it for Cloudflare Workers
- Deploy using Wrangler
-
Manual deployment:
# Build the project pnpm run build # Deploy with OpenNext npx @opennextjs/cloudflare@latest # Or deploy with Wrangler npx wrangler deploy
The project is configured for OpenNext deployment:
wrangler.jsonc- Cloudflare Workers configurationnext.config.js- Next.js configuration optimized for OpenNext- Node.js Runtime - Full Node.js API support (not Edge Runtime)
Compared to @cloudflare/next-on-pages:
- ✅ Full Node.js Runtime - Complete Node.js API compatibility
- ✅ No API Restrictions - Use any Node.js modules
- ✅ Better Feature Support - All Next.js 15 features supported
- ✅ Simplified Configuration - Less setup complexity
pnpm run dev # Start development server
pnpm run build # Build for production
pnpm run start # Start production server
pnpm run lint # Run ESLint
pnpm run deploy # Deploy to Cloudflare WorkersSet in wrangler.toml or Cloudflare dashboard:
[vars]
ENVIRONMENT = "production"
SERVER_ID = "your-server-id"MIT