5789 字
29 分钟
用 Docker 部署 Nginx 反向代理与自动 HTTPS
2026-11-15

前几篇里我们已经把 Docker 装好、把容器跑起来了(没装的先看 《如何在Linux部署Docker&Compose》)。但跑起来只是第一步:服务监听在 8080、3001、4000 这些端口上,要访问就得记一串 IP 加端口,既难记又不安全——密码、数据全在明文 HTTP 里跑,浏览器还动不动弹「不安全」警告。这篇就解决这个问题:用反向代理给所有服务统一套上域名和 HTTPS。

全文给两条路线:路线 A 是原生 Nginx + Certbot,配置全在自己手里,适合想搞懂原理、服务不多的场景;路线 B 是 Nginx Proxy Manager(下文简称 NPM),图形化点点鼠标就能上线,适合服务多、怕折腾配置的场景。两条路线最后有对比表,先通读再选一条动手即可。

为什么需要反向代理#

先说清楚反向代理到底解决了什么问题。假设你的服务器上跑了三个容器服务:

服务容器端口直接访问的方式
博客8080http://服务器IP:8080
监控面板3001http://服务器IP:3001
网盘5212http://服务器IP:5212

反向代理是站在这些服务前面的一层「门卫」:外部请求统一打到服务器的 80/443 端口,门卫根据请求的域名把流量转发给后面对应的服务。它带来四个实实在在的好处:

  1. 一个入口分发多个服务。 80 和 443 是仅有的两个需要对外开放的端口,门卫按域名分流:blog.example.com 去博客、vps.example.com 去监控面板,一个 IP 承载任意多个站点。
  2. 统一 HTTPS。 证书只在门卫这一层申请和续期,后面的服务继续跑普通 HTTP 就行,不用每个服务单独折腾证书。
  3. 隐藏内部端口。 后端服务可以只监听本机回环地址或 Docker 内网,外面根本扫不到 8080、3001 这些端口,攻击面小很多。
  4. 集中做通用处理。 上传大小限制、超时、WebSocket 转发、访问日志,全在门卫一处配置,所有服务共享。

前置准备#

动手之前把三件事备齐,后面能少踩一半的坑。

域名解析到服务器#

在你的域名 DNS 管理里,给要用的域名加 A 记录指向服务器公网 IP,比如:

主机记录记录类型记录值
blogA你的服务器公网 IP
vpsA你的服务器公网 IP

解析生效需要几分钟到几十分钟。验证是否生效,在本地执行:

Terminal window
ping blog.example.com

返回的 IP 是你服务器的 IP 就说明解析到位了。这一步没到位之前不要去申请证书,Let’s Encrypt 会因为验证不到域名归属直接拒绝,失败次数多了还会触发限额(后面排障部分会细说)。

放行 80 和 443 端口#

两个地方都要放行,缺一个都不行:

  • 云厂商安全组(如果你的服务器在云上):入方向放行 TCP 80、443。
  • 本机防火墙(如果开了 ufw / firewalld):
Terminal window
# ufw
ufw allow 80/tcp
ufw allow 443/tcp
# firewalld
firewall-cmd --permanent --add-service=http
firewall-cmd --permanent --add-service=https
firewall-cmd --reload

80 端口不只是给 HTTP 访问用的,Let’s Encrypt 的 HTTP-01 验证也要走 80 端口,80 不通 = 证书申请必失败,这是新手最常见的翻车点。

被反代的服务先「藏好」#

反代上线之后,后端服务就不应该再直接暴露公网了。两种做法:

  • 服务跑在宿主机或端口映射时只绑回环地址:Compose 里把端口映射写成 127.0.0.1:8080:80,这样只有本机能连,外网连不上,流量只能从反代进来。
  • 服务和反代在同一个 Docker 网络里:端口干脆不映射(不写 ports),只通过容器名在内网互通。

先有个概念就行,路线 A 用第一种衔接方式(同网络容器名互通也会讲到),路线 B 会专门讲它惯用的网关地址写法。

路线 A:原生 Nginx + Certbot#

这条路线的思路是:Nginx 容器负责反代,Certbot 容器负责申请和续期 Let’s Encrypt 证书,两者通过共享目录交换证书文件和验证文件。

