<- Back to Software Development

Cloudflare Deployment Models: Pages, Workers, and Static Export

June 7, 20268 min read
Share

Cloudflare deployment can feel confusing because Pages, Workers, Wrangler, OpenNext, and Next.js static export appear close to each other in the UI and documentation. They are related, but they are not the same deployment model. The first mistake is treating them as one thing.

Short Answer

For a simple personal blog built with Next.js and static content, the cleanest deployment path is usually:

Next.js output: "export"
→ npm run build
→ out/
→ Cloudflare Pages deploys out/

This means the site is deployed as static files.

You usually do not need:

OpenNext
Workers SSR
@opennextjs/cloudflare
wrangler deploy

Those tools are useful when you want to run server-side logic on Cloudflare's runtime. A static blog does not need that by default.

Why This Feels Confusing

Cloudflare puts several related products under a similar deployment experience:

NameWhat It Mainly DoesMental Model
Cloudflare PagesHosts frontend sites and static output foldersDeploy files
Cloudflare WorkersRuns JavaScript or TypeScript at the edgeRun code
WranglerCLI for Cloudflare Workers and related resourcesDeploy and configure runtime
OpenNextAdapter for running Next.js server features outside VercelConvert Next.js server output
Next.js static exportGenerates plain HTML, CSS, and JS filesRemove the server

The confusion happens because a Next.js project can be deployed in more than one way.

The same framework can produce either:

static files

or:

server-rendered application output

Cloudflare needs to know which model you are using.

Pages Means Deploy Files

Cloudflare Pages is mainly for frontend websites.

Typical examples:

React static site
Vite app
Next.js static export
Astro static site
personal blog
documentation site
landing page

The core flow is simple:

GitHub repo
→ build command runs
→ static output folder is generated
→ Cloudflare deploys that folder to its CDN

So Pages mostly cares about two things:

QuestionExample Answer
What is the build command?npm run build
What is the build output folder?out
Where is the frontend project?frontend

For a Next.js static export project, the important output is the out folder.

Run this from the frontend directory to generate the static export output and check whether the site can build locally:

npm run build

After the build succeeds, the expected result is usually:

frontend/
  out/
    index.html
    _next/
    posts/

This is the folder Cloudflare Pages should deploy.

Workers Means Run Code

Cloudflare Workers is different. Workers is not mainly about uploading an out folder. It is about running code when a request reaches Cloudflare.

The request flow looks like this:

request arrives
→ Cloudflare executes Worker code
→ Worker returns a response

Workers are useful for:

API server
edge middleware
authentication logic
proxy
webhook handling
SSR rendering
rate limit
cron job
KV / R2 / D1 / Durable Objects integration

Workers care about different things:

QuestionExample
What is the Worker entry file?src/index.ts
What does wrangler.toml configure?routes, bindings, compatibility date
What resources are bound?KV, D1, R2, Durable Objects
What code runs per request?fetch handler, middleware, API logic

That is why Workers feel more like backend or edge application deployment.

The short distinction is:

Pages = deploy files
Workers = run code

Static Export Means No Server Runtime

In Next.js, this config changes the deployment model:

const nextConfig = {
  output: "export",
};

export default nextConfig;

output: "export" tells Next.js to generate static files during build time.

The output is similar to:

out/
  index.html
  about.html
  posts/example.html
  _next/static/...

The request flow becomes:

user opens /posts/rag
→ Cloudflare CDN returns a generated HTML file
→ no server render
→ no Node.js server
→ no OpenNext runtime

This is why static export fits a blog well.

A blog usually has pages that can be known at build time:

home page
about page
post list
individual markdown or MDX posts
images
static assets

The benefit is operational simplicity:

Fast

Static files can be served directly from the CDN without waiting for server-side rendering.

Cheap

There is no application server running for every request.

Stable

Fewer runtime components means fewer deployment and compatibility failures.

Simple

The main deployment question becomes whether the build output folder is correct.

SSR Means Request-Time Rendering

SSR means Server-Side Rendering.

With SSR, the page is not fully generated ahead of time. The server generates the HTML when the user makes a request.

The flow looks like this:

user opens /dashboard
→ request reaches a server or Worker
→ server checks identity or queries data
→ server renders HTML
→ response returns to the user

SSR is useful when the page depends on request-time information:

