反向代理与 HTTPS
用 Caddy 或 Nginx 前置 ImgYoung,正确配置 IMGYOUNG_TRUSTED_PROXIES、base_url 与上传体积上限。
为什么要前置反向代理
ImgYoung 只监听明文 HTTP,不终结 TLS。对公网提供服务时应在前面放一个反向代理:
- 由反代申请证书、提供 HTTPS;
- 会话 cookie 是否带
Secure、未配置base_url时返回链接的协议,都依据反代传来的X-Forwarded-Proto; - 在入口层拦掉
/metrics,指标只留给内网抓取; - ImgYoung 本身改为只监听回环地址,不直接暴露。
Caddy
Caddy 自动申请并续期证书,默认就会设置 X-Forwarded-For / X-Forwarded-Proto / X-Forwarded-Host,并原样转发 Host。
# Caddy 反向代理示例:Caddy 自动申请并续期 HTTPS 证书。把域名换成你的,DNS 指向本机。
# Caddy 默认就会设置 X-Forwarded-For / -Proto / -Host,并忽略客户端自带的这些头。
# ImgYoung 直接运行在本机(非容器)时,Caddy 的连接来自回环地址,默认受信任,无需额外配置。
# ImgYoung 在容器里时,不论 Caddy 在宿主机还是另一个容器,ImgYoung 看到的对端都是 Docker 网络里的地址,
# 要给 ImgYoung 设 IMGYOUNG_TRUSTED_PROXIES 为该网络的网段(docker network inspect 查看);
# Caddy 也在容器里时,把 reverse_proxy 改成 imgyoung:8721。
img.example.com {
# 指标只给内网抓取,公网入口一律不转发
respond /metrics 404
reverse_proxy 127.0.0.1:8721
}
Nginx
# Nginx 反向代理示例:放进 http {} 块。域名与证书路径换成你的。
# ImgYoung 直接运行在本机(非容器)时,Nginx 的连接来自回环地址,默认受信任,无需额外配置。
# 其余情况要给 ImgYoung 设 IMGYOUNG_TRUSTED_PROXIES 为它看到的对端所在网段:ImgYoung 在容器里时,
# 不论 Nginx 在宿主机还是另一个容器,对端都是 Docker 网络里的地址(docker network inspect 查看);
# Nginx 在别的主机上时,是 Nginx 的地址。
server {
listen 80;
server_name img.example.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
http2 on; # nginx 1.25.1 起的写法;更早的版本改成 listen 443 ssl http2;
server_name img.example.com;
ssl_certificate /etc/nginx/certs/img.example.com/fullchain.pem;
ssl_certificate_key /etc/nginx/certs/img.example.com/privkey.pem;
# 不小于后台「单图上限」(出厂 50MB),多出的部分留给 multipart 表单开销;Nginx 默认只有 1MB
client_max_body_size 60m;
# 指标只给内网抓取,公网入口一律不转发
location = /metrics {
return 404;
}
location / {
proxy_pass http://127.0.0.1:8721;
proxy_set_header Host $host;
# ImgYoung 从 X-Forwarded-For 自右向左取第一个非信任代理的地址作客户端地址,
# 登录限速与 /metrics 放行都按它判定;少了这一行,所有请求都会被当成来自 Nginx 本身
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# 决定会话 cookie 是否带 Secure,以及未配置 base_url 时返回链接的协议与域名
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Host $host;
}
}
示例里的几行 proxy_set_header 都不能省:
Host $host:S3 协议面的签名校验包含Host头,反代改写它会导致 S3 客户端签名不匹配;X-Forwarded-For:缺了它,所有请求都会被当成来自 Nginx 本机,见下一节;X-Forwarded-Proto与X-Forwarded-Host:ImgYoung 取它们的第一个值,所以要用覆盖写法(proxy_set_header),不能把自己的值追加在客户端传来的值后面。
信任的反代:IMGYOUNG_TRUSTED_PROXIES
ImgYoung 从请求推导三件事:客户端地址(登录限速的键、/metrics 的放行判定)、第一跳是否 HTTPS(会话 cookie 的 Secure)、对外域名(未配置 base_url 时拼返回链接)。只有 TCP 连接的对端地址落在 IMGYOUNG_TRUSTED_PROXIES 网段内时,才会采信 X-Forwarded-* 头;变量未设置时取默认值 127.0.0.0/8,::1/128,只信任同机的反代;设为空串(或只有空白)表示一个代理都不信任,转发头一律忽略,后台「引导配置」卡片上显示为「空:不采信任何转发头」。
对端受信任时:
- 依次收集全部
X-Forwarded-For头里逗号分隔的地址,无法解析的项跳过。 - 自右向左逐个检查,跳过落在信任网段内的地址,遇到的第一个不在信任网段内的地址就是客户端地址。
- 整条链都是信任网段内的地址(内网客户端)时,取最左边那个;头缺失或没有可用地址时,取连接对端。
X-Forwarded-Proto取第一个值,等于https(不区分大小写)即视为 HTTPS 访问。X-Forwarded-Host取第一个值,用于推导对外域名。
对端不受信任时,X-Forwarded-* 一律忽略:客户端地址就是连接对端,HTTPS 判定看连接本身(ImgYoung 不终结 TLS,所以恒为否)。
**为什么伪造无效。**每一级代理都把它看到的对端追加到 X-Forwarded-For 末尾,客户端能控制的只有最左边的部分。例如客户端 203.0.113.7 自带 X-Forwarded-For: 1.2.3.4,经同机 Nginx 后变成 1.2.3.4, 203.0.113.7;自右向左第一个非信任地址是 203.0.113.7,伪造的 1.2.3.4 不起作用。直接连 ImgYoung 的请求,对端不在信任网段内,带什么转发头都不会被采信。
按部署拓扑取值:
| 拓扑 | ImgYoung 看到的连接对端 | IMGYOUNG_TRUSTED_PROXIES |
|---|---|---|
| 反代与 ImgYoung(二进制或 systemd)在同一台主机 | 127.0.0.1 / ::1 | 默认值即可 |
| ImgYoung 跑在 Docker 里,反代在宿主机上 | Docker 网桥的网关地址,不是回环 | 网关所在网段,如 172.16.0.0/12;用 docker network inspect 查看实际网关 |
| 反代与 ImgYoung 是同一 Compose 项目里的两个容器 | 反代容器在项目网络里的地址 | 项目网络的网段 |
| 反代在另一台主机 | 反代主机的内网地址 | 该地址或其网段 |
| 反代前面还有 CDN | 反代地址;X-Forwarded-For 里最右边一跳是 CDN 节点 | 反代网段加上 CDN 的回源网段,客户端地址才会解析到真实访客 |
信任配错的表现
连接对端不在信任网段内时,所有经反代的请求都被当成来自反代自身:全站访客共用一个登录限速额度;如果反代地址恰好在 IMGYOUNG_METRICS_ALLOW 内(Docker 网段默认就在),而反代又转发了 /metrics,任何人都能读到指标。反代没有设置 X-Forwarded-For 时也是同样的后果。
只监听回环
有了反代,就不要让 ImgYoung 的端口直接对外:
设置 IMGYOUNG_LISTEN=127.0.0.1:8721。systemd 部署用 sudo systemctl edit imgyoung 加一行 Environment=IMGYOUNG_LISTEN=127.0.0.1:8721 后重启,见配置。
什么时候必须设置 base_url
base_url(后台「设置 → 运行配置 → 访问与链接 → 对外站点地址」)为空时,ImgYoung 按请求推导对外地址:协议取 HTTPS 判定,域名在对端受信任时取 X-Forwarded-Host,否则取 Host。以下情况必须显式设置:
- 对外域名与 ImgYoung 收到的域名不一致:CDN 用另一个域名回源、反代改写了
Host又没传X-Forwarded-Host、反代不在信任网段内等。此时上传接口返回的url、后台复制的分享链接、「S3 客户端配置」弹窗里的端点与链接前缀都会是错的。 - 启用 CDN 缓存清除:清除任务在后台异步执行,没有请求可推导,只认
base_url,见 CDN。 - 网盘渠道一键授权:授权回调地址必须与开放平台登记的地址逐字一致,只用
base_url拼出,见网盘渠道。
上传体积上限要与反代一致
ImgYoung 的单图上限(max_upload_mb,出厂 50 MB)只约束文件本身,恰好等于上限的文件可以上传;整个请求体的上限是单图上限再加 64 KiB,留给 multipart 边界与 exif_policy、ttl 等短字段。反代的请求体上限不应小于这个值:
- Nginx 默认只允许 1 MB,示例里设为
client_max_body_size 60m;; - Caddy 示例没有限制请求体;如果你用
request_body设置了max_size,不要小于单图上限加 64 KiB。
在后台调大单图上限后,记得同步调大反代的限制,否则大图会在反代处直接被 413 拒掉,请求到不了 ImgYoung。