cloudflare-vpc-services

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Cloudflare VPC Services

Cloudflare VPC Services

Enable Workers to securely access private APIs and services through encrypted tunnels without public internet exposure.
让Workers能够通过加密隧道安全访问私有API和服务,无需暴露在公网中。

⚠️ BEFORE YOU START

⚠️ 开始之前

This skill prevents 5 common errors and saves ~60% tokens.
MetricWithout SkillWith Skill
Setup Time45+ min10 min
Common Errors50
Token Usage~8000~3000
本技能可避免5种常见错误,并节省约60%的token使用量。
指标未使用技能使用技能
设置时间45+ 分钟10 分钟
常见错误数量50
Token使用量~8000~3000

Known Issues This Skill Prevents

本技能可预防的已知问题

  1. dns_error
    from outdated cloudflared version or wrong protocol
  2. Requests leaving VPC due to using public hostnames instead of internal
  3. Port mismatch - fetch() port is ignored, service config port is used
  4. Missing absolute URLs in fetch() calls
  5. Incorrect tunnel ID or service binding configuration
  1. 因cloudflared版本过旧或协议错误导致的
    dns_error
  2. 因使用公网主机名而非内部主机名,导致请求流出VPC
  3. 端口不匹配 - fetch()中的端口会被忽略,将使用服务配置中的端口
  4. fetch()调用中缺少绝对URL
  5. 隧道ID或服务绑定配置错误

Quick Start

快速开始

Step 1: Verify Tunnel Requirements

步骤1:验证隧道要求

bash
undefined
bash
undefined

Check cloudflared version on remote infrastructure (K8s, EC2, etc.)

检查远程基础设施(K8s、EC2等)上的cloudflared版本

Must be 2025.7.0 or later

必须为2025.7.0或更高版本

cloudflared --version
cloudflared --version

Verify QUIC protocol is configured (not http2)

验证已配置QUIC协议(而非http2)

Check tunnel config or Cloudflare dashboard

检查隧道配置或Cloudflare控制台


**Why this matters:** Workers VPC requires cloudflared 2025.7.0+ with QUIC protocol. Older versions or http2 protocol cause `dns_error`.

**为什么这很重要:** Workers VPC要求cloudflared版本为2025.7.0及以上,且使用QUIC协议。旧版本或http2协议会导致`dns_error`。

Step 2: Create VPC Service

步骤2:创建VPC服务

bash
undefined
bash
undefined

Use Cloudflare API or dashboard to create VPC service

使用Cloudflare API或控制台创建VPC服务

See templates/vpc-service-ip.json or templates/vpc-service-hostname.json

参考templates/vpc-service-ip.json或templates/vpc-service-hostname.json


**Why this matters:** The VPC service defines the actual target (IP/hostname) that the tunnel routes to. The fetch() URL only sets Host header and SNI.

**为什么这很重要:** VPC服务定义了隧道要路由到的实际目标(IP/主机名)。fetch() URL仅用于设置Host头和SNI。

Step 3: Configure Wrangler Binding

步骤3:配置Wrangler绑定

jsonc
// wrangler.jsonc
{
  "vpc_services": [
    {
      "binding": "PRIVATE_API",
      "service_id": "<YOUR_SERVICE_ID>",
      "remote": true
    }
  ]
}
Why this matters: The binding name becomes the environment variable used in Worker code:
env.PRIVATE_API.fetch()
.
jsonc
// wrangler.jsonc
{
  "vpc_services": [
    {
      "binding": "PRIVATE_API",
      "service_id": "<YOUR_SERVICE_ID>",
      "remote": true
    }
  ]
}
为什么这很重要: 绑定名称将成为Worker代码中使用的环境变量:
env.PRIVATE_API.fetch()

Critical Rules

关键规则

✅ Always Do

✅ 必须遵守

  • ✅ Use absolute URLs with protocol, host, and path in fetch()
  • ✅ Use internal VPC hostnames, not public endpoints
  • ✅ Ensure cloudflared is 2025.7.0+ with QUIC protocol
  • ✅ Allow UDP port 7844 outbound for QUIC connections
  • ✅ 在fetch()中使用包含协议、主机和路径的绝对URL
  • ✅ 使用内部VPC主机名,而非公网端点
  • ✅ 确保cloudflared版本为2025.7.0及以上,且使用QUIC协议
  • ✅ 允许UDP 7844端口的出站流量,用于QUIC连接

❌ Never Do