logged-in user dashboard
admin page
profile page
user orders
permission-based content
real-time database result
cookie or session logic

For example, /dashboard may show different data to different users. That page should not be exported as one static HTML file shared by everyone.

The trade-off is that SSR needs a runtime.

On Cloudflare, that usually means using Cloudflare Workers with an adapter such as OpenNext.

Static Export vs SSR

AreaStatic ExportSSR
Generated atBuild timeRequest time
OutputHTML, CSS, JS filesServer/runtime output
Runtime neededNoYes
Good forBlog, docs, portfolioDashboard, SaaS, admin
Reads cookies on serverNoYes
Server-side database queryNoYes
Deployment complexityLowerHigher
Cloudflare targetPagesWorkers or Pages with Functions/runtime support

Static export is not weaker by default. It is just optimized for a different type of site.

For a content-heavy blog, static export is often the better engineering choice because the page content does not need to change per request.

Why OpenNext Appears

Next.js was originally optimized heavily around Vercel's deployment platform. When you want to run Next.js server features on Cloudflare Workers, the output needs to be adapted.

That is where tools such as these appear:

OpenNext
@opennextjs/cloudflare
next-on-pages

Their job is not to deploy a plain static export folder.

Their job is closer to:

take Next.js server output
→ adapt it to Cloudflare runtime
→ deploy it as Worker-compatible code

So these two directions should not be mixed:

output: "export"

means:

I do not want a server. Give me static files.

OpenNext means:

I need Next.js server behavior to run on Cloudflare.

When these two mental models are mixed, deployment errors become hard to understand because the platform may think you are deploying a full-stack SSR app while your code is actually prepared as a static site.

The Practical Decision Rule

Use this decision rule for a blog project:

RequirementRecommended Direction
Static posts from markdown or MDXNext.js static export + Cloudflare Pages
Personal blog without loginNext.js static export + Cloudflare Pages
Landing pageCloudflare Pages
Documentation siteCloudflare Pages
API endpointsWorkers API or Pages Functions
User login and dashboardSSR or separate backend
Admin panel with database writesSSR, Workers, or separate backend
Request-time personalizationSSR with runtime

For the current blog case, the recommended setup is:

Next.js static export
Cloudflare Pages
Build command: npm run build
Output directory: out

If the project root on Cloudflare is the repository root but the app is inside frontend, then the output directory may need to be configured as:

frontend/out

If Cloudflare runs the build from the frontend directory, then the output directory should usually be:

out

Common Mistakes

Mistake 1: Using Workers for a Static Blog

A static blog does not need a Worker runtime unless it has backend logic.

Mistake 2: Mixing OpenNext with Static Export

OpenNext is for server-capable Next.js deployment. Static export is for removing the server.

Mistake 3: Wrong Output Folder

If the build creates frontend/out but Cloudflare deploys out from the repository root, the deployment may fail or deploy nothing useful.

Mistake 4: Using the Wrong Build Command

npx run build is not the normal command. For npm projects, the usual build command is npm run build.

The safest debugging sequence is to first prove the local build works, then map the local output folder to Cloudflare's output directory setting.

Run this from the same directory Cloudflare uses as the build root to confirm the build script exists and runs correctly:

npm run build

If the build succeeds locally, inspect where the output folder is created:

out/

or:

frontend/out/

That exact folder is the one Pages should deploy.

The Main Principle

Do not start by choosing a Cloudflare button. Start by identifying the deployment model.

For a static blog, the model is:

build once
generate files
deploy files
serve from CDN

For a full-stack or SSR app, the model is:

receive request
run server code
generate response
return response

Most of the confusion disappears once the distinction is clear:

Pages deploys files.
Workers run code.
Static export removes the server.
OpenNext helps when the server is still needed.

Cloudflare 部署会让人觉得很乱,是因为 Pages、Workers、Wrangler、OpenNext、Next.js static export 在 UI 和文档里经常一起出现。它们有关联,但不是同一种部署模型。真正的问题不是某个工具很难,而是你一开始没有区分清楚自己到底是在“部署文件”还是“运行代码”。

简短答案

如果你的项目是一个简单的个人 blog,使用 Next.js 加 markdown / MDX 静态文章,那么最清晰的部署路线通常是:

Next.js output: "export"
→ npm run build
→ out/
→ Cloudflare Pages 部署 out/

