Envoy API 网关
Architecture and configuration of the Envoy API gateway for self-hosted Supabase.
自托管的 Supabase 配有一个可选的基于 Envoy 的 API 网关。它接收来自客户端的请求,将它们路由到内部服务(Auth、PostgREST、Realtime、Storage、Edge Functions、postgres-meta、Studio),并通过将不透明的 sb_ 密钥转换为这些服务使用的内部凭证来强制执行 API 密钥认证。
🌐 Self-hosted Supabase ships with an optional Envoy-based API gateway. It accepts incoming client requests, routes them to internal services (Auth, PostgREST, Realtime, Storage, Edge Functions, postgres-meta, Studio), and enforces API key authentication by translating opaque sb_ keys into the internal credentials used by those services.
本指南向想要了解或自定义 Envoy 网关的运维人员介绍其架构、配置布局和安全状态。这不是 Envoy 教程——有关过滤器、路由和集群的参考,请查看 Envoy 文档。
🌐 This guide explains the architecture, configuration layout, and security posture of the Envoy gateway for operators who want to understand or customize it. It is not an Envoy tutorial - for reference on filters, routes, and clusters, see the Envoy documentation.
在你开始之前 #
🌐 Before you begin
- 完成 使用 Docker 自托管 设置
- 要启用不透明的
sb_密钥转换,请参阅 新的 API 密钥和非对称认证
启用 Envoy 网关 #
🌐 Enabling the Envoy gateway
Envoy 网关作为 Docker Compose 的覆盖配置提供。
🌐 The Envoy gateway is provided as a Docker Compose override.
如果你的堆栈已经从初始设置运行,先用 sh run.sh stop 把它关闭。
🌐 If your stack is already running from the initial setup, bring it down first with sh run.sh stop.
启用 Envoy 覆盖,然后启动堆栈:
🌐 Enable the Envoy override, then start the stack:
1sh run.sh config add envoy2sh run.sh start这个覆盖会禁用默认的 Kong 网关,并在同一个端口(默认 8000)启动 Envoy。它还会通过依赖重新配置 Functions 服务,让它等待 Envoy。
🌐 The override disables the default Kong gateway and starts Envoy on the same port (default 8000). It also reconfigures the Functions service to wait for Envoy via a dependency.
Envoy 被注册为 api-gw 服务,并且还将 kong 作为网络别名暴露;基础的 Kong 服务同样暴露 api-gw。任意主机名都会解析到当前活跃的网关,因此那些硬编码了 kong:8000 的内部配置(例如在 Edge Functions 或 Studio 中)依然可以正常工作,无需修改。
🌐 Envoy is registered as the api-gw service and also exposes kong as a network alias; the base Kong service likewise exposes api-gw. Either hostname resolves to whichever gateway is currently active, so internal configs that hardcode kong:8000 (for example, in Edge Functions or Studio) keep working without changes.
验证 #
🌐 Verify
确认网关正在路由请求并执行 API 密钥:
🌐 Confirm the gateway is routing requests and enforcing API keys:
1curl -i -H "apikey: your-service-role-key" http://<your-domain>/rest/v1/来自 PostgREST 的 200 OK 响应确认网关已启动。一个没有 apikey 头的 401 Unauthorized 确认执行已激活。
🌐 A 200 OK response from PostgREST confirms the gateway is up. A 401 Unauthorized without the apikey header confirms enforcement is active.
架构 #
🌐 Architecture
Envoy作为一个单容器(supabase-envoy)运行,在端口8000上有一个监听器。每个传入请求都会先经过一个按顺序排列的HTTP过滤器链,然后才被转发到上游集群:
🌐 Envoy runs as a single container (supabase-envoy) with one listener on port 8000. Every incoming request passes through an ordered chain of HTTP filters before being forwarded to an upstream cluster:
1Client2 │3 ▼4Listener (port 8000)5 │6 ▼7HTTP filter chain8 ├─ CORS9 ├─ Basic Auth (dashboard only)10 ├─ Lua: copy ?apikey query to header11 ├─ Lua: 401 for missing/invalid API key12 ├─ Lua: translate opaque keys in query13 ├─ Lua: translate opaque keys in header14 ├─ Lua: mirror apikey to x-api-key (Realtime WS)15 ├─ Lua: synthesize Authorization header16 ├─ RBAC (global: service_role → /pg/, apikey → other API routes;17 │ per-route DENY override on /mcp and Realtime18 │ /api/tenants, /api/openapi)19 └─ Router20 │21 ▼22Upstream clusters23 ├─ auth (auth:9999)24 ├─ rest (rest:3000)25 ├─ realtime (realtime-dev.supabase-realtime:4000)26 ├─ storage (storage:5000)27 ├─ functions (functions:9000)28 ├─ meta (meta:8080)29 └─ studio (studio:3000)路由会按照监听器配置中声明的顺序进行匹配。每条路由都会选择一个上游集群,重写请求路径前缀,并且可以覆盖过滤器的行为(例如,在 API 路由上禁用基本认证,或者在 MCP 路由上拒绝所有流量)。
🌐 Routes are matched in the order declared in the listener config. Each route selects an upstream cluster, rewrites the request path prefix, and can override filter behavior (for example, disabling basic auth on API routes or denying all traffic on MCP routes).
配置文件结构 #
🌐 Configuration file structure
所有 Envoy 配置都存放在 ./volumes/api/envoy/(相对于包含 docker-compose.yml 的目录):
🌐 All Envoy configuration lives in ./volumes/api/envoy/ (relative to the directory containing docker-compose.yml):
| 文件 | 作用 |
|---|---|
envoy.yaml | 引导配置。将 Envoy 指向 CDS 和 LDS 文件,配置管理界面,并对下游连接设置过载管理器限制。 |
cds.yaml | 集群发现服务 - 上游服务定义(DNS、端口、健康检查、连接超时、熔断器)。 |
lds.template.yaml | 监听器发现服务模板。定义监听器、过滤器链、路由、RBAC 策略和 CORS 策略。包含密钥和凭证的占位符。 |
docker-entrypoint.sh | 在容器启动时,将 LDS 模板渲染到 lds.yaml,通过替换环境变量,计算 DASHBOARD_PASSWORD 的 SHA1+base64 哈希用于基本认证,然后启动 Envoy。 |
启动时配置是如何呈现的 #
🌐 How the configuration is rendered at startup
Envoy 本身不能直接在配置中读取环境变量。入口脚本会在启动 Envoy 之前,用 sed 将环境特定的值渲染到监听器配置中:
🌐 Envoy cannot natively read environment variables inside its config. The entrypoint script renders environment-specific values into the listener config with sed before launching Envoy:
- 对
DASHBOARD_PASSWORD计算 SHA1+base64 哈希,然后将其与DASHBOARD_USERNAME拼接成一个DASHBOARD_BASIC_AUTH字符串,格式为 Envoy 的basic_auth过滤器所期望的username:{SHA}<base64-encoded-sha1>格式。不支持其他哈希格式。 - 接着将以下变量代入
lds.template.yaml,结果写入lds.yaml:
| 变量 | 用途 |
|---|---|
ANON_KEY | 旧版 HS256 匿名 JWT(API 密钥验证,RBAC) |
SERVICE_ROLE_KEY | 旧版 HS256 service_role JWT(API 密钥验证,RBAC) |
SUPABASE_PUBLISHABLE_KEY | 不透明的 sb_publishable_* 密钥(翻译源) |
SUPABASE_SECRET_KEY | 不透明的 sb_secret_* 密钥(翻译源) |
ANON_KEY_ASYMMETRIC | 预签名 ES256 匿名 JWT(翻译目标;仅内部使用) |
SERVICE_ROLE_KEY_ASYMMETRIC | 预签名 ES256 service_role JWT(翻译目标;仅内部使用) |
DASHBOARD_BASIC_AUTH | 仪表板基础认证凭证(在步骤 1 中计算) |
- 如果
SUPABASE_SECRET_KEY、SUPABASE_PUBLISHABLE_KEY、ANON_KEY_ASYMMETRIC和SERVICE_ROLE_KEY_ASYMMETRIC都设置了,就会启用不透明密钥翻译。否则,Envoy 会以仅支持传统模式运行,只接受传统的 HS256 密钥。入口点会在启动时把当前模式打印到容器日志里。
配置更改需要重启容器,这样入口点才能重新渲染模板:
🌐 Configuration changes require restarting the container so the entrypoint re-renders the template:
1sh run.sh restart api-gw路线 #
🌐 Routes
路由会按声明的顺序匹配。第一个匹配的前缀优先。受保护的路由需要有效的 apikey 头;开放路由无需 API 密钥验证即可通过。
🌐 Routes are matched in the order declared. The first matching prefix wins. Protected routes require a valid apikey header; open routes pass through without API key validation.
| 路径前缀 | 上游 | 路径重写 | 访问控制 | 备注 |
|---|---|---|---|---|
/auth/v1/verify | 认证 | /verify | 开放 | 邮箱验证 |
/auth/v1/callback | 认证 | /callback | 开放 | OAuth 回调 |
/auth/v1/authorize | 认证 | /authorize | 开放 | OAuth 授权 |
/auth/v1/.well-known/jwks.json | 认证 | /.well-known/jwks.json | 开放 | 用于第三方验证的 JWKS |
/.well-known/oauth-authorization-server | 认证 | - | 开放 | OAuth 2.0 授权服务器元数据 (RFC 8414) |
/auth/v1/sso/saml/acs | 认证 | /sso/saml/acs | 开放 | SAML 断言消费者 |
/auth/v1/sso/saml/metadata | 认证 | /sso/saml/metadata | 开放 | SAML 元数据 |
/functions/v1/ | functions | / | 绕过 | Edge Functions 运行时执行自己的 JWT 验证;150 秒超时 |
/storage/v1/ | 存储 | / | 绕过 | 存储执行它自己的授权 |
/auth/v1/ | 认证 | / | API 密钥 | 受保护的认证端点 |
/rest/v1/ | 剩余 | / | API 密钥 | PostgREST 开放 API 根(需要密钥) |
/rest/v1/* | 剩余 | / | API 密钥 | PostgREST 数据端点 |
/graphql/v1 | 剩余 | /rpc/graphql | API 密钥 | pg_graphql(添加 Content-Profile: graphql_public) |
/realtime/v1/api/tenants | 实时 | - | 拒绝 | 实时管理 API(默认阻止) |
/realtime/v1/api/openapi | 实时 | - | 拒绝 | 实时 OpenAPI 规范(默认被阻止) |
/realtime/v1/api | 实时 | /api | API 密钥 | 实时 REST API(广播、通道、ping) |
/realtime/v1/ | 实时 | /socket/ | API 密钥 | 实时 WebSocket |
/pg/ | 元数据 | / | API 密钥 | postgres-meta / Studio 数据库操作(需要密钥) |
/api/mcp | 工作室 | - | 拒绝 | MCP 端点(默认通过 RBAC 拒绝阻止) |
/mcp | 工作室 | /api/mcp | 拒绝 | MCP 端点(默认通过 RBAC 拒绝阻止) |
/(通用) | 工作室 | - | 基本认证 | 仪表板;会去掉入站的 Authorization 头 |
默认情况下,网关会拒绝 MCP 路由。要允许本地访问,需要编辑 /mcp 路由上的 RBAC 规则。有关允许模式,请查看 lds.template.yaml 中的内联注释,安全模型请参考 启用 MCP 服务器访问。
🌐 MCP routes are denied at the gateway by default. Allowing local access requires editing the RBAC rule on the /mcp route. See the inline comments in lds.template.yaml for the allowed pattern, and Enabling MCP Server Access for the security model.
身份验证 #
🌐 Authentication
网关处理三个与认证相关的步骤:在通用路由上进行仪表板基本认证、在受保护路由上执行 API 密钥验证,以及在执行前进行不透明到内部密钥的转换步骤。
🌐 The gateway handles three authentication-related steps: dashboard basic auth on the catch-all route, API key enforcement on protected routes, and an opaque-to-internal key translation step that runs before enforcement.
仪表板基本认证 #
🌐 Dashboard basic auth
通配的 / 路由需要使用来自 DASHBOARD_USERNAME 和 DASHBOARD_PASSWORD 的 HTTP 基本认证。Authorization 头会在请求转发到 Studio 之前被移除(不管是什么方案),所以基本认证的凭据永远不会到达上游服务。
🌐 The catch-all / route requires HTTP basic auth with credentials from DASHBOARD_USERNAME and DASHBOARD_PASSWORD. The Authorization header is stripped (regardless of scheme) before the request is forwarded to Studio, so the basic auth credentials never reach the upstream service.
在受保护的路由上强制使用 API 密钥 #
🌐 API key enforcement on protected routes
受保护的路由(/auth/v1/、/rest/v1/、/graphql/v1、/realtime/v1/api、/realtime/v1/、/pg/)需要 apikey 头包含一个有效的配置密钥。可接受的密钥是新的不透明 sb_publishable_* 和 sb_secret_* 密钥(转换为内部 JWT)或旧版的 ANON_KEY 和 SERVICE_ROLE_KEY HS256 JWT API 密钥。
🌐 The protected routes (/auth/v1/, /rest/v1/, /graphql/v1, /realtime/v1/api, /realtime/v1/, /pg/) require the apikey header to contain a valid configured key. Acceptable keys are the new opaque sb_publishable_* and sb_secret_* keys (translated to internal JWTs) or the legacy ANON_KEY and SERVICE_ROLE_KEY HS256 JWT API keys.
一个 Lua 过滤器会用 HTTP 401 Unauthorized 拒绝缺失或无效的键。然后,RBAC 过滤器会应用更精细的规则:
🌐 A Lua filter rejects missing or invalid keys with HTTP 401 Unauthorized. An RBAC filter then applies finer-grained rules:
/pg/- 只允许使用service_role键(sb_secret_*或旧版SERVICE_ROLE_KEY)/rest/v1/(确切路径,OpenAPI 架构根)- 只允许使用service_role键/rest/v1/<table>和其他数据 API 路径对所有有效密钥仍然可访问
不透明密钥翻译 #
🌐 Opaque key translation
当配置了新的 API 密钥(sb_publishable_*、sb_secret_*)时,一系列 Lua 过滤器会在请求到达 API 密钥强制执行和上游服务之前,将不透明的密钥转换为对应的预签名内部 JWT。在 /functions/v1/ 上整个链会被跳过:Edge Runtime 会原样接收原始的 apikey 和 Authorization 头。
🌐 When the new API keys (sb_publishable_*, sb_secret_*) are configured, a chain of Lua filters translates opaque keys into the corresponding pre-signed internal JWTs before the request reaches API key enforcement and upstream services. The entire chain is skipped on /functions/v1/: the Edge Runtime receives the original apikey and Authorization headers unchanged.
这个链条按这个顺序运行:
🌐 The chain operates in this order:
- 查询参数复制。 如果请求包含
?apikey=...但没有apikey头部,该值会被复制到apikey头部。这可以统一仅使用查询参数的客户端(例如来自浏览器的 Realtime WebSocket 连接,因为自定义头部不可用)。 - 查询参数翻译。 如果
apikey查询参数包含一个不透明的密钥,它会被替换——无论是在 URL 中还是在apikey头中——为对应的预签名内部 JWT。 - 头部翻译。 如果
apikey头部包含一个不透明的密钥,它会被对应的预签名内部 JWT 替换。 x-api-key镜像。 在实时 WebSocket 路由上,apikey值会被复制到x-api-key头中(Realtime 首先读取它用于 WebSocket 认证)。Authorization合成。 如果客户端在Authorization头中没有发送真实的 JWT(或者只发送了Bearer sb_*值,这不是有效的 JWT),网关会从(可能已翻译的)apikey头合成Authorization: Bearer <apikey>。在实时 WebSocket 路由上会跳过这一步,该路由使用的是x-api-key。
有关不透明密钥与非对称密钥的背景信息,请参见 新 API 密钥与非对称认证。
🌐 For background on opaque vs asymmetric keys, see New API Keys and Asymmetric Authentication.
转发的头信息和跨域资源共享 #
🌐 Forwarded headers and CORS
X-Forwarded 头 #
🌐 X-Forwarded headers
Envoy 会把转发的头信息附加到每个上游请求上,这样下游服务就可以重建最初面向客户端的 URL。
🌐 Envoy attaches forwarded headers to every upstream request so downstream services can reconstruct the original client-facing URL.
| 头部 | 值 |
|---|---|
X-Forwarded-Host | 客户端的 Host 头部(如果尚未存在则添加) |
X-Forwarded-Port | 监听器端口(8000) |
X-Forwarded-Proto | 由 Envoy 根据连接自动设置(http 或 https) |
X-Forwarded-Prefix | 按路由设置为匹配的路径前缀(例如,Storage 的 /storage/v1) |
X-Forwarded-For | 自动设置,因为监听器上启用了 use_remote_address: true |
X-Forwarded-Prefix 是 Storage 用于 S3 签名 v4 验证和构建 TUS 上传位置 URL 所必需的。
跨域资源共享 #
🌐 CORS
网关在虚拟主机层面应用了宽松的 CORS 策略:
🌐 The gateway applies a permissive CORS policy at the virtual-host level:
- 允许所有来源
- 方法:
GET, POST, PUT, PATCH, DELETE, OPTIONS, HEAD, CONNECT, TRACE - 允许所有请求和响应头
- 预检最大存活时间:3600 秒
这与当前的 Supabase 平台行为以及之前基于 Kong 的网关相匹配。Supabase API 的认证边界是 apikey 头,而不是请求来源。
🌐 This matches both the current Supabase platform behavior and the previous Kong-based gateway. The auth boundary for Supabase APIs is the apikey header rather than the request origin.
如果你在 lds.template.yaml 中自定义 cors: 块以启用 allow_credentials: true,你必须将 allow_origin_string_match 限制到特定来源——浏览器(以及 Envoy)会拒绝将凭证与通配符来源一起使用的组合。
🌐 If you customize the cors: block in lds.template.yaml to enable allow_credentials: true, you must restrict allow_origin_string_match to specific origins - browsers (and Envoy) reject the combination of credentials with a wildcard origin.
安全加固 #
🌐 Security hardening
网关已按这些面向生产的设置进行了配置:
🌐 The gateway is configured with these production-oriented settings:
| 设置 | 目的 |
|---|---|
normalize_path: true、merge_slashes: true | 防止绕过 RBAC 前缀规则导致的路径混淆 |
path_with_escaped_slashes_action: REJECT_REQUEST | 拒绝路径中包含 URL 编码斜杠的请求 |
use_remote_address: true | 将 Envoy 视为边缘代理:使用对等连接 IP 作为受信任的客户端地址(而不是信任客户端提供的 X-Forwarded-For),并删除不受信任的 x-envoy-* 请求头 |
headers_with_underscores_action: REJECT_REQUEST | 阻止利用下划线与连字符规范化进行的标题走私攻击 |
per_connection_buffer_limit_bytes: 32768 | 将每个连接的缓冲区内存限制为 32 KiB |
max_active_downstream_connections: 30000 | 下游连接总数的过载管理限制 |
管理界面绑定到 127.0.0.1:9901 | 管理 API 只能从容器内部访问,不能从其他容器或主机访问 |
镶贴到 envoyproxy/envoy:v1.37.2(或更新版本)的镜像 | 包含 Envoy 1.37.x 已发布的安全补丁 |
在 127.0.0.1:9901 的 Envoy 管理界面会暴露 /config_dump,其中包含完整的渲染配置,包括所有 API 密钥、JWT 和明文的基本认证哈希。不要将 9901 端口暴露给其他容器或主机。
🌐 The Envoy admin interface at 127.0.0.1:9901 exposes /config_dump, which contains the fully rendered configuration including all API keys, JWTs, and the basic auth hash in plaintext. Never expose port 9901 to other containers or to the host.
网关只监听普通 HTTP,不进行 TLS 终止。对于公网部署,应在上游反向代理处终止 TLS——Docker 设置提供了 docker-compose.caddy.yml 和 docker-compose.nginx.yml 来实现这个功能。
🌐 The gateway listens on plain HTTP only and does not terminate TLS. For public deployments, terminate TLS at an upstream reverse proxy - the Docker setup ships docker-compose.caddy.yml and docker-compose.nginx.yml for this purpose.
自定义配置 #
🌐 Customizing the configuration
所有路由、过滤器和集群的更改都在 ./volumes/api/envoy/ 下的 YAML 文件中进行。
🌐 All routing, filter, and cluster changes are made in the YAML files under ./volumes/api/envoy/.
- 添加或修改路由。 编辑
lds.template.yaml。路由是有顺序的——将新路由放在通配/路由之前以确保匹配。保留每个路由的basic_auth: disabled用于 API 路由,如果某条路由需要绕过全局策略,请设置适当的 RBAC 覆盖。 - 添加一个新的上游服务。 在
cds.yaml中添加一个集群定义,包含服务的 DNS 名称和端口,然后在路由的cluster:字段中引用它。 - 添加一个新的环境变量。 模板中的占位符使用
${VAR_NAME}形式。如果你添加了一个占位符,需要同时更新docker-compose.envoy.yml(将变量传入容器)和docker-entrypoint.sh(用sed替换它)。 - 正在应用更改。 Envoy 会从文件系统读取渲染后的
lds.yaml。配置更改需要重启容器,以便入口点重新渲染模板:
1sh run.sh restart api-gw本指南不涵盖 Envoy 的路由、过滤器和集群参考。有关完整的配置内容,请参阅 Envoy 文档。
🌐 This guide does not cover Envoy's route, filter, and cluster reference. See the Envoy documentation for the full configuration surface.
管理界面 #
🌐 Admin interface
Envoy 在容器内部的 127.0.0.1:9901 上暴露了一个管理接口,有像 /ready、/clusters、/stats 和 /config_dump 这样的端点。标准的 envoyproxy/envoy 镜像是最小化的(没有 curl 或 wget),所以没有添加工具的话,管理员端点无法通过 docker exec 访问。
🌐 Envoy exposes an admin interface on 127.0.0.1:9901 inside the container, with endpoints like /ready, /clusters, /stats, and /config_dump. The standard envoyproxy/envoy image is minimal (no curl or wget), so the admin endpoints are not reachable via docker exec without adding tooling.
在调试时,你可以通过运行一个短期存在的 curl 容器来查询管理界面,该容器会加入与 supabase-envoy 相同的网络命名空间:
🌐 You can query the admin interface during debugging by running a short-lived curl container that joins the same network namespace as supabase-envoy:
1docker run --rm --network container:supabase-envoy curlimages/curl http://127.0.0.1:9901/clusters1docker run --rm --network container:supabase-envoy curlimages/curl http://127.0.0.1:9901/stats1docker run --rm --network container:supabase-envoy curlimages/curl http://127.0.0.1:9901/config_dump/config_dump 包含明文的秘密信息。切勿将端口 9901 暴露给公共网络或不受信任的主机,也不要在未先移除秘密信息的情况下分享其输出。
故障排除 #
🌐 Troubleshooting
日志 #
🌐 Logs
Envoy 会将访问日志和过滤器输出写入 stdout。可以这样查看它们:
🌐 Envoy writes access logs and filter output to stdout. View them with:
1docker compose logs api-gw访问日志格式是一个标准的组合日志,包含请求方法、原始路径、响应代码和发送的字节数。
🌐 The access log format is a standard combined log with the request method, original path, response code, and bytes sent.
常见问题 #
🌐 Common issues
401 Unauthorized在受保护的路由上。apikey头缺失或与任何配置的密钥不匹配。请确认头的值完全匹配你在.env文件中的ANON_KEY、SERVICE_ROLE_KEY、SUPABASE_PUBLISHABLE_KEY或SUPABASE_SECRET_KEY之一。注意,只有当新的密钥配置完全设置好时,才接受SUPABASE_PUBLISHABLE_KEY和SUPABASE_SECRET_KEY—— 详细信息见 新 API 密钥和非对称认证。403 Forbidden在/pg/。/pg/路由需要 service_role 密钥(SUPABASE_SECRET_KEY或旧版SERVICE_ROLE_KEY)。匿名和可发布密钥会被拒绝。403 Forbidden在/api/mcp或/mcp上。 这些路径默认是阻塞的。请参阅 启用 MCP 服务器访问。403 Forbidden在/realtime/v1/api/tenants或/realtime/v1/api/openapi上。 这些实时管理端点在网关处被设计性阻止,外部客户端即使有有效密钥也无法访问。SignatureDoesNotMatch在 S3 上发起对存储的请求。 确认docker-compose.yml中的存储服务配置包含了REQUEST_ALLOW_X_FORWARDED_PATH=true和STORAGE_PUBLIC_URL。存储会使用网关发送的X-Forwarded-Prefix头来重建原始请求路径,以进行 SigV4 验证。- 带下划线的
400 Bad Request头部。headers_with_underscores_action: REJECT_REQUEST已启用。一些客户端发送像X_Forwarded_For这样的带下划线的头部;这些会被拒绝。头部名称请使用连字符。
另请参阅 #
🌐 See also
- 新 API 密钥与非对称认证 - 关于不透明密钥和非对称 JWT 的背景
- 配置反向代理和 HTTPS - 在网关前使用 Caddy 或 Nginx
- Envoy 文档 - 过滤器、路由和集群参考