目录结构#

先建好目录,后面所有路径都以此为准:

Terminal window
mkdir -p /opt/nginx-proxy/{conf.d,certbot/www,certbot/conf}
cd /opt/nginx-proxy
目录用途
conf.d/Nginx 的站点配置文件,挂进容器
certbot/www/ACME 验证文件目录,Nginx 和 Certbot 共享
certbot/conf/证书存放目录,Certbot 写、Nginx 读

docker-compose.yml#

services:
nginx:
image: nginx:alpine
container_name: nginx-proxy
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- ./conf.d:/etc/nginx/conf.d:ro
- ./certbot/www:/var/www/certbot:ro
- ./certbot/conf:/etc/letsencrypt:ro
certbot:
image: certbot/certbot:latest
container_name: certbot
volumes:
- ./certbot/www:/var/www/certbot
- ./certbot/conf:/etc/letsencrypt

两个细节注意:

  • Nginx 侧的三个挂载都加了 :ro(只读),Nginx 只需要读配置和证书,不给写权限更安全;Certbot 侧不能加 :ro,它要往里面写证书和验证文件。
  • certbot 服务没有 command,它是一次性工具容器,需要用的时候用 docker compose run --rm certbot ... 临时拉起来执行命令,执行完自动删除,这正是下面申请证书的用法。

第一步:先写 HTTP 版站点配置#

关键顺序问题:证书还没申请下来之前,Nginx 不能加载引用证书文件的 HTTPS 配置,否则启动直接报错。所以流程是先用纯 HTTP 配置把 Nginx 跑起来 → 用 Certbot 申请证书 → 再换成 HTTPS 配置并重载。

在 conf.d/ 里新建 blog.conf,先写 HTTP 版本(以把本机 8080 端口的博客服务反代到 blog.example.com 为例):

server {
listen 80;
server_name blog.example.com;
# ACME 验证路径:Let's Encrypt 会来这里取验证文件,必须放在 location / 之前并单独声明
location /.well-known/acme-challenge/ {
root /var/www/certbot;
}
location / {
proxy_pass http://172.17.0.1:8080;
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 $scheme;
}
}

这份配置逐行讲清楚,这是全篇最核心的部分:

  • listen 80;:监听 HTTP 端口。
  • server_name blog.example.com;:这个站点块只处理域名为 blog.example.com 的请求,这就是「按域名分流」的实现方式——多个服务就写多个 server 块,各自认领一个域名。
  • location /.well-known/acme-challenge/:Let’s Encrypt 的 HTTP-01 验证路径。验证时它会访问 http://blog.example.com/.well-known/acme-challenge/某个随机文件名,Certbot 会把验证文件写到共享目录里,Nginx 从这里读到并返回,验证就通过了。这个 location 缺失或被其他规则拦截,是证书申请失败的第二大原因。
  • proxy_pass http://172.17.0.1:8080;:把请求转发到后端服务。这里的 172.17.0.1 是 Docker 默认网桥在宿主机一侧的网关地址,也就是说后端服务是「监听在宿主机 8080 端口」的情况(比如另一个 Compose 项目映射了 127.0.0.1:8080)。如果后端服务和 Nginx 在同一个 Compose、同一个网络里,这里可以直接写容器名加容器端口,如 proxy_pass http://blog:80;,靠 Docker 内置 DNS 解析。
  • proxy_set_header 四件套,每一项都不能省:
    • Host $host:把用户实际访问的域名原样告诉后端。不传的话后端收到的 Host 是 172.17.0.1:8080,很多应用会据此生成跳转链接、校验来源,轻则链接全错,重则直接拒绝请求。
    • X-Real-IP $remote_addr:把访客的真实 IP 传给后端。经过反代之后,后端看到的连接来源全是 Nginx 的地址,没有这一行,后端日志里所有访客 IP 都会变成同一个网关地址,限流、封禁、审计全没法做。
    • X-Forwarded-For $proxy_add_x_forwarded_for:和上一行的分工不同,这个头记录的是完整的代理链(访客 IP 经过每一层代理依次追加),后端框架普遍优先读这个头来还原真实 IP。$proxy_add_x_forwarded_for 的含义是「在已有的 X-Forwarded-For 后面追加上一层的地址」,而不是覆盖,多层代理时不会丢链。
    • X-Forwarded-Proto $scheme:告诉后端「用户到 Nginx 这一段用的是 http 还是 https」。等启用 HTTPS 后这一行尤其关键:后端如果不知道前面是 HTTPS,会生成 http 的跳转地址,用户就会被甩回明文或陷入重定向死循环(登录后反复跳回登录页是典型症状)。