这代表你的站点是静态文件部署。

你通常不需要:

OpenNext
Workers SSR
@opennextjs/cloudflare
wrangler deploy

这些工具更适合你要在 Cloudflare runtime 上运行 server-side logic 的情况。单纯的静态 blog 默认不需要它们。

为什么这件事很 Confusing

Cloudflare 把几个相关产品放在相似的部署流程里:

名称主要用途心智模型
Cloudflare Pages部署前端网站和静态输出目录Deploy files
Cloudflare Workers在 edge 执行 JavaScript / TypeScriptRun code
WranglerCloudflare 的 CLI,用来部署和配置 runtimeDeploy and configure runtime
OpenNext让 Next.js server output 能跑在非 Vercel 平台Convert Next.js server output
Next.js static export生成纯 HTML、CSS、JS 文件Remove the server

混乱的原因是:同一个 Next.js 项目可以有不同的部署方式。

它可以变成:

静态文件

也可以变成:

需要 server runtime 的 SSR 应用

Cloudflare 需要知道你是哪一种。

Pages 是 Deploy Files

Cloudflare Pages 主要是部署前端网站。

常见场景包括:

React static site
Vite app
Next.js static export
Astro static site
个人 blog
文档站
landing page

它的核心流程很简单:

GitHub repo
→ 执行 build command
→ 生成静态输出目录
→ Cloudflare 把这个目录部署到 CDN

所以 Pages 最关心的是:

问题例子
build command 是什么?npm run build
build output folder 是什么?out
frontend 项目在哪里?frontend

对于 Next.js static export 项目,关键输出目录就是 out

frontend 目录运行这个命令,用来生成静态输出并确认本地 build 能不能成功:

npm run build

成功后,常见结果会像这样:

frontend/
  out/
    index.html
    _next/
    posts/

这个 out 目录才是 Cloudflare Pages 应该部署的东西。

Workers 是 Run Code

Cloudflare Workers 是另一回事。Workers 不是单纯上传一个 out 文件夹,而是在 request 到达 Cloudflare 时执行代码。

它的请求流程是:

request arrives
→ Cloudflare 执行 Worker code
→ Worker 返回 response

Workers 适合这些场景:

API server
edge middleware
authentication logic
proxy
webhook handling
SSR rendering
rate limit
cron job
KV / R2 / D1 / Durable Objects integration

Workers 关心的问题也不一样:

问题例子
Worker entry file 是什么?src/index.ts
wrangler.toml 配什么?routes, bindings, compatibility date
绑定哪些资源?KV, D1, R2, Durable Objects
每个 request 执行什么代码?fetch handler, middleware, API logic

所以 Workers 更像 backend 或 edge application 的部署方式。

最简单的区别就是:

Pages = deploy files
Workers = run code

Static Export 代表不需要 Server Runtime

在 Next.js 里,这个配置会改变部署模型:

const nextConfig = {
  output: "export",
};

export default nextConfig;

output: "export" 的意思是:Next.js 在 build time 直接生成静态文件。

输出结果通常类似:

out/
  index.html
  about.html
  posts/example.html
  _next/static/...

请求流程会变成:

用户打开 /posts/rag
→ Cloudflare CDN 直接返回生成好的 HTML
→ 没有 server render
→ 没有 Node.js server
→ 没有 OpenNext runtime

所以 static export 很适合 blog。

一个 blog 的页面通常可以在 build time 就确定:

首页
about 页面
文章列表
单篇 markdown / MDX 文章
图片
静态资源

它的好处是部署简单:

静态文件可以直接从 CDN 返回,不需要等待 server-side rendering。

便宜

每个请求不需要启动或执行应用服务器逻辑。

稳定

runtime 组件越少,部署和兼容性问题通常越少。

简单

主要部署问题变成:build 是否成功,以及 output folder 是否配置正确。

SSR 是 Request-Time Rendering

SSR 是 Server-Side Rendering。

意思是页面不是全部提前生成,而是在用户请求时由 server 生成 HTML。

流程像这样:

用户打开 /dashboard
→ request 打到 server 或 Worker
→ server 检查身份或查询数据
→ server render HTML
→ response 返回给用户

SSR 适合页面依赖 request-time information 的情况:

