Skip to content

Cloudflare - Pages Functions ​

前言 ​

首先我们来讲解一下平时开发的时候 dev server 的相关流程

pnpm dev 时的调用流程(典型情况) ​

  1. 请求发起(浏览器 → Vite dev server 或 直接到目标)
  • 前端运行在 Vite 提供的开发服务器(默认 http://localhost:5173)。
  • 发起请求的 URL 取决于代码中的前缀写法:
    • 如果是相对路径或以 /api 开头(例如 fetch('/api/run')),浏览器会向当前 origin(localhost:5173)发起请求。
    • 如果是完整外部地址(例如 fetch('https://api.example.com/run')),浏览器会直接向该地址发起请求(会遭遇 CORS 限制,除非远端允许)。
  1. Vite 的 proxy(vite.config.ts 的 server.proxy)的作用
  • 当你在 vite.config.ts 中配置了:
js
 server: {
  proxy: {
    '/api': {
      target: 'https://api.example.com',
      changeOrigin: true,
      rewrite: (path) => path.replace(/^\/api/, ''),
    }
  }
}
  • 浏览器请求 http://localhost:5173/api/run 时,Vite dev server 会拦截该请求并在开发服务器端将它转发到 https://api.example.com/run(或按 rewrite 后的路径)。
  • 代理可修改请求头、转发 body,并避免浏览器端 CORS(因为浏览器只看到和 Vite 的同源请求)。
  • 代理不会改变客户端代码里的 API_PREFIX 值——它只是对匹配到的网络请求“替身”转发。
  1. 前缀(API_PREFIX)从哪里来(在 Vite dev)
  • 常见来源:
    • 构建时注入:import.meta.env.VITE_API_PREFIX(在 dev 模式同样可用)——Vite 在运行时把 import.meta.env 暴露给客户端,值来自 .env 文件(详见 env 部分)。
    • 硬编码:直接写 '/api' 或 '/backend'。
    • 运行时注入(少见):通过 <script>window.__CONFIG__ = {...}</script> 在 index.html 中注入并在客户端读取(优点:可在不重建的情况下改变)。
  1. 如果我们希望在dev阶段通过vite server来代理添加header(比如在network里面隐藏token),可以按照下面这样做
js
import { defineConfig, loadEnv} from 'vite'
import react from '@vitejs/plugin-react'
import path from 'path'
import type { ProxyOptions } from 'vite';

export default defineConfig(({mode})=>{
  const env = loadEnv(mode, process.cwd())
  return {
  plugins: [react()],
  resolve: {
    alias: {
      '@': path.resolve(__dirname, './src'),
    },
  },
  build: {
    target: 'esnext',
  },
  esbuild: {
    target: 'esnext',
  },
  optimizeDeps: {
    esbuildOptions: {
      target: 'esnext',
    },
  },
  define: {
    __APP_CONFIG__: {},
  },
  server: {
      proxy: {
      // 任何 /api/rodin 的请求都会被本地 dev-server 转发并注入 Authorization
      '/api': {
        target: 'https://api.hyper3d.com',
        changeOrigin: true,
        secure: true,
        // configure 用来访问底层的 http-proxy 实例并注册事件
        configure: (proxy, options) => {
          // proxy 是 http-proxy 实例(来自 http-proxy-middleware / http-proxy)
          proxy.on('proxyReq', (proxyReq, req, res) => {
            // 仅在本地 dev 时把 token 加到发往上游的请求里
            if (env.VITE_RODIN_TOKEN) {
              proxyReq.setHeader('authorization', `Bearer ${env.VITE_RODIN_TOKEN}`);
            }
          });
        }
      } as ProxyOptions
    }
  },
}
})

wrangler pages dev ./dist(用本地构建的 dist 由 wrangler Pages/Workers 提供)时的调用流程 ​

浏览器发出的请求,只要包含 token,就会在 DevTools 的 Network 里可见;要“看不见”就必须让 token 不从浏览器发出,而由服务端(Pages Function / dev proxy)代为添加并转发到上游。Pages Functions 可以通过绑定的 secrets / environment variables 在运行时读取 token 并注入。

说明:wrangler pages dev ./dist 会把 ./dist 当作 Pages 的静态站点来服务;同时 Cloudflare Pages 支持 Functions(Worker) 用于处理比如 /api/* 的请求;wrangler 能把你的 Worker 本地运行并与 Pages 集成 — 行为取决于你的 wrangler.toml、functions 目录或 routes 配置。

  1. 客户端(浏览器)发起请求 —— 路径解析
  • 编译后的前端代码(dist)里 URL 是构建时已确定的:
    • 如果你在源码中使用 import.meta.env.VITE_API_PREFIX,这些值在构建时就已被内联到 JS 文件里(例如替成 "/api" 或 "https://api.example.com")。
    • 因此,当 wrangler pages dev 提供 index.html 与静态资源时,浏览器会按构建后固化的 URL 发起请求。
  • 关键点:在 dist 模式下要改变 API 前缀通常需要重新构建,除非你采用运行时配置(window.CONFIG)手段。
  1. 如何被转发 / 由谁处理
  • 可能的处理方式:
    • Pages Functions(或 Worker)处理 /api/*:如果你的项目在 Pages 设置了 Functions(例如 functions/ 下的 Worker 脚本)并且 route 规则匹配 /api/*,那么 wrangler pages dev 会在本地用 Worker 拦截这些请求并执行函数逻辑(可以调用外部 API、注入 secrets 等)。这时浏览器请求到达本地 wrangler,然后由 Worker 代为 fetch() 到外部服务并把响应返回给浏览器(Worker 能安全读取绑定的环境变量/Secrets)。
    • 直接请求外部主机:如果构建时 API_PREFIX 指向 https://api.example.com,浏览器会直接向外部主机发起请求(这受远端 CORS 控制)。
    • wrangler 本身没有自动代理到外部:不像 Vite dev proxy,wrangler pages dev 并不会自动为 /api 做远端代理,除非你明确创建了一个 Worker/Function 去转发。
  • 总结:要在 wrangler pages dev 下做到“本地端统一入口并转发 API、隐藏 token”,你通常需要实现一个 Worker(Pages Function)来处理 /api/* 并在 Worker 中用 fetch() 调用真实后端。Worker 可以从 wrangler.toml 的 env / secrets 中读取敏感值(不会暴露给客户端)。
  1. Env 与 secrets 的获取
  • 客户端 env(构建时):import.meta.env.VITE_* 在构建时被替换成字面量,dist 中包含这些值。wrangler pages dev ./dist 无法在运行时替换这些值(除非你在页面里使用运行时注入的 window.CONFIG)。
  • Worker/Pages Function 的 env / secret:
    • 在 wrangler.toml 中或通过 wrangler secret put 设置的 secret,会以绑定的形式传入 Worker 的 env(或 global)。在 Worker 代码中可以访问并用于转发请求(例如 env.API_KEY)。
    • 这些对浏览器不可见(安全),适合在 Worker 里注入 Authorization header 后再 fetch 到后端。
    • 示例 Worker 转发伪代码:
js
    export default {
  async fetch(request, env) {
    const url = new URL(request.url);
    if (url.pathname.startsWith('/api/')) {
      const upstream = `https://api.example.com${url.pathname.replace(/^\/api/, '')}`;
      const res = await fetch(upstream, {
        method: request.method,
        headers: {
          'Authorization': `Bearer ${env.API_SECRET}`,
          // copy other headers as needed
        },
        body: request.body
      });
      return res;
    }
    return fetch(request); // or return static asset
  }
}

📁 文件结构 ​

mulerun-agent/
├── functions/
│   └── [[path]].ts              ← 统一 API 网关 (新建)
├── .dev.vars                    ← 本地 token 配置(不提交 Git)
├── .env.development             ← 前端本地 env(已更新)
├── .env.production              ← 前端生产 env(已更新)
├── .gitignore                   ← 添加 .dev.vars(已更新)
├── QUICK_START.md               ← 快速启动指南(新建)
├── PAGES_FUNCTION_GUIDE.md      ← 完整使用文档(新建)
└── src/
    └── pages/Home.tsx           ← 调用 API 的示例位置

✅ 已完成的优化 ​

1. 多 Provider 路由支持 ​

新建统一的根路由文件 functions/[[path]].ts,支持:

  • ✅ /api/v2/* → RODIN API (Bearer token)
  • ✅ /sessions/metering* → MULE API (x-api-key)
  • ✅ 自动 CORS 处理
  • ✅ 完整的调试日志

关键优势:

  • 不需要 /api 前缀也能被拦截(/sessions/metering 直接路由)
  • 每个 provider 独立管理 token,便于权限隔离
  • 易于扩展新的 provider

2. 安全的环境变量管理 ​

文件用途Git安全性
.dev.vars本地 wrangler 环境❌ 不提交🔐 本机仅有
.env.development前端本地构建✅ 提交🟡 非敏感数据
.env.production前端生产构建✅ 提交🟡 非敏感数据
Cloudflare Secrets线上 token 存储N/A🔐 加密存储

3. 完整的调试与文档 ​

  • 📄 QUICK_START.md - 一键启动指南
  • 📄 PAGES_FUNCTION_GUIDE.md - 完整部署与使用文档
  • 📝 代码注释 - Pages Function 中详细的逻辑说明
  • 🔍 结构化日志 - [Request]、[Proxy]、[Response] 便于排查

🔄 工作流总结 ​

本地开发 ​

.dev.vars (RODIN_API_TOKEN, MULE_AGENT_KEY)
    ↓
pnpm build
    ↓
pnpm wrangler pages dev ./dist
    ↓
浏览器访问 http://localhost:8788
    ↓
前端请求 /api/v2/rodin 或 /sessions/metering
    ↓
Pages Function 拦截并根据路径转发到对应 provider
    ↓
Token 由服务端添加,浏览器无法看到 ✅

生产部署 ​

编辑 .env.production 中的 VITE_API_URL
    ↓
在 Cloudflare Pages UI 配置 Secrets:
  - RODIN_API_BASE, RODIN_API_TOKEN
  - MULE_API_BASE, MULE_AGENT_KEY
    ↓
pnpm build && wrangler pages deploy ./dist
或 Git 自动部署
    ↓
Cloudflare Pages 运行 functions/[[path]].ts
    ↓
前端请求到 https://yoursite.pages.dev/api/v2/rodin
    ↓
Pages Function 从 Secrets 读取 token,转发请求
    ↓
Token 安全隐藏 ✅

📋 前端代码使用示例 ​

RODIN API 调用 ​

typescript
import { request } from '@/services'

// 调用 RODIN 生成模型
const response = await request.post('/api/v2/rodin', {
  prompt: 'A 3D cat',
  settings: { quality: 'high' }
})

路由过程:

/api/v2/rodin 
  → 匹配 pathname.startsWith('/api/v2')
  → 使用 RODIN_API_BASE + RODIN_API_TOKEN
  → https://api.hyper3d.com/api/v2/rodin (Bearer Token)

MULE API 调用 ​

typescript
// 查询计费数据
const response = await request.get('/sessions/metering?user_id=123')

路由过程:

/sessions/metering
  → 匹配 pathname.startsWith('/sessions/metering')
  → 使用 MULE_API_BASE + MULE_AGENT_KEY
  → https://api.mulerun.com/sessions/metering (x-api-key)

⚙️ 扩展新的 Provider ​

如果需要添加第三个 provider(比如 /billing/* 用另一个 API),步骤如下:

1. 修改 functions/[[path]].ts ​

在 getProvider() 函数中添加新路由:

typescript
// 在 getProvider 函数中添加
if (pathname.startsWith('/billing')) {
  return {
    provider: {
      base: env.BILLING_API_BASE,
      token: env.BILLING_API_KEY,
      authType: 'bearer', // 或 'key'
    },
    pathForUpstream: pathname,
  };
}

2. 更新 .dev.vars ​

dotenv
BILLING_API_BASE="https://billing.example.com"
BILLING_API_KEY="your-billing-key"

3. 线上部署 ​

在 Cloudflare Pages UI → Settings → Environment variables 中添加相同的变量。

4. 前端使用 ​

typescript
const response = await request.get('/billing/invoices')

🔐 安全最佳实践 ​

✅ 应该做 ​

  • ✅ 把所有 token/key 放在 .dev.vars 或 Cloudflare Secrets(不提交 Git)
  • ✅ 使用 Pages Function 作为 API 网关隐藏 token
  • ✅ 在生产环境中标记 Secrets 为加密类型
  • ✅ 定期轮换 token
  • ✅ 为不同环境使用不同的 token

❌ 不应该做 ​

  • ❌ 把 token 放在 VITE_* 或 .env.production (会被编译进前端代码)
  • ❌ 把 .dev.vars 提交到 Git
  • ❌ 在浏览器 Console 中输出 token
  • ❌ 使用生产 token 在本地开发

📊 对比:修改前后 ​

修改前 ​

❌ 两个独立的 API 调用方式
❌ /api/v2/* 和 /sessions/metering 无法区分认证方式
❌ 无法同时隐藏多个 token
❌ 扩展新 provider 需要新建文件
❌ 路由逻辑散落

修改后 ​

✅ 统一的 API 网关 (functions/[[path]].ts)
✅ 支持多 provider,灵活路由
✅ 每个 provider 独立 token 管理
✅ 所有 token 在服务端,浏览器无法看到
✅ 易于扩展,添加新 provider 只需修改一个文件
✅ 完整的日志与调试信息

一、先给结论(方便快速决定)

如果你已经把前端部署在 Cloudflare Pages 且仅需一个简单代理 /api/v2 来隐藏 token:优先选 Pages Functions(最简单、同仓部署、同域、易调试)。

如果你需要更复杂的路由、多个独立服务、或想把代理独立管理:选 Cloudflare Worker(独立 Worker)(更灵活、可复用、适合大型场景)。

下面先把概念讲清,再分别给出两套完整可运行示例(Pages Functions 版本与独立 Worker 版本),包含本地调试、如何写 secret、如何部署、如何在前端调用、注意事项与常见问题排查。

二、概念与差异速览(图文并茂口语化)

Cloudflare Worker:一个独立的边缘 JavaScript 环境(类似在全球有很多小型 serverless 节点),你可以写任意后端逻辑、绑定任意域名或 route。通常通过 wrangler 管理与部署。 优点:独立、灵活、强;缺点:对初学者多一步配置(wrangler、route、zone_id 等)。

Pages Functions:Cloudflare Pages 内置的“函数目录”方案,意味着你的静态站(Pages)与后端函数放在同一个代码仓库 & 一次部署。文件放在 functions/(或 Pages 要求的路径),Pages 会自动部署并将路径映射到函数。 优点:极简体验(同一 repo、一键部署),对前端工程师友好;缺点:在多项目/微服务场景下不够独立。

三、环境准备(通用)

Node.js(建议 >=18)

Cloudflare 帐号(已注册)

你已有前端项目(React + Vite),并且已部署/准备部署到 Cloudflare Pages(可选)

本地终端、编辑器(VSCode 等)

安装/登录 wrangler(用于独立 Worker 或 pages secret 操作):

bash
npm install -g wrangler@latest   # 可选:也可用 npx 执行
wrangler login

四、重要概念:Variable vs Secret(必须记住)

Variable(env / vars):可以是明文,用于非敏感配置(构建时注入、或 env 运行时使用)。不要放敏感 token。

Secret(加密):加密存储,只能在 Worker/Pages Function 的运行时读取,不能泄露在构建或浏览器端。把 Bearer token 放到 Secret 里。

在 CLI:

Pages secret(绑定到 Pages 项目):

bash
npx wrangler pages secret put API_TOKEN --project-name <your-pages-project>

Worker secret(独立 Worker):

bash
echo "MY_TOKEN" | npx wrangler secret put API_TOKEN

五、方案 A:Pages Functions(推荐你的场景)

场景适用:你的网站已经部署在 Cloudflare Pages,想在同域下把 /api/v2/* 代理到后端并隐藏 Bearer token。页面与函数放同仓,一起部署最方便。

  1. 文件位置 & 约定(以常见 Pages 项目结构为例)

在你的项目里(与 dist / build 输出并列)创建:

bash
project-root/
  ├─ src/            # React 源码(不受影响)
  ├─ functions/
  │   └─ api/
  │       └─ v2/
  │           └─ [...path].js   # catch-all,处理 /api/v2/*
  ├─ package.json
  └─ other files...

functions/api/v2/[...path].js(完整代码):

javascript
// functions/api/v2/[...path].js
export async function onRequest(context) {
  // context 包含 request, env, params, etc.
  const { request, env, params } = context;
  const url = new URL(request.url);
  // params.path 是 catch-all 捕获到的部分(数组形式或字符串,依据 runtime)
  // 这里我们直接利用 pathname 替换更简单:
  const upstreamBase = env.GLOBAL_UPSTREAM_BASE || 'https://api.example.com';
  if (!url.pathname.startsWith('/api/v2')) {
    return new Response('Not Found', { status: 404 });
  }

  // 映射路径: /api/v2/foo/bar -> /v2/foo/bar
  const upstreamPath = url.pathname.replace(/^\/api\/v2/, '/v2');
  const upstreamUrl = upstreamBase + upstreamPath + url.search;

  const apiToken = env.API_TOKEN; // Pages secret / environment binding
  if (!apiToken) {
    return new Response('Server misconfigured: API_TOKEN missing', { status: 500 });
  }

  // 复制原请求的 headers 和 body
  const headers = new Headers(request.headers);
  headers.set('Authorization', `Bearer ${apiToken}`);
  headers.delete('Host');

  const init = {
    method: request.method,
    headers,
    body: (request.method === 'GET' || request.method === 'HEAD') ? undefined : request.body,
    redirect: 'manual'
  };

  const resp = await fetch(upstreamUrl, init);

  // 处理 CORS(如果同域部署,严格限制 origin 更安全)
  const respHeaders = new Headers(resp.headers);
  respHeaders.set('Access-Control-Allow-Origin', '*');
  respHeaders.set('Access-Control-Allow-Methods', 'GET,POST,PUT,DELETE,OPTIONS');
  respHeaders.set('Access-Control-Allow-Headers', 'Content-Type,Authorization');

  return new Response(resp.body, {
    status: resp.status,
    statusText: resp.statusText,
    headers: respHeaders
  });
}

说明要点

env.API_TOKEN:在 Pages 项目里通过 CLI 或 Dashboard 把 secret 加入(见下)。

Path catch-all:Pages Functions 支持以 [...] 方式捕获任意子路径。

CORS:示例使用 *,上线请把 * 换成你的域名以更安全(例如 https://mulerun-rodin.pages.dev)。

  1. 把 Secret 加入 Pages(CLI)
bash
# 假设你的 pages 项目名是 mulerun-rodin(在 dashboard 可以看到 project name)
npx wrangler pages secret put API_TOKEN --project-name mulerun-rodin
# 它会提示你粘贴 token(不会在控制台回显),粘贴后回车保存

也可以在 Cloudflare Dashboard → Pages → 你的 Project → Settings → Variables & Secrets 添加。

  1. 本地开发与调试

Pages Functions 在本地可用 wrangler dev 调试,但通常你会先用 Vite 本地开发:

方法 A:在另一个终端运行 npx wrangler dev(模拟 functions),然后把 Vite 的 proxy 指向 http://127.0.0.1:8787,在 Vite 中配置 server.proxy:

javascript
// vite.config.js
export default {
  // ...
  server: {
    proxy: {
      '/api/v2': 'http://127.0.0.1:8787'
    }
  }
}

方法 B:直接 wrangler pages dev(如果你要模拟完整 Pages 行为,需参照 docs)。 4) 部署

提交代码到你的 Pages 仓库(如果你使用 GitHub + Cloudflare Pages),Cloudflare 会在你 push 后自动 build & deploy(会自动部署 functions)。

或用 wrangler 手动操作(视 Pages 流程而定)。 六、方案 B:独立 Cloudflare Worker(适合更复杂场景)

场景适用:你想把代理做成独立服务、需要自定义路由或多个站点共用同一个代理,或想独立运维。

  1. 项目结构(最小)
bash
my-wrangler/
  ├─ src/
  │   └─ index.js
  ├─ wrangler.toml
  └─ package.json

src/index.js(模块化 Worker):

js
export default {
  async fetch(request, env) {
    const url = new URL(request.url);

    if (!url.pathname.startsWith('/api/v2')) {
      return fetch(request); // passthrough 或 404
    }

    const upstreamBase = env.GLOBAL_UPSTREAM_BASE || 'https://api.example.com';
    const upstreamPath = url.pathname.replace(/^\/api\/v2/, '/v2');
    const upstreamUrl = upstreamBase + upstreamPath + url.search;

    const apiToken = env.API_TOKEN;
    if (!apiToken) {
      return new Response('Server misconfigured: API_TOKEN missing', { status: 500 });
    }

    const headers = new Headers(request.headers);
    headers.set('Authorization', `Bearer ${apiToken}`);
    headers.delete('Host');

    const init = {
      method: request.method,
      headers,
      body: (request.method === 'GET' || request.method === 'HEAD') ? undefined : request.body,
      redirect: 'manual'
    };

    const upstreamResp = await fetch(upstreamUrl, init);

    const respHeaders = new Headers(upstreamResp.headers);
    respHeaders.set('Access-Control-Allow-Origin', '*');
    respHeaders.set('Access-Control-Allow-Methods', 'GET,POST,PUT,DELETE,OPTIONS');
    respHeaders.set('Access-Control-Allow-Headers', 'Content-Type,Authorization');

    return new Response(upstreamResp.body, {
      status: upstreamResp.status,
      statusText: upstreamResp.statusText,
      headers: respHeaders
    });
  }
};
  1. wrangler.toml(最小)
toml
name = "mulerun-rodin-proxy"
main = "src/index.js"
compatibility_date = "2025-11-18"
workers_dev = true
# 若要绑定到 pages 域名下的 route,可在 Dashboard 配置 route:
# route = "mulerun-rodin.pages.dev/api/*"
# zone_id = "<your-zone-id>"
  1. 把 Secret 加到 Worker
bash
echo "YOUR_REAL_API_TOKEN" | npx wrangler secret put API_TOKEN
# 或在交互中运行:
npx wrangler secret put API_TOKEN
# 然后粘贴值
  1. 本地调试
bash
npx wrangler dev src/index.js
# 打开 http://127.0.0.1:8787 并测试 /api/v2/...

注意:wrangler dev 本地调试不会自动读取 Dashboard 上的 secret,你需要使用 .dev.vars 或在本地模拟(详见下面「本地 secret 与 .dev.vars」)。 5) 部署并绑定到 Pages 域名(如果你想让 mulerun-rodin.pages.dev/api/* 触发 Worker)

方法 A(Dashboard):Cloudflare Dashboard → Workers → 选择你的 Worker → Routes → 添加 mulerun-rodin.pages.dev/api/*。这样访问 mulerun-rodin.pages.dev/api/... 时会先走 Worker。

方法 B(wrangler):在 wrangler.toml 填 route、zone_id 并 wrangler publish(这种方式通常需要 zone_id,获取 zone_id 可在 dash 或 API 查找)。

七、本地开发细节:.dev.vars 与 Vite 本地代理

开发时你不该把真实 token 放到仓库。两种常用方法:

  1. 用 .dev.vars(仅本地、NOT committed)来提供 env:
bash
API_TOKEN = "LOCAL_FAKE_TOKEN"
GLOBAL_UPSTREAM_BASE = "https://api.example.com"

wrangler dev 会读取 .dev.vars 并注入 env。 2. Vite 本地 dev 时,用 server.proxy 把 /api/v2 转发到 wrangler dev 本地地址(http://127.0.0.1:8787),从而在本地体验同域调用:

js
// vite.config.js
export default {
  server: {
    proxy: {
      '/api/v2': 'http://127.0.0.1:8787'
    }
  }
}

八、前端(React)如何调用代理

前端只需要像调用本地 API 一样调用相对路径,不带任何敏感 token:

js
// src/api.js
export async function callApi(path, data) {
  const res = await fetch(`/api/v2/${path}`, {
    method: data ? 'POST' : 'GET',
    headers: { 'Content-Type': 'application/json' },
    body: data ? JSON.stringify(data) : undefined,
    credentials: 'same-origin',
  });
  if (!res.ok) { throw new Error(`HTTP ${res.status}`); }
  return res.json();
}

注意在本地开发时,若你没有运行 wrangler dev,可以在 Vite 中配置代理把 /api/v2 指向真实上游(仅用于开发,切勿把真实 token 放在前端 env 中)。

九、CORS、安全与注意事项

CORS:如果你把前端与 Functions/Worker 放在同域(pages domain),通常无需 CORS;如果跨域访问,需要在 Worker/Function 返回 Access-Control-Allow-Origin 等头。生产环境请替换 * 为具体域名。

不要把 token 写成 VITE_ 前缀的 env:Vite 中以 VITE_ 开头的环境变量会被打包进前端代码并能被浏览器看到 —— 绝对不要放敏感信息。

不要把 secret 写到 wrangler.toml 的 vars:那是明文。应该使用 wrangler secret put 或 Pages 的 secret 功能。

鉴权:Worker/Function 能隐藏后端 token,但仍需做业务级鉴权(比如用户身份验证、访问频率控制等),否则任何人都能调用代理(可以限制 origin / referer,但这些头可伪造)。更安全的方式是让前端先登录并带上用户 token(短期 token)进行二次校验,Worker 再用服务器 token 和上游服务沟通。 十、完整操作清单(Copy & Paste)

下面给出最常用的 Pages Functions 流程(你选择 Pages Functions 时照做):

在你的项目根创建 function 文件:

bash
mkdir -p functions/api/v2
# 将上面的 [...path].js 内容粘贴进去

在 Cloudflare Pages Dashboard 查看你的 project name(如 mulerun-rodin)。

本地开发(可选):

bash
# 给本地 dev 用一个假的 token(仅本地)
cat > .dev.vars <<EOF
API_TOKEN = "LOCAL_FAKE_TOKEN"
GLOBAL_UPSTREAM_BASE = "https://api.example.com"
EOF

# 启动 wrangler dev(如果需要模拟 functions)
npx wrangler dev
# 启动 vite
npm run dev

在 Pages 项目中添加 Secret(生产):

bash
npx wrangler pages secret put API_TOKEN --project-name mulerun-rodin

提交代码并 push(Cloudflare Pages 自动构建并部署)。部署后访问:

https://mulerun-rodin.pages.dev/api/v2/your/path

前端调用:

js
fetch('/api/v2/your/path') // 前端不包含 token,Worker/Function 注入 token 再转发

十一、排错清单(常见问题)

请求返回 500,提示 API_TOKEN missing:说明 env 没注入 secret,确认你在 Pages Dashboard 或用 wrangler pages secret put 提交了 API_TOKEN。

本地 wrangler dev 看不到 secret:wrangler dev 不会自动读取 Dashboard 的 secret;用 .dev.vars 本地模拟或用 wrangler secret put(这会上传到 Cloudflare)。

CORS 报错(浏览器):检查 Worker/Function 是否在 OPTIONS 请求中返回 Access-Control-Allow-* 头。对 preflight(OPTIONS)要返回 200 并包含允许的 headers。

404:检查 functions 文件路径与请求路径是否匹配(Pages Functions 路径映射严格,catch-all 文件名要写对)。