Press "Enter" to skip to content

Docker Pull 报错 server gave HTTP response to HTTPS client?Harbor + Nginx 反向代理终极填坑指南

📝 场景还原

在部署私有镜像仓库 Harbor 时,为了节省服务器性能并统一管理证书,我们通常会采用这样的经典架构:
外网 Docker 客户端 (HTTPS) 👉 前置 Nginx 反向代理 (卸载 SSL) 👉 内网 Harbor (纯 HTTP)

在 Harbor 的 harbor.yml 中,我们已经完美地:

  1. 注释掉了 https 模块。
  2. http 端口改为了 1280
  3. 开启并配置了 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 配置不当共同造成的:

  1. Docker 客户端向 Harbor 请求拉取镜像。
  2. Harbor 收到请求后,告诉客户端:“你没有权限,请先去我指定的 URL 获取一个 Token”。
  3. 【核心问题】 由于你的前置 Nginx 在转发流量时,没有把用户真实访问的域名(Host)和协议(HTTPS)传递给内网的 Harbor
  4. 内网 Harbor 误以为用户就是直接通过内网 IP (10.255.90.18:1280) 访问的,于是它用这个内网 IP 拼凑出了一个获取 Token 的链接返回给客户端。
  5. 并且,因为外部强制走的是 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),在配置反向代理时,HostX-Forwarded-Protoclient_max_body_size 0 永远是必不可少的“三板斧”。希望能帮到同样在踩坑的你!

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注