写好后启动 Nginx:

Terminal window
docker compose up -d nginx
docker compose logs nginx # 确认没有配置报错

第二步:用 Certbot 申请证书#

Terminal window
docker compose run --rm certbot certonly --webroot \
-w /var/www/certbot \
-d blog.example.com \
--email your@email.com \
--agree-tos \
--no-eff-email

参数说明:

  • certonly:只申请证书,不自动改任何配置(配置我们自己写,可控)。
  • --webroot -w /var/www/certbot:用 webroot 验证方式,把验证文件写到共享目录,正好对应上一步 Nginx 配置里的 ACME 路径。
  • -d blog.example.com:要申请证书的域名,多个域名可以继续加 -d。
  • --email:证书快到期时 Let’s Encrypt 会发提醒邮件到这个邮箱。

成功后证书会出现在 certbot/conf/live/blog.example.com/ 下,关键的两个文件是 fullchain.pem(证书链)和 privkey.pem(私钥)。

WARNING

第一次申请前务必确认两件事:域名已经解析到这台服务器、80 端口从外网能通。验证失败会计入 Let’s Encrypt 的限额:同一域名组合 一周内失败验证和重复签发都有次数上限(重复证书每周 5 张、失败验证每小时 5 次),触发限额只能等窗口过期,急也没用。所以别拿生产域名反复试错,先把前置准备做扎实。

第三步:换上 HTTPS 配置并加 HTTP 跳转#

把 conf.d/blog.conf 替换成完整版本:

server {
listen 80;
server_name blog.example.com;
location /.well-known/acme-challenge/ {
root /var/www/certbot;
}
# 其余 HTTP 请求一律 301 跳转到 HTTPS
location / {
return 301 https://$host$request_uri;
}
}
server {
listen 443 ssl;
server_name blog.example.com;
ssl_certificate /etc/letsencrypt/live/blog.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/blog.example.com/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
location / {
proxy_pass http://172.17.0.1:8080;
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 $scheme;
}
}

新增的部分:

  • 第一个 server 块只干两件事:保留 ACME 验证路径(续期还要用它,删了续期就失败),其余请求 return 301 https://$host$request_uri; 永久跳转到 HTTPS,$request_uri 保证跳转后路径和参数原样保留。
  • 第二个 server 块监听 443 ssl,用 ssl_certificate / ssl_certificate_key 指向刚申请下来的证书,ssl_protocols 只允许 TLS 1.2 和 1.3,老旧协议全部关掉。
  • 反代的四件套和 HTTP 版一字不差,注意此时 $scheme 的值变成了 https,后端靠它就知道外层是加密连接。

改好后先验配置再重载,不要直接重启:

Terminal window
docker compose exec nginx nginx -t # 语法检查,必须看到 successful
docker compose exec nginx nginx -s reload

浏览器访问 https://blog.example.com,地址栏有锁、证书信息里签发者是 Let’s Encrypt,就算上线了。再访问 http://blog.example.com 应该会被自动跳到 HTTPS。

第四步:证书自动续期#

Let’s Encrypt 证书有效期只有 90 天,自动续期是这条路线的必修课。原理是定期跑 certbot renew(它只会续期离到期不足 30 天的证书,没到期跑了也无副作用),续期成功后重载 Nginx 让新证书生效。用宿主机的 cron 实现最稳妥:

Terminal window
crontab -e

加入一行(每周一凌晨 3 点执行,路径换成你的实际目录):

0 3 * * 1 cd /opt/nginx-proxy && docker compose run --rm certbot renew --quiet && docker compose exec nginx nginx -s reload >> /var/log/certbot-renew.log 2>&1

用 systemd 的机器也可以写 timer,效果一样,二选一即可。续期机制上线后建议手动演练一次,确认整条链是通的:

Terminal window
cd /opt/nginx-proxy
docker compose run --rm certbot renew --dry-run

--dry-run 是向 Let’s Encrypt 的测试环境走一遍完整续期流程,不消耗正式限额,输出里看到 Congratulations, all simulated renewals succeeded 就说明解析、80 端口、验证路径都没问题。

WARNING

续期失败最常见的两个原因:一是有人「优化」配置时把 /.well-known/acme-challenge/ 那个 location 删了或被跳转规则吞了;二是 80 端口被关了。所以 HTTP server 块里的 ACME 路径和 301 跳转要并存——验证路径优先匹配自己的 location,其余才走跳转,上面配置的写法已经处理好了,不要把 return 301 写到 server 块顶层去,那样验证路径也会被跳走。

路线 B:Nginx Proxy Manager#

不想碰配置文件的走这条:NPM 把 Nginx 包装成了带 Web 后台的成品,反代、证书、跳转全部在网页上点选完成。

docker-compose.yml#

services:
npm:
image: jc21/nginx-proxy-manager:latest
container_name: nginx-proxy-manager
restart: unless-stopped
ports:
- "80:80" # HTTP 入口
- "443:443" # HTTPS 入口
- "81:81" # 管理后台
volumes:
- ./data:/data
- ./letsencrypt:/etc/letsencrypt
Terminal window
docker compose up -d

data 卷存 NPM 的全部配置和数据库,letsencrypt 卷存它申请的证书,两个都要持久化,重建容器不会丢配置。

首次登录:先改密码#

浏览器打开 http://服务器IP:81,用官方默认账号登录:

字段默认值
邮箱admin@example.com
密码changeme
WARNING

admin@example.com / changeme 是 NPM 官方公开的默认账号密码,任何人都知道。登录后系统会强制要求你先完善资料并修改密码,不要跳过、不要拖延:81 端口等于把全站流量的总闸开在了公网,默认密码不改就是裸奔。改完密码后,进一步建议把 81 端口也收一下:只绑 127.0.0.1:81:81 再通过内网或隧道访问,或者在防火墙里只放行你自己的 IP。

添加一个反代站点(Proxy Host)#

登录后点顶部的 Hosts → Proxy Hosts → Add Proxy Host,在 Details 页填:

字段填法
Domain Namesblog.example.com(一个站点可以填多个域名)
Schemehttp(指 NPM 到后端这一段的协议,后端跑的是 HTTP 就选 http)
Forward Hostname / IP172.17.0.1(理由见下一小节)
Forward Port后端服务在宿主机上的端口,如 8080
Block Common Exploits勾选(拦截常见攻击特征,没副作用)
Websockets Support需要 WebSocket 的服务勾选(原理见 WebSocket 小节)

保存后先用 http://blog.example.com 访问验证反代本身通不通,通了再去开证书,不要两个变量一起改,出问题不好定位。

一键申请证书并强制 HTTPS#

编辑刚建的 Proxy Host,切到 SSL 页:

  1. SSL Certificate 下拉选 Request a new SSL Certificate;
  2. 打开 Force SSL(等于路线 A 里手写的 301 跳转);
  3. 打开 HTTP/2 Support;
  4. 填你的邮箱,勾选同意 Let’s Encrypt 条款,保存。

NPM 会自动走完验证、签发、配置、重载的全过程,十几秒后 https://blog.example.com 就能访问了。证书续期也由 NPM 内部自动处理,不用再单独配 cron——这是图形化路线最省心的地方。

上游地址为什么填 172.17.0.1#

这是 NPM(以及一切独立部署的反代)最值得讲清楚的一个细节。添加 Proxy Host 时,后端地址有两种填法:

  • 填容器名:要求 NPM 和后端服务处在同一个 Docker 网络里,NPM 才能靠内置 DNS 解析到容器名。后果是每接一个新服务,就要把 NPM 拉进那个服务的网络,服务多了 NPM 身上挂着一堆网络,配置互相耦合,删服务、改网络时还容易误伤总闸。
  • 填 172.17.0.1:<端口>(本篇推荐):172.17.0.1 是 Docker 默认网桥 docker0 在宿主机一侧的网关地址,从任何容器里访问它,就等于访问宿主机本身。所以后端服务只要把端口映射到宿主机(配合前面说的绑 127.0.0.1 更安全),NPM 用「网关地址 + 宿主机端口」就能找到它,完全不用把 NPM 拉进各业务网络。反代和业务彻底解耦:业务随便增删改,反代这边只改一行转发地址。