❌ 禁止操作

  • ❌ Use port numbers in fetch() URL (they're ignored)
  • ❌ Use public hostnames for services inside VPC
  • ❌ Assume http2 protocol works (only QUIC is supported)
  • ❌ Use relative URLs in fetch()
  • ❌ 在fetch() URL中使用端口号(会被忽略)
  • ❌ 为VPC内的服务使用公网主机名
  • ❌ 假设http2协议可用(仅支持QUIC)
  • ❌ 在fetch()中使用相对URL

Common Mistakes

常见错误示例

❌ Wrong:
javascript
// Port is ignored, relative URL fails
const response = await env.VPC_SERVICE.fetch("/api/users:8080");
✅ Correct:
javascript
// Absolute URL, port configured in VPC service
const response = await env.VPC_SERVICE.fetch("https://internal-api.company.local/api/users");
Why: The VPC service configuration determines actual routing. The fetch() URL only populates the Host header and SNI value.
❌ 错误写法:
javascript
// 端口会被忽略,相对URL会失败
const response = await env.VPC_SERVICE.fetch("/api/users:8080");
✅ 正确写法:
javascript
// 使用绝对URL,端口在VPC服务中配置
const response = await env.VPC_SERVICE.fetch("https://internal-api.company.local/api/users");
原因: VPC服务配置决定了实际路由规则。fetch() URL仅用于填充Host头和SNI值。

Known Issues Prevention

已知问题预防方案

IssueRoot CauseSolution
dns_error
cloudflared < 2025.7.0 or http2 protocolUpdate cloudflared, configure QUIC, allow UDP 7844
Requests go to public internetUsing public hostname in fetch()Use internal VPC hostname
Connection refusedWrong port in VPC service configConfigure correct http_port/https_port in service
TimeoutTunnel not running or wrong tunnel_idVerify tunnel status, check tunnel_id
404 errorsIncorrect path routingVerify internal service path matches fetch() path
问题根本原因解决方案
dns_error
cloudflared版本低于2025.7.0或使用http2协议升级cloudflared,配置QUIC协议,允许UDP 7844端口
请求流向公网fetch()中使用了公网主机名使用内部VPC主机名
连接被拒绝VPC服务配置中的端口错误在服务中配置正确的http_port/https_port
请求超时隧道未运行或隧道ID错误验证隧道状态,检查隧道ID
404错误路径路由配置错误验证内部服务路径与fetch()中的路径一致

Configuration Reference

配置参考

wrangler.jsonc

wrangler.jsonc

jsonc
{
  "name": "my-worker",
  "main": "src/index.ts",
  "compatibility_date": "2024-01-01",
  "vpc_services": [
    {
      "binding": "PRIVATE_API",
      "service_id": "daf43e8c-a81a-4242-9912-4a2ebe4fdd79",
      "remote": true
    },
    {
      "binding": "PRIVATE_DATABASE",
      "service_id": "453b6067-1327-420d-89b3-2b6ad16e6551",
      "remote": true
    }
  ]
}
Key settings:
  • binding
    : Environment variable name for accessing the service
  • service_id
    : UUID from VPC service creation
  • remote
    : Must be
    true
    for VPC services
jsonc
{
  "name": "my-worker",
  "main": "src/index.ts",
  "compatibility_date": "2024-01-01",
  "vpc_services": [
    {
      "binding": "PRIVATE_API",
      "service_id": "daf43e8c-a81a-4242-9912-4a2ebe4fdd79",
      "remote": true
    },
    {
      "binding": "PRIVATE_DATABASE",
      "service_id": "453b6067-1327-420d-89b3-2b6ad16e6551",
      "remote": true
    }
  ]
}
关键设置:
  • binding
    : 用于访问服务的环境变量名称
  • service_id
    : 创建VPC服务时生成的UUID
  • remote
    : 对于VPC服务必须设置为
    true

Common Patterns

常见使用模式

Basic GET Request

基础GET请求

javascript
export default {
  async fetch(request, env) {
    const response = await env.PRIVATE_API.fetch(
      "https://internal-api.company.local/users"
    );
    return response;
  }
};
javascript
export default {
  async fetch(request, env) {
    const response = await env.PRIVATE_API.fetch(
      "https://internal-api.company.local/users"
    );
    return response;
  }
};

POST with Authentication

带认证的POST请求

javascript
const response = await env.PRIVATE_API.fetch(
  "https://internal-api.company.local/users",
  {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      Authorization: `Bearer ${env.API_TOKEN}`
    },
    body: JSON.stringify({ name: "John", email: "john@example.com" })
  }
);
javascript
const response = await env.PRIVATE_API.fetch(
  "https://internal-api.company.local/users",
  {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      Authorization: `Bearer ${env.API_TOKEN}`
    },
    body: JSON.stringify({ name: "John", email: "john@example.com" })
  }
);

API Gateway with Path Routing

带路径路由的API网关

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

    if (url.pathname.startsWith('/api/users')) {
      return env.USER_SERVICE.fetch(
        `https://user-api.internal${url.pathname}`
      );
    } else if (url.pathname.startsWith('/api/orders')) {
      return env.ORDER_SERVICE.fetch(
        `https://orders-api.internal${url.pathname}`
      );
    }

    return new Response('Not Found', { status: 404 });
  }
};
javascript
export default {
  async fetch(request, env) {
    const url = new URL(request.url);

    if (url.pathname.startsWith('/api/users')) {
      return env.USER_SERVICE.fetch(
        `https://user-api.internal${url.pathname}`
      );
    } else if (url.pathname.startsWith('/api/orders')) {
      return env.ORDER_SERVICE.fetch(
        `https://orders-api.internal${url.pathname}`
      );
    }

    return new Response('Not Found', { status: 404 });
  }
};

