📝 场景还原
在部署私有镜像仓库 Harbor 时,为了节省服务器性能并统一管理证书,我们通常会采用这样的经典架构:
外网 Docker 客户端 (HTTPS) 👉 前置 Nginx 反向代理 (卸载 SSL) 👉 内网 Harbor (纯 HTTP)
在 Harbor 的 harbor.yml 中,我们已经完美地:
- 注释掉了
https模块。 - 将
http端口改为了1280。 - 开启并配置了
external_url: https://harbor.你的域名.com。
Harbor 顺利启动,网页端也能通过 HTTPS 正常访问登录。但是,当我们在客户端尝试拉取镜像时:
docker pull harbor.xiaocaicai.com/public/smartping
却无情地收到了这样一段报错:
Error response from daemon: Head "https://harbor.xiaocaicai.com/v2/public/smartping/manifests/latest": Get "https://10.255.90.18:1280/service/token…": http: server gave HTTP response to HTTPS client
🔍 剥开报错看本质:为什么会暴露内网 IP?
仔细看报错信息,你会发现一个极其诡异的现象:Docker 客户端本来请求的是域名,为什么报错里会吐出一段 https://内网IP:内部端口 的地址?
这其实是 Docker 的认证机制和 Nginx 配置不当共同造成的:
- Docker 客户端向 Harbor 请求拉取镜像。
- Harbor 收到请求后,告诉客户端:“你没有权限,请先去我指定的 URL 获取一个 Token”。
- 【核心问题】 由于你的前置 Nginx 在转发流量时,没有把用户真实访问的域名(Host)和协议(HTTPS)传递给内网的 Harbor。
- 内网 Harbor 误以为用户就是直接通过内网 IP (
10.255.90.18:1280) 访问的,于是它用这个内网 IP 拼凑出了一个获取 Token 的链接返回给客户端。 - 并且,因为外部强制走的是 HTTPS,导致拼接出的链接变成了畸形的
https://内网IP:HTTP端口。客户端去请求这个畸形链接,自然就爆出了http: server gave HTTP...的错误。
🛠️ 终极解决方案:补全 Nginx 代理头
要解决这个问题,一毛钱都不用改 Harbor 的配置,核心在于修改你前置 Nginx 的配置文件。
我们需要让 Nginx 在做反向代理时,把“真实情况”如实汇报给 Harbor。打开你的 Nginx 配置,找到代理 Harbor 的 server 块,确保 location / 中包含以下完整的配置:
server {
listen 443 ssl;
server_name harbor.xiaocaicai.com;
# ... 这里是你的 SSL 证书配置 ...
location / {
# 转发到内网 Harbor 的 HTTP 端口
proxy_pass http://10.255.90.18:1280;
# 🔴【解决当前报错的核心 1】强制把外网访问的真实域名传递给 Harbor
proxy_set_header Host $http_host;
# 🔴【解决当前报错的核心 2】强制告诉 Harbor 用户是从 HTTPS 进来的
proxy_set_header X-Forwarded-Proto $scheme;
# 传递用户的真实 IP
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# 🟢【附赠避坑 1】解除 Nginx 上传文件大小限制!
# 如果不加这行,以后 docker push 推送稍大的镜像层会直接报 413 Request Entity Too Large 错误
client_max_body_size 0;
# 🟢【附赠避坑 2】支持 WebSocket!
# 如果不加这两行,你在 Harbor 网页端执行“复制迁移”等任务时,看不到实时滚动的日志
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}
修改完成后,重载 Nginx 配置:
nginx -s reload
再次回到客户端执行 docker pull harbor.xiaocaicai.com/public/smartping,镜像就能如丝般顺滑地拉取下来了!
💡 进阶避坑:证书不受信任怎么办?
如果你配置完上面的 Nginx 后,报错变成了:
x509: certificate signed by unknown authority
原因: 说明你前置 Nginx 配置的 HTTPS 证书是自己签发的(或者过期了),Docker 客户端默认不信任这种野鸡证书。
解法: 在你需要执行 docker pull / push 的那台机器上(不是 Harbor 服务器),编辑 /etc/docker/daemon.json 文件,添加你的域名到不安全仓库列表中:
{
"insecure-registries": ["harbor.xiaocaicai.com"]
}
保存后重启 Docker 服务 (systemctl restart docker) 即可解决。
(注:如果你 Nginx 上用的是阿里云、Let's Encrypt 等正规机构颁发的免费/付费证书,则完全不会遇到这个问题,直接拉取即可!)
总结: 凡是带有 Web UI 且有认证机制的容器服务(如 Harbor、GitLab、Nextcloud),在配置反向代理时,Host、X-Forwarded-Proto 和 client_max_body_size 0 永远是必不可少的“三板斧”。希望能帮到同样在踩坑的你!