代价只有一个:后端服务必须在宿主机上有端口映射(哪怕只绑 127.0.0.1 也行,网关地址同样能连通回环绑定的端口)。对绝大多数个人部署来说,这个代价可以忽略,换来的是干净的网络结构。

WebSocket 支持#

很多现代应用(在线终端、实时面板、聊天类服务、各类 Dashboard 的实时数据)都用 WebSocket 长连接。WebSocket 是靠 HTTP 的 Upgrade 机制升级出来的协议,反代默认不转发升级头的话,表现就是「页面能开,但实时功能连不上、一直转圈重连」。

路线 A(原生 Nginx) 需要在对应站点的 location / 里加三行:

location / {
proxy_pass http://172.17.0.1:8080;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
# 原来的四件套继续保留
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 $scheme;
}
  • proxy_http_version 1.1;:Upgrade 机制要求 HTTP/1.1,而 Nginx 反代默认用 HTTP/1.0,这行不加后面两行等于白写。
  • Upgrade $http_upgrade:把客户端发来的 Upgrade 头原样传给后端。
  • Connection "upgrade":告诉后端这条连接要升级成 WebSocket。

路线 B(NPM) 就是添加/编辑 Proxy Host 时勾选那个 Websockets Support 开关,它在底层生成的正是上面这组配置。一行代码和一个勾的区别,这就是两条路线风格差异的缩影。

其他常用配置项#

这几项在路线 A 里是往 location / 或 server 块里加行,路线 B 里 NPM 的 Proxy Host 有个 Advanced 页,填的就是原生 Nginx 配置片段,语法完全一样,抄过去即可。

放开上传大小。 Nginx 默认请求体上限只有 1MB,网盘类服务传个稍大的文件就报 413 Request Entity Too Large:

client_max_body_size 1024m; # 按需改,0 表示不限制(不建议)

放长读超时。 备份导出、AI 对话、大文件处理这类接口经常跑几分钟,默认 60 秒读超时会让请求被反代掐断、前端报 504:

proxy_read_timeout 600s;
proxy_send_timeout 600s;

静态资源缓存头。 让浏览器缓存图片、CSS、JS,减轻后端压力、打开更快:

location ~* \.(jpg|jpeg|png|gif|ico|css|js)$ {
proxy_pass http://172.17.0.1:8080;
expires 7d;
add_header Cache-Control "public";
}

注意这类带正则的 location 要和 location / 并列写,Nginx 会优先匹配正则规则。动态内容千万别套缓存头,会把 A 用户的页面缓存给 B 用户看。

排障全流程#

502 Bad Gateway#

502 的含义非常明确:反代本身活着,但它连不上或读不懂后端。按这个顺序查,基本五分钟内定位:

  1. 后端服务活没活。 docker ps 看容器在不在跑;直接在服务器上 curl -v http://127.0.0.1:8080(换成你的后端地址端口),本机都访问不通就先修后端,反代不用看了。
  2. 转发地址和端口对不对。 对照 Proxy Host / 配置文件里的地址端口和后端实际监听的是否一致,尤其注意「容器内端口」和「映射到宿主机的端口」是两回事:走 172.17.0.1 时填的是宿主机映射端口。
  3. 看反代的错误日志。 路线 A:docker compose logs nginx 或容器内 /var/log/nginx/error.log;路线 B:docker logs nginx-proxy-manager。日志会直说原因,常见三类:
    • connect() failed (111: Connection refused):后端没监听这个端口,回到第 1、2 步。
    • no live upstreams:同上,本质还是连不上。
    • upstream sent too big header:后端返回头过大,给 location 加 proxy_buffer_size 128k; 和 proxy_buffers 4 256k; 再重载。

还有一个隐蔽情况:后端绑了 127.0.0.1、本机 curl 也通,但走网关地址就是不通——检查后端端口映射绑的是不是 127.0.0.1,绝大多数发行版上回环绑定对 172.17.0.1 过来的连接同样有效;个别环境不行的话,改绑 0.0.0.0 并用防火墙封掉外网访问该端口即可。