登录后的 dashboard
admin 页面
profile 页面
user orders
基于权限显示的内容
实时数据库结果
cookie 或 session 逻辑

例如 /dashboard 每个用户看到的数据可能不同。这种页面就不适合导出成一个所有人共享的静态 HTML。

代价是 SSR 需要 runtime。

在 Cloudflare 上,这通常意味着你要使用 Cloudflare Workers,再搭配类似 OpenNext 的 adapter。

Static Export vs SSR

项目Static ExportSSR
生成时间Build timeRequest time
输出结果HTML、CSS、JS 文件Server/runtime output
是否需要 runtime不需要需要
适合场景Blog、docs、portfolioDashboard、SaaS、admin
server-side 读取 cookie不可以可以
server-side 查询数据库不可以可以
部署复杂度较低较高
Cloudflare 目标PagesWorkers 或带 runtime 的 Pages

Static export 不是比较弱。它只是适合另一种类型的网站。

对于内容型 blog 来说,static export 反而经常是更好的工程选择,因为文章内容不需要每次 request 都重新生成。

为什么会出现 OpenNext

Next.js 原本很大程度上围绕 Vercel 的部署环境优化。如果你想把 Next.js 的 server features 跑在 Cloudflare Workers 上,就需要把 Next.js 的 server output 转成 Cloudflare runtime 能理解的结构。

这就是这些工具出现的原因:

OpenNext
@opennextjs/cloudflare
next-on-pages

它们的目标不是部署一个普通的 static export 文件夹。

它们更像是在做这件事:

拿到 Next.js server output
→ 转换成 Cloudflare runtime 可运行的结构
→ 部署成 Worker-compatible code

所以这两个方向不要混在一起:

output: "export"

代表:

我不要 server,给我静态文件。

OpenNext 代表:

我需要 Next.js server behavior 跑在 Cloudflare 上。

如果把这两个模型混在一起,错误就会很难看懂。Cloudflare 可能以为你在部署 full-stack SSR app,但你的代码其实是准备作为 static site 输出。

实际选择规则

对于 blog 项目,可以直接用这个判断表:

需求推荐方向
markdown / MDX 静态文章Next.js static export + Cloudflare Pages
没有登录的个人 blogNext.js static export + Cloudflare Pages
Landing pageCloudflare Pages
文档站Cloudflare Pages
API endpointsWorkers API 或 Pages Functions
用户登录和 dashboardSSR 或独立 backend
有数据库写入的 admin panelSSR、Workers 或独立 backend
request-time personalizationSSR with runtime

对于当前 blog,更合理的配置是:

Next.js static export
Cloudflare Pages
Build command: npm run build
Output directory: out

如果 Cloudflare 的 project root 是 repository root,但你的 app 在 frontend 里面,那么 output directory 可能要设成:

frontend/out

如果 Cloudflare 是从 frontend 目录开始 build,那么 output directory 通常就是:

out

常见错误

错误 1:静态 Blog 却用 Workers

纯静态 blog 不需要 Worker runtime,除非你有 backend logic。

错误 2:把 OpenNext 和 Static Export 混用

OpenNext 是为了部署带 server behavior 的 Next.js。Static export 是为了移除 server。

错误 3:Output Folder 配错

如果 build 生成的是 frontend/out,但 Cloudflare 从 repo root 部署 out,那就可能部署失败或部署到错误内容。

错误 4:Build Command 写错

npx run build 不是正常的 npm build 命令。npm 项目通常应该用 npm run build

最安全的 debug 顺序是:先证明本地 build 成功,再把本地实际生成的 output folder 对应到 Cloudflare Pages 的 output directory 设置。

在 Cloudflare 实际使用的 build root 目录运行这个命令,用来确认 build script 存在并且能正常执行:

npm run build

如果本地 build 成功,再检查输出目录到底在哪里:

out/

或:

frontend/out/

Cloudflare Pages 应该部署的就是这个准确目录。

核心原则

不要一开始就选 Cloudflare 某个按钮。先判断你的部署模型。

如果是 static blog,模型是:

build once
generate files
deploy files
serve from CDN

如果是 full-stack 或 SSR app,模型是:

receive request
run server code
generate response
return response

大部分 confusion 都来自没有先分清楚这件事:

Pages deploys files.
Workers run code.
Static export removes the server.
OpenNext helps when the server is still needed.