Sanic 与 Caddy 反向代理部署实战:TLS、HTTP/3 与静态资源卸载
【免费下载链接】sanicAccelerate your web app development | Build fast. Run fast.项目地址: https://gitcode.com/gh_mirrors/sa/sanic
导读
本文基于 Sanic 官方部署指南(guide/content/en/guide/deployment/caddy.md),完整讲解如何让 Sanic 应用在本地端口监听,由 Caddy 作为面向公网的入口服务器:自动签发 Let's Encrypt TLS 证书、支持 HTTP/1.1/2/3、处理反向代理头,并进一步将静态文件托管、Brotli 预压缩与 Unix Socket 通信等高级玩法落到可复制的配置。读完你将掌握一套从单行命令起步、到 Caddyfile 与 Sanic 代理头配置的完整部署方案。
部署架构一览
Caddy 是一款现代 Web 服务器与反向代理,支持最高 HTTP/3 协议。它的简洁性体现在极简配置和内置能力上:自动为你的域名从 Let's Encrypt 申请 TLS 证书,无需手动管理证书续期。
在本部署方案中,分工如下:
| 组件 | 角色 | 监听位置 |
|---|---|---|
| Sanic 应用 | 业务后端,仅监听本地 | 127.0.0.1:8001 |
| Caddy | 面向公网的入口代理 | example.com:443(TCP/UDP) |
Caddy 承担 TLS 终止、HTTP 到 HTTPS 跳转、反向代理头注入、流式传输与 WebSocket 转发,Sanic 只关心业务逻辑。Caddy 可通过各平台(Windows、Linux、Mac)的软件包管理器安装,软件包名为caddy。
第一步:准备一个"可被代理"的 Sanic 应用
以官方指南中的proxied_example.py为例:
from sanic import Sanic from sanic.response import text app = Sanic("proxied_example") @app.get("/") def index(request): # This should display external (public) addresses: return text( f"{request.remote_addr} connected to {request.url_for('index')}\n" f"Forwarded: {request.forwarded}\n" )保存为proxied_example.py,通过 Sanic 命令行接口启动:
SANIC_PROXIES_COUNT=1 sanic proxied_example --port 8001设置SANIC_PROXIES_COUNT环境变量的作用是告诉 Sanic 信任 Caddy 发送的 X-Forwarded-* 请求头,从而正确识别真实客户端的 IP 地址等信息。注意此时--port 8001仅绑定本地回环地址127.0.0.1,外部流量必须经由 Caddy 进入。
代理头在源码层面是如何生效的
SANIC_PROXIES_COUNT对应应用配置中的PROXIES_COUNT,默认值为None(见 sanic/config.py),即默认不信任任何代理头——这本身就是一道安全防线:恶意客户端无法通过伪造X-Forwarded-For来伪装自己的 IP。
当配置了PROXIES_COUNT后,sanic/headers.py 中的parse_xforwarded会:
- 读取
REAL_IP_HEADER指定头部(如CF-Connecting-IP)作为直接来源; - 否则拼接、拆分并过滤
X-Forwarded-For链中的所有 IP(FORWARDED_FOR_HEADER默认即X-Forwarded-For); - 取链中倒数第
PROXIES_COUNT个 IP 作为真实客户端地址:addr = proxies[-proxies_count],即"前面每一跳都是代理,只有倒数第 N 个才是用户"。
随后请求对象的request.forwarded属性会综合 RFC 7239Forwarded头与传统 X-头(x-forwarded-proto、x-forwarded-host、x-forwarded-port、x-forwarded-path、x-scheme),归一化出for、proto、host、port、path字段(实现见 sanic/request/types.py)。request.remote_addr即取forwarded["for"]的值;若要"有代理读代理、无代理读直连",可优先使用request.client_ip(self.remote_addr or self.ip,见 sanic/request/types.py)。
相关配置项在 sanic/config.py 中的默认值如下:
| 配置项 | 默认值 | 说明 |
|---|---|---|
PROXIES_COUNT | None | 信任的代理层数,须为正整数 |
REAL_IP_HEADER | None | 直接携带真实 IP 的头部名 |
FORWARDED_FOR_HEADER | X-Forwarded-For | 传统代理链头部 |
FORWARDED_SECRET | None | RFC 7239Forwarded头的校验密钥 |
更完整的代理头语义(含FORWARDED_SECRET用法与 curl 验证示例)可参见 guide/content/en/guide/advanced/proxy-headers.md。另外注意:PROXIES_COUNT为负数会触发断言错误,对应测试见 tests/test_config.py。
第二步:一行命令完成公网部署
如果你机器上没有运行其他 Web 服务器,可以直接用 Caddy 命令行(Linux 下需要sudo):
caddy reverse-proxy --from example.com --to :8001这条命令背后是一个完整的生产级服务器:为你的域名申请并管理证书、HTTP 自动跳转 HTTPS、注入反向代理头、支持流式传输和 WebSocket。执行后,你的 Sanic 应用即可通过 HTTP/1.1、HTTP/2 与 HTTP/3 三种版本对外提供服务。
提醒:要在公网启用 HTTP/3(基于 QUIC),请记得在防火墙中开放UDP/443端口;仅开放 TCP/443 无法建立 H3 连接。
第三步:从命令行到 Caddyfile
单行命令适合起步,但很快你会需要托管多个站点、或对细节有更多控制——此时应当使用配置文件。上面的命令等价于以下Caddyfile,它也是你部署的起点:
example.com { reverse_proxy localhost:8001 }Caddy 配置的加载方式
部分 Linux 发行版安装 Caddy 后,会自动从/etc/caddy/Caddyfile读取配置,并对每个站点执行import /etc/caddy/conf.d/*。如果你的发行版没有这样的机制,则需要手动将caddy run作为系统服务运行,并指向正确的配置文件:
caddy run --config /path/to/Caddyfile另一种方式是使用 Caddy 的 API 模式持久化修改配置:
caddy run --resume需要特别留意:任何一次 Caddyfile 的加载都会替换掉此前全部配置,因此传统方式下,caddy-api管理接口所保存的配置并不与 Caddyfile 混用。
第四步(进阶):静态资源卸载与 Unix Socket
有时你希望站点根路径同时服务静态文件与业务处理器,获得更干净的 URL。在 Sanic 中可以这样实现:
app.static("/", "static", index="index.html")app.static的完整签名见 sanic/mixins/static.py,除index外还支持stream_large_files(大文件流式传输)、use_content_range(范围请求)、directory_view(目录浏览)等参数。
不过,为了更好的性能,可以把静态文件服务卸载给 Caddy,让 Sanic 专注于动态请求:
app.example.com { # Look for static files first, proxy to Sanic if not found route { file_server { root /srv/sanicexample/static precompress br # brotli your large scripts and styles pass_thru } reverse_proxy unix//tmp/sanic.socket # sanic --unix /tmp/sanic.socket } }这段配置的三个要点:
file_server块:指定静态文件根目录/srv/sanicexample/static;precompress br预先用 Brotli 压缩体积较大的脚本和样式文件并直接下发;pass_thru表示未命中静态文件时继续走后续处理流程,即把请求转交给反向代理;reverse_proxy unix//tmp/sanic.socket:通过 Unix Domain Socket 转发动态请求,省去 TCP 回环开销;- Sanic 侧对应启动方式:
sanic --unix /tmp/sanic.socket,即让应用监听 Unix Socket 而非 TCP 端口。代码中亦可等价地通过app.run(unix="./uds_socket")指定(见 examples/unix_socket.py),Nginx 部署指南中同样给出了 Unix Socket 的上游写法可作参照(见 guide/content/en/guide/deployment/nginx.md)。
验证与排查建议
完成部署后,可以在浏览器或 curl 中检查:
https://example.com/应展示request.remote_addr为真实公网 IP,request.forwarded中包含for/proto等字段,证明代理头配置生效;- 若
remote_addr显示为127.0.0.1之类的代理地址,优先检查SANIC_PROXIES_COUNT是否设置、Caddy 是否正常注入代理头; - 若 HTTP/3 不可用,确认防火墙已开放 UDP/443,并可用支持 QUIC 的浏览器访问验证。
更多 Caddy 指令与高级选项(如负载均衡、访问日志、重写规则),可查阅 Caddy 官方文档;Sanic 侧的代理头语义与FORWARDED_SECRET的强校验用法,则可继续阅读仓库中的 代理配置指南。
【免费下载链接】sanicAccelerate your web app development | Build fast. Run fast.项目地址: https://gitcode.com/gh_mirrors/sa/sanic
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考