证书申请失败#

按可能性从高到低排查:

  1. 域名没解析过来。 ping 域名 看 IP 对不对;刚改的解析没生效就等等,别反复重试耗限额。
  2. 80 端口不通。 云安全组和本机防火墙两处都查;可以在别的网络用手机流量打开 http://域名 验证(能打开反代的默认页或后端都行,关键是通)。
  3. ACME 路径被拦截。 路线 A 检查 /.well-known/acme-challenge/ 的 location 还在不在、有没有被 301 跳转吞掉;路线 B 一般不会,但如果 Advanced 里写过自定义配置要重点怀疑。
  4. 触发 Let’s Encrypt 限额。 错误信息里出现 rate limit 就是它,只能等(重复证书限额按周滚动,失败验证按小时)。等待期间想先上线,可以临时用自签证书顶着,或者换一个子域名继续。

证书过期与有效期检查#

怀疑证书过期或想确认续期是否生效,用这两条命令在任何机器上都能查:

Terminal window
# 看证书的生效与到期时间
echo | openssl s_client -connect blog.example.com:443 -servername blog.example.com 2>/dev/null | openssl x509 -noout -dates
# 或者从 curl 的握手信息里看
curl -vI https://blog.example.com 2>&1 | grep -E "expire|SSL certificate verify"

notAfter 是到期时间。两条路线分别这样查:

路线查法
路线 A续期后到期时间没变,说明续期或重载环节断了:先看 cron 有没有执行(grep certbot /var/log/syslog 或你重定向的日志文件),再手动跑一遍 renew --dry-run 看报错
路线 B去后台的 SSL Certificates 页面看每个证书的到期状态,红色就是已过期或续期失败,点进去能看到错误原因

两条路线对比与选型#

对比项路线 A:原生 Nginx + Certbot路线 B:Nginx Proxy Manager
上手方式写配置文件Web 后台点选
上线速度30 分钟左右(第一次)10 分钟左右
证书申请命令行手动走 webroot 流程后台一键申请
证书续期自己配 cron / timer内置自动续期
WebSocket手写三行 Upgrade 配置勾选开关
可控程度最高,Nginx 的任何能力都能用常用功能够全,高级玩法靠 Advanced 补配置
排障方式看配置文件和 error.log,最直接先看后台状态,再看容器日志
额外攻击面无(只有 Nginx 本身)多一个管理后台,必须改默认密码、收好 81 端口
适合人群服务少、想学原理、喜欢一切在代码里服务多、经常增删站点、想省事

选型建议很简单:只有一两个服务、且愿意花半小时把原理搞透,选路线 A,这套配置知识在任何公司、任何环境都通用;服务三五个以上、以后还会不断加新服务,选路线 B,把时间花在服务本身而不是反代配置上。两条路线并不互斥——不少人就是用 NPM 起步,等某个站点需要精细调优时,再把那一个站点迁到原生 Nginx 上单独伺候。

小结#

  • 反向代理的价值是「一个入口、统一 HTTPS、隐藏后端」,域名分流靠 server_name(路线 A)或 Proxy Host(路线 B)实现。
  • 动手前先把解析、80/443 放行、后端绑回环这三件前置做完,证书申请的坑能避掉大半。
  • 路线 A 的正确顺序是:HTTP 配置先行 → webroot 申请证书 → 换 HTTPS 配置 → cron 自动续期,顺序错了 Nginx 起不来。
  • proxy_set_header 四件套缺一不可,真实 IP 和协议头丢了,后端的日志、限流和跳转都会出问题。
  • NPM 的上游填 172.17.0.1:<端口> 是解耦的关键:后端只管映射端口,反代永远不用钻进业务网络。
  • 出问题先分清层:502 查后端连通性,证书失败查解析和 80 端口,命令和日志都在排障小节里备好了。
用 Docker 部署 Nginx 反向代理与自动 HTTPS
https://blog.anexns.com/posts/docker-nginx-https/
作者
Anexns
发布于
2026-11-15
许可协议
CC BY-NC-SA 4.0