解决 Mastodon Docker 容器中 Nginx 重定向循环问题 (ERR_TOO_MANY_REDIRECTS)
如果你在使用 Unraid 或其他 Docker 环境部署 Mastodon,并通过 Cloudflare Tunnel (或其他将 HTTPS 流量转发为 HTTP 的代理) 访问时,可能会遇到经典的 ERR_TOO_MANY_REDIRECTS
错误。这通常是因为容器内部的 Nginx 与外部代理之间的协议识别不一致造成的无限重定向循环。
本文将深入分析这个问题,并提供一个详细的解决方案。
问题现象
你可能已经配置了 Cloudflare Tunnel (或其他反向代理) 来处理 SSL 证书和外部流量,并将流量转发到运行在 Docker 容器内的 Mastodon Nginx。然而,当你尝试访问你的 Mastodon 网站 (例如 https://your.domain.com
) 时,浏览器却显示“重定向次数过多”的错误。
在 Mastodon Docker 容器的日志中,你可能会看到类似 Nginx 报告的 unknown "schemehttp_x_forwarded_proto" variable
错误,或者没有任何错误,但重定向循环依然存在。
问题根源分析
这个问题的核心在于协议识别的混淆。
- 外部代理 (Cloudflare Tunnel) 的作用: Cloudflare Tunnel 会在 Cloudflare 边缘终止 HTTPS 连接。这意味着用户浏览器通过 HTTPS 访问 Cloudflare,然后 Cloudflare Tunnel 会将这些请求转换为 HTTP 并发送到你的 Docker 容器内部的 Nginx。
- 容器内部 Nginx 的默认行为: Mastodon Docker 容器 (特别是基于 LinuxServer.io 的镜像) 内部通常包含一个 Nginx 实例。
- 这个 Nginx 可能默认配置成监听 80 端口 (HTTP) 和 443 端口 (HTTPS)。
- 在处理
proxy_set_header X-Forwarded-Proto
头部时,如果使用了$scheme
变量,Nginx 会根据它实际收到的协议来设置这个头部。当它从 Cloudflare Tunnel 收到 HTTP 请求时,$scheme
的值就是http
。
- Mastodon 应用程序的响应: Mastodon 应用程序会检查
X-Forwarded-Proto
头部来判断原始请求是否通过 HTTPS。- 如果 Mastodon 收到的
X-Forwarded-Proto
是http
(即使原始用户访问的是 HTTPS),它会误以为这是一个不安全的 HTTP 请求。 - 为了安全性,Mastodon 应用程序会尝试将用户重定向到 HTTPS。
- 如果 Mastodon 收到的
- 重定向循环的形成:
- 用户
HTTPS
访问 -> Cloudflare -> Cloudflare Tunnel -> Docker Nginx (收到HTTP
) - Docker Nginx 将
X-Forwarded-Proto: http
发送给 Mastodon 后端 - Mastodon 收到
X-Forwarded-Proto: http
,尝试302/301
重定向到HTTPS
- 重定向响应回到 Docker Nginx -> Cloudflare Tunnel -> Cloudflare -> 用户浏览器
- 用户浏览器再次尝试
HTTPS
访问 (响应了重定向) -> 重复第一步,形成无限循环。
- 用户
之前可能遇到的 Nginx 错误 unknown "schemehttp_x_forwarded_proto" variable
则是 Nginx 配置文件中变量拼写错误导致 Nginx 无法启动的问题。
解决方案
解决此问题的关键是确保 Nginx 正确地将原始请求协议 (HTTPS) 传递给 Mastodon 应用程序,即使 Nginx 实际收到的是 HTTP 请求。我们需要利用 Cloudflare 在转发请求时添加的 X-Forwarded-Proto
头部。
步骤 1: 进入 Mastodon Docker 容器的 Shell
首先,你需要进入你的 Mastodon Docker 容器的命令行界面。
docker exec -it <mastodon_容器名称或ID> bash
提示: 你可以通过 docker ps
命令查看正在运行的容器及其名称或 ID。
步骤 2: 定位并备份 Nginx 配置文件
Mastodon Docker 容器 (特别是 LinuxServer.io 的镜像) 的 Nginx 配置文件通常位于 /config/nginx/site-confs/default.conf
。
- 确认文件存在:Bash
ls /config/nginx/site-confs/default.conf
如果你不确定哪个文件包含 Nginx 配置,可以通过grep
命令来查找监听 80 端口的server
块:Bashgrep -r "listen 80" /config/nginx/site-confs/
输出通常会指向default.conf
。 - 备份配置文件 (强烈推荐): 在进行任何修改之前,务必备份原始文件,以防万一。Bash
cp /config/nginx/site-confs/default.conf /config/nginx/site-confs/default.conf.bak
3. 编辑 Nginx 配置文件
使用 vi
或 nano
(如果容器内有安装) 编辑 /config/nginx/site-confs/default.conf
文件:
vi /config/nginx/site-confs/default.conf
在文件中,找到所有包含 proxy_set_header X-Forwarded-Proto
的行。这通常出现在 location @proxy
和 location ^~ /api/v1/streaming
块中。
你需要将这两行 (或其他任何包含 X-Forwarded-Proto $scheme;
的行) 从:
proxy_set_header X-Forwarded-Proto $scheme;
修改为:
proxy_set_header X-Forwarded-Proto $http_x_forwarded_proto;
正确修改后的示例片段如下:
location ^~ /api/v1/streaming {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $http_x_forwarded_proto; # <-- 修正这里
proxy_set_header Proxy "";
proxy_pass http://streaming;
proxy_buffering off;
proxy_redirect off;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
tcp_nodelay on;
}
location @proxy {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $http_x_forwarded_proto; # <-- 修正这里
proxy_set_header Proxy "";
proxy_pass_header Server;
proxy_pass http://backend;
proxy_buffering on;
proxy_redirect off;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_cache CACHE;
proxy_cache_valid 200 7d;
proxy_cache_valid 410 24h;
proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504;
add_header X-Cached $upstream_cache_status;
tcp_nodelay on;
}
解释:
$scheme
变量表示 Nginx 实际接收到的协议 (在这个场景中是 HTTP)。$http_x_forwarded_proto
变量则捕获了上游代理 (Cloudflare) 传递过来的原始协议 (也就是 HTTPS)。通过使用这个变量,我们确保 Mastodon 应用程序总是知道用户最初是通过 HTTPS 访问的,从而避免了不必要的重定向。
4. 保存并退出
在 vi
中,输入 :wq
然后按回车保存并退出。
5. 重启 Mastodon Docker 容器
退出容器的 Shell,然后从你的 Docker 管理界面 (如 Unraid UI) 或通过命令行重启 Mastodon 容器。
docker restart <mastodon_容器名称或ID>
6. 清除浏览器缓存
重启容器后,务必清除你的浏览器缓存和 Cookie,然后尝试在无痕模式/隐私模式下访问你的 Mastodon 网站。这可以确保你不会受到旧的重定向缓存影响。
避免未来再次出现问题
因为您修改的配置文件 (/config/nginx/site-confs/default.conf
) 位于 /config
目录下,而这个目录通常在 Docker 容器中是通过 卷 (Volume) 挂载到宿主机的。这意味着您的修改是持久化的。在正常的容器升级中,这个文件会保持不变,所以再次遇到相同问题的可能性大大降低。
不过,作为最佳实践,下次升级 Docker 容器前,可以快速查看 Mastodon 容器或其 Nginx 基础镜像的更新日志 (Changelog)。如果日志中提到 Nginx 配置文件的重大修改,你可以稍微留意一下,以防需要重新检查你的设置。