Bundled Resources

附带资源

Templates

模板

Located in
templates/
:
  • wrangler-vpc.jsonc
    - Ready-to-use wrangler config with VPC bindings
  • vpc-service-ip.json
    - IP-based VPC service API payload
  • vpc-service-hostname.json
    - Hostname-based VPC service API payload
Copy these templates as starting points for your implementation.
位于
templates/
目录下:
  • wrangler-vpc.jsonc
    - 可直接使用的带VPC绑定的wrangler配置模板
  • vpc-service-ip.json
    - 基于IP的VPC服务API请求体模板
  • vpc-service-hostname.json
    - 基于主机名的VPC服务API请求体模板
可复制这些模板作为实现的起点。

Scripts

脚本

Located in
scripts/
:
  • list-vpc-services.sh
    - List VPC services via Cloudflare API
  • tail-worker.sh
    - Debug VPC connections with live logs
  • set-api-token.sh
    - Set secrets for private API auth
位于
scripts/
目录下:
  • list-vpc-services.sh
    - 通过Cloudflare API列出VPC服务
  • tail-worker.sh
    - 通过实时日志调试VPC连接
  • set-api-token.sh
    - 设置私有API认证所需的密钥

References

参考文档

Located in
references/
:
  • api-patterns.md
    - Comprehensive fetch() patterns and examples
位于
references/
目录下:
  • api-patterns.md
    - 全面的fetch()使用模式和示例

Dependencies

依赖项

Required

必须安装

PackageVersionPurpose
wranglerlatestDeploy Workers with VPC bindings
cloudflared2025.7.0+Tunnel daemon (on remote infrastructure)
包名版本用途
wranglerlatest部署带VPC绑定的Workers
cloudflared2025.7.0+隧道守护进程(部署在远程基础设施上)

Optional

可选安装

PackageVersionPurpose
@cloudflare/workers-typeslatestTypeScript types for Workers
包名版本用途
@cloudflare/workers-typeslatestWorkers的TypeScript类型定义

Official Documentation

官方文档

Troubleshooting

故障排查

dns_error when calling VPC service

调用VPC服务时出现dns_error

Symptoms: Worker returns
dns_error
when calling
env.VPC_SERVICE.fetch()
Solution:
  1. Update cloudflared to 2025.7.0+ on remote infrastructure
  2. Configure QUIC protocol (not http2)
  3. Allow UDP port 7844 outbound
症状: 调用
env.VPC_SERVICE.fetch()
时,Worker返回
dns_error
解决方案:
  1. 在远程基础设施上升级cloudflared到2025.7.0及以上版本
  2. 配置QUIC协议(而非http2)
  3. 允许UDP 7844端口的出站流量

Requests going to public internet

请求流向公网

Symptoms: Logs show requests hitting public endpoints instead of internal
Solution:
javascript
// Use internal hostname
const response = await env.VPC_SERVICE.fetch(
  "https://internal-api.vpc.local/endpoint"  // Internal
  // NOT "https://api.company.com/endpoint"   // Public
);
症状: 日志显示请求命中了公网端点而非内部服务
解决方案:
javascript
// 使用内部主机名
const response = await env.VPC_SERVICE.fetch(
  "https://internal-api.vpc.local/endpoint"  // 内部地址
  // 不要使用 "https://api.company.com/endpoint"   // 公网地址
);

Connection timeout

请求超时

Symptoms: Requests hang and eventually timeout
Solution:
  1. Verify tunnel is running: check cloudflared logs
  2. Verify tunnel_id matches in VPC service config
  3. Check network connectivity from tunnel to target
症状: 请求挂起并最终超时
解决方案:
  1. 验证隧道是否运行:检查cloudflared日志
  2. 验证VPC服务配置中的隧道ID是否正确
  3. 检查隧道到目标服务的网络连通性

Setup Checklist

设置检查清单

Before using this skill, verify:
  • cloudflared 2025.7.0+ deployed on remote infrastructure
  • QUIC protocol configured (not http2)
  • UDP port 7844 outbound allowed
  • VPC service created with correct tunnel_id
  • wrangler.jsonc has vpc_services binding
  • Using internal hostnames (not public endpoints)
  • Using absolute URLs in fetch() calls
使用本技能前,请验证以下项:
  • 远程基础设施上已部署2025.7.0及以上版本的cloudflared
  • 已配置QUIC协议(而非http2)
  • 已允许UDP 7844端口的出站流量
  • 已创建带正确隧道ID的VPC服务
  • wrangler.jsonc中已配置vpc_services绑定
  • 使用的是内部主机名(而非公网端点)
  • fetch()调用中使用的是绝对URL