尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

Nginx Proxy Manager入门:可视化反向代理与自动HTTPS证书配置实战

Nginx Proxy Manager入门:可视化反向代理与自动HTTPS证书配置实战 一台服务器上跑了好几个 Web 服务日子会很快从美好变成难受端口要记住一大串、证书要么没有要么手动续期、发给同事的访问地址永远是“IP冒号端口”。这种时候大多数人会想到直接用 Nginx 写反向代理配置但配置文件一多改起来就头大。我后来基本固定用 Nginx Proxy Manager以下简称 NPM来解决这类问题它是一个带网页界面的反向代理管理工具能让“添加一个域名、绑定一个后端服务、自动申请 HTTPS 证书”变成点几下鼠标的事。这篇文章会把安装、配置和实战过程完整展开适合正在折腾个人服务器、家庭实验室或者小团队内部系统的朋友照着做就能把整套流程跑通。1. 反向代理利器定位Nginx Proxy Manager 在项目里的真正职责1.1 反向代理到底在“反向”什么先简单说下反向代理是干什么的。你可以把一台服务器理解成一个单位的前台大厅反向代理就是那个前台接待员。来访者只需要知道单位的总机号码和分机规则不需要记住每个员工的工位号和姓名前台拿到请求以后按照“找谁”把问题转给对应的人。对应到服务器上就是用户只访问服务器的 80 和 443 端口NPM 在后台根据请求的域名、路径等信息把流量转发给真正运行着业务应用的其他端口或者容器。这里的关键是“按域名分发”。举个实际场景服务器上同时跑着一个文档系统、一个监控面板、一个 API 服务它们分别监听 8081、3000、9090 端口。如果没有反向代理用户访问时就得输入三个不同的端口端口一多还会撞车更别提 SSL 证书必须和域名绑定而 IP 直连很难申请到受信任的免费证书。有了 NPM 以后你只要把三个子域名分别指向这台服务器再在后台把每个域名对应到不同端口用户就只需要记住一组域名访问体验瞬间正常了。NPM 本质上是把 Nginx 的能力封装成一套可视化操作界面。它保留了 Nginx 强大的转发能力又把最常用的功能抽成了表单和按钮所以新手不需要背诵一整套配置语法也能完成八成以上的日常需求。1.2 为什么选择 NPM而不是手工改 Nginx 配置直接手写 Nginx 配置并不是不行很多老牌运维也这么干了十几年。但对个人和小团队来说手工方案有几个很现实的问题第一配置语法写错了可能整个网站直接打不开而且在浏览器报错之前你通常需要先跑nginx -t才能发现第二每次新增一个服务都要在配置目录里新建文件、软链接到 sites-enabled、再重载服务这套流程说简单也简单但次数多了总会出纰漏第三SSL 证书的申请和续期如果全手动操作非常容易过期没人管。NPM 把这些高频操作都收进了网页里。在新增代理主机时你只需要填域名、内部转发地址和端口然后勾选申请 Lets Encrypt 证书保存以后证书的申请、部署、续期就都自动完成了。这对于没有专职运维的场景来说非常省心。而且它支持在同一台服务器上同时管理很多组代理规则每一个都能单独配置访问控制、SSL 参数和自定义 Nginx 指令灵活度足够覆盖绝大多数项目需求。需要说明的是NPM 毕竟还是在 Nginx 外面包了一层工具如果遇到特别复杂的路由逻辑比如同一个域名下要做非常精细的路径分流、灰度发布或者多级重写它还不一定能完全替代手工配置。但反过来绝大多数中小规模场景根本用不到这么复杂的活儿NPM 的图形化方案反而更高效、更不容易出错。1.3 适合的使用场景与不适合的场景从我实际接触的项目来看NPM 最适合这几类情况一台服务器上部署了多个 Docker 容器服务希望统一出入口家里拥有一台小主机跑着各种自托管应用想用好看的子域名代替端口号开发环境里需要把本地服务临时暴露给测试人员并且需要快速加上 HTTPS小团队内部搭建了几个运维工具需要集中管理访问入口。它不适合的场景我也说一下。如果服务规模大到了需要服务网格、完善的灰度发布、按权重切流量的程度NPM 就不是首选了如果应用中包含很多极其特殊、非标准的 Nginx 魔改写法你也需要评估一下 UI 能否完整表达。判断方法很简单你的需求能不能用“域名 后端地址 端口 SSL 几条附加指令”描述清楚能NPM 就很合适不行再考虑更重型的方案。2. 部署前准备与 Docker Compose 安装全流程2.1 安装前先想清楚三件事端口、域名、网络很多人在安装 NPM 时会踩的第一个坑是根本没想清楚端口规划。NPM 默认要占用三个端口80 用于 HTTP 流量转发443 用于 HTTPS 流量转发81 是它的管理面板端口。这三者缺一不可尤其是 80 和 443你一旦要在公网上做代理和证书申请就必须保证这两个端口不被别的服务占用。域名方面要提前准备好一个真实可解析的域名。为什么必须是真实域名因为 NPM 的自动 SSL 证书功能基本依赖 Lets Encrypt而证书签发机构需要验证你对域名的控制权通常是通过 HTTP-01 验证也就是访问http://你的域名/.well-known/acme-challenge/...。如果域名没有解析到这台服务器的公网 IP或者 80 端口被阻塞申请流程就会失败。如果你只是在内网里做实验也可以用自签名证书或者内网 CA但那个流程更繁琐本文还是以公网域名为例来讲。网络规划指的是 NPM 容器和业务容器之间的连通方式。这点在初次使用时特别容易被忽略默认情况下每个 docker-compose 项目会创建自己独立的网络两个项目里的容器如果没有显式加入同一个网络彼此之间是互相隔离的。所以我会建议在部署 NPM 之前先手动创建一个公共的 Docker 网络这会在后面第 4 节的实战里详细展开。2.2 编写 docker-compose.yml 并启动我推荐使用 Docker Compose 来安装 NPM原因很简单可复现、可备份、不影响宿主机系统环境升级时也只需要拉取新镜像然后重建容器。先创建目录并进入再新建一个 docker-compose.yml 文件。version: 3.8 services: nginx-proxy-manager: image: jc21/nginx-proxy-manager:latest container_name: nginx-proxy-manager restart: unless-stopped ports: - 80:80 - 443:443 - 81:81 environment: DB_SQLITE_FILE: /data/database.sqlite volumes: - ./data:/data - ./letsencrypt:/etc/letsencrypt这个文件里有三个点需要特别说明。一是数据卷./data存放 NPM 的数据库文件里面包含所有代理配置、用户信息./letsencrypt存放证书文件。这两个目录是整个部署里最珍贵的东西丢失了等于所有代理配置和证书都要重新做一遍。二是容器名显式指定container_name可以让你更方便地用docker logs nginx-proxy-manager查看日志。三是 81 端口如果你并不想把管理面板暴露到公网可以改成127.0.0.1:81:81这样面板只有服务器本机能访问更安全。启动命令很简单docker compose up -d启动后先不要着急登录给它半分钟到一分钟初始化时间。你可以用docker ps看一眼容器状态确保状态是 Up。如果有红色异常用docker logs nginx-proxy-manager查看启动日志最常见的问题是 80 端口被 Nginx、Apache 或者其他容器占用了。2.3 第一次登录与修改初始化默认密码初始化完成后浏览器访问http://服务器IP:81就能看到 NPM 的登录页面。它的默认账号是adminexample.com默认密码是changeme。这里我有两个建议。第一登录后第一件事是修改管理员邮箱和密码路径在页面右上角的设置区域里。默认密码是明文写在官方文档里的如果服务器在公网且你没有限制 81 端口访问被扫到以后别人可以轻易登录管理界面。第二建议把登录邮箱改成真实可用的邮箱这样证书申请失败、到期提醒之类的通知能第一时间收到。改完邮箱以后记得点保存再退出重登一次确认密码已经生效。登录进去以后你会看到几个主菜单Dashboard 是仪表盘Hosts 下面管代理主机Access Lists 是访问控制列表Certificates 是证书管理Settings 是系统设置。整体布局非常直白不需要太多解释就能上手但要把每个菜单里的细节用好还是得在水里泡几次。2.4 端口冲突排查与面板安全暴露建议安装过程中最常见的失败原因真的就是端口占用。如果你的服务器上已经装了 Nginx 或者 Apache它们默认就占着 80 端口NPM 容器启动时会直接报错退出。排查方式很简单sudo lsof -i :80 sudo lsof -i :443 sudo lsof -i :81看到有进程占用就先把它们停掉或者改到其他端口再重新启动 NPM。如果是云服务器还要去安全组或防火墙的控制台确认放行了 80、443、81 三个端口不然公网访问不了。关于面板安全我的习惯是只要能不进公网就不把 81 端口开给全世界。家里用就把 81 端口限制在内网网段服务器上就只绑定本机地址或者用防火墙规则做白名单。如果你确实需要在外部管理面板建议配合双向验证和强密码但坦白说把管理入口暴露到公网的风险远大于便利能不开就不开。3. 反向代理核心配置从 HTTP 转发到 HTTPS 证书3.1 添加第一条代理主机的完整操作点击左侧菜单 Hosts 下的 Proxy Hosts再点右上角的 Add Proxy Host会看到一个表单。这个表单就是 NPM 最核心的地方字段不多但每一个都容易填错。Domain Names 填写你想对外提供服务的域名比如wiki.example.com多个域名可以用逗号分隔。Scheme 选择后端服务是 HTTP 还是 HTTPS绝大多数 Docker 容器内部跑的是 HTTP所以这里选 http。Forward Hostname/IP 是真正重要的一项它填写后端服务的地址。Forward Port 填服务监听的实际端口。举个例子假设服务器上有一个容器运行着某文档系统容器名是docs_app它内部监听 8081 端口而且已经和 NPM 加入了同一个 Docker 网络那么在 Forward Hostname/IP 里可以直接填容器名docs_app端口填8081。为什么可以填容器名因为 Docker 自带 DNS 解析同一自定义网络里的容器可以通过服务名互相访问。如果业务服务不是容器而是直接跑在宿主机上的程序监听127.0.0.1:8081这里就不能填127.0.0.1。原因在于 NPM 本身也是容器它看到的 127.0.0.1 是 NPM 容器自己并不是宿主机。要访问宿主机上的服务需要填 Docker 默认网桥的网关地址通常是172.17.0.1具体的可以通过命令查出来docker network inspect bridge | grep Gateway我第一次用的时候就在这个 127.0.0.1 上栽过跟头填完以后所有请求都变成 502查了半天才发现 NPM 容器里的 127.0.0.1 和宿主机的 127.0.0.1 根本不是一个地方。表单里还有几个开关Cache Assets 会启用静态资源缓存适合资源较多但更新不频繁的服务Block Common Exploits 开启常见攻击拦截建议默认打开Websockets Support 是给 WebSocket 服务用的像在线编辑器、聊天服务这类应用必须打开。保存以后你的第一个代理规则就生效了此时用http://你的域名已经能访问到后端服务但还缺一个 HTTPS 证书。3.2 获取 Lets Encrypt 自动证书进入上一步创建的代理主机编辑页面切到 SSL 标签页。在这里点 Request a new SSL Certificate选择 Lets Encrypt 方式勾选 Force SSL 和 HTTP/2 Support填一个邮箱地址然后提交。NPM 会在后台向证书机构发起验证请求验证成功后证书会自动配置到这条代理规则上整个过程通常一分钟以内。这里有两个前提要反复检查。一是域名必须已经解析到当前服务器公网 IP而且解析记录要生效。很多新手申请失败就是用nslookup 域名一看IP 还是旧的或者没解析。二是 80 端口必须能公网访问因为 Lets Encrypt 默认走 HTTP-01 验证它要通过 80 端口访问到你的域名才行。如果申请失败页面会显示红色的错误提示最准确的信息还是在 NPM 的日志里docker logs --tail 200 nginx-proxy-manager证书申请成功以后NPM 会自动帮你续期这个步骤非常省心。但要注意如果域名后来不再指向这台服务器或者 80 端口被封了续期也会失败。在 Certificates 菜单里能看到证书列表和到期时间建议隔段时间扫一眼。3.3 强制 HTTPS、HTTP/2、WebSocket 这些开关用不用开在 SSL 配置页面里Force SSL 和 HTTP/2 Support 我基本都会打开。Force SSL 的意思是用户用 HTTP 访问时NPM 会返回一个 301 跳转把请求带到 HTTPS 地址上。现在所有主流浏览器都对 HTTP 页面不友好这个开关能保证整个站点默认走加密通道。HTTP/2 则能显著改善页面加载效率尤其是在有很多小资源的场景下多路复用带来的体感非常明显。WebSockets Support 放在 Forms 标签页的下面和这些开关不在同一个位置。如果你代理的是实时通信类服务、在线协同工具、聊天应用只要后端依赖 WebSocket 长连接就必须打开这个开关。它会在 Nginx 配置里加入正确的 Upgrade 相关头信息不然浏览器会一直报 WebSocket 连接失败或者连接建立后很快断开。在拦截设置方面我通常会保留 Block Common Exploits 和 Cache Assets 的默认状态再根据自己的实际需要调整。这两个开关英文原意都很好理解一个是拦截常见攻击一个是缓存静态资源。缓存开久了以后偶尔会遇到修改了前端资源但浏览器还是旧版本的尴尬这时候可以暂时关掉缓存开关强制刷新后再打开或者配合业务代码的静态资源版本号来处理。3.4 自定义 Nginx 配置的边界与注意点NPM 的 Advanced 标签页里有一个 Custom Nginx Configuration 文本框它允许你写入额外的 Nginx 指令。这是 NPM 保留灵活性的关键入口但也需要注意边界。举个例子如果你要允许上传超大文件默认 Nginx 的client_max_body_size只有 1MB上传几十上百兆的文件就会直接报 413。此时可以在自定义配置里写client_max_body_size 100m;保存生效后上传限制就放宽了。类似的还有调整超时时间proxy_read_timeout 300s; proxy_send_timeout 300s;这对于一些长时间计算后才返回结果的接口非常有用。但要注意NPM 会对这些配置做拼接你写进去的内容会被放到对应的 server 块上下文中所以不要在这里面再写完整的server { }块也不要写listen 443这类指令否则会导致配置冲突严重时连 Nginx 都重启不了。如果遇到保存后页面打不开的情况就去看看日志通常是指令冲突导致的 config test 失败。修改自定义配置前建议先在本地把要贴进去的内容简单检查一遍别把明显有语法错误的文本直接塞进去。4. 实战一台服务器上接入多个服务4.1 规划一个小型多服务拓扑这一节用一套比较典型的组合来演示。假设现在有一台测试服务器要跑三个服务服务内部监听地址对外域名用途某文档系统docs_app:8081docs.example.com团队知识库某监控面板monitor_app:3000status.example.com查看服务器状态某模拟 APIapi_app:9090api.example.com给前端调用的接口如果没有 NPM这三组服务会分散在三个端口用户得记 8081、3000、9090 三组数字。现在把它们全部收敛到 NPM统一走 80/443 端口由域名来区分访问目标。看起来简单实际操作时最需要先解决的问题就是 Docker 网络互通。4.2 使用公共 Docker 网络消除互通问题默认部署 docker-compose 项目时每个项目都会创建一个以项目名命名的网络里面包含它自己的容器。NPM 项目有自己的网络文档系统项目也有自己的网络两个网络之间不互通。这就导致一个问题在 NPM 里填写文档系统地址时你没法直接用容器名docs_app因为 NPM 容器所在网络不认识这个名字。强行填进去转发过去就是 502。解决办法是显式创建一个公共网络让 NPM 和所有业务容器都加入这个网络。docker network create proxy-net然后在 NPM 的 docker-compose.yml 末尾给服务和文件内容都加上网络声明networks: default: external: name: proxy-net再用同样的方式在业务容器的 compose 文件里声明加入这个外部网络。比如文档系统的 compose 文件就加上networks配置确保它启动后和 NPM 处于同一个proxy-net。只要网络打通了NPM 里填 Forward Hostname/IP 时就能直接用服务名不用关心容器实际的动态 IP 是多少。我把这套网络规划放在最开始讲就是要提醒大家一定不要等容器全跑起来了才想起来互不来往。如果已经遇到 502排查方式也很简单先在 NPM 容器里看能不能解析到业务容器名然后再看能不能连通端口。docker exec -it nginx-proxy-manager ping docs_app docker exec -it nginx-proxy-manager curl http://docs_app:8081连接成功后再在 NPM 里配置代理规则成功率会高很多。4.3 子路径转发及兼容性避坑有时候你不想为一个服务单独申请子域名而想在一个主域名下面用路径区分比如example.com/docs/转到文档系统example.com/status/转到监控面板。NPM 支持这种方案吗支持但它藏得不是那么明显。在 Proxy Hosts 的编辑页面里你需要在 Domain Names 中填写主域名然后在 Advanced 标签页里使用 Custom Locations。新增一个路径规则比如 URL 填/docs/Scheme 选 httpForward Hostname/IP 填文档服务的地址端口填 8081这样/docs/路径下的请求就会被转到文档系统。这里有一个我特别想提醒的坑不是所有 Web 应用都适合放在子路径下。很多应用在开发时假设自己部署在域名根路径会在 HTML、CSS、JS 里写死绝对路径比如/static/style.css结果就是你通过/docs/访问时页面加载的静态资源会跑到根路径去一样找不到。解决思路通常是优先选用独立子域名省事且兼容性最好如果真的必须用子路径就要找应用设置里的 base path 或 homepage 选项把它改成/docs/有些应用甚至需要在反向代理层做 sub_filter 重写工作量一下子就上来了。我的个人建议是能用子域名就不要用子路径DNS 记录多一条不是什么大成本但能避开大量应用兼容性问题。4.4 代理到内网其他设备时的安全底线NPM 不止能代理本机容器还能代理局域网里的其他设备。比如你的 NAS 管理后台跑在某台内网设备上映射了 5000 端口你不想每次都用 IP 加端口访问那就可以在 NPM 里新增一条代理规则Forward Hostname/IP 填内网 IP比如192.168.1.100端口填 5000对外域名填nas.example.com或者内部专用域名。这里面有个非常容易忽视的安全问题把内网设备暴露到公网相当于把设备的全部控制面挂到了互联网上。NAS 一旦被攻破损失的是整台设备的全部数据。我处理这类需求时一般会分两个层次。如果只是局域网内访问那么域名也只在本地 DNS 里做解析路由器或内网 DNS 把域名指到 NPM 所在的内网 IP 就行不需要在公网 DNS 里配置也不用对外开放 80/443。如果确实需要从外部访问至少也要给这条代理规则加上访问控制列表、Basic Auth并且确认设备本身的系统、固件、口令都已经足够健壮能不上公网就不要上公网。5. 访问控制与安全加固实录5.1 访问列表IP 白名单与 Basic AuthNPM 的 Access Lists 菜单是最被低估的功能。它提供了一套非常简洁的访问控制模型你可以创建包含多条规则的访问列表再把这个列表绑定到任意代理主机上。创建流程是进入 Access Lists点击 Add Access List给它起个名字比如admin_only。在详情页里能添加两类规则一种是 IP 地址规则允许或拒绝特定的 IP、IP 范围另一种是 Access Control也就是 Basic Auth添加用户名和密码。全部编辑好后保存再到某个代理主机的编辑页面里在 Access List 下拉框里选中这个列表并保存。这样做的效果是实时的。比如你有一个内部运维系统只想让办公室网段访问其他来源一概拒绝那么可以在列表里添加一条192.168.1.0/24的 Allow 规则一条0.0.0.0/0的 Deny 规则。注意规则是有顺序的先匹配先生效所以 White List 类型的规则要放在前面。5.2 给敏感服务叠加一层额外的身份校验Basic Auth 是我在部署敏感内部工具时一定会加的一层保险。很多应用自己也有登录功能但登录前的请求比如登录页面本身、接口探索其实是暴露在公网上的攻击者可以反复尝试撞库。如果在 NPM 这一层就先加上 Basic Auth浏览器访问时会先弹出一个基础认证框要求输入独立的用户名密码连应用登录页面都看不到等于多了一层门槛。具体做法就是在访问列表里添加一组用户名和密码然后把列表绑定到对应的代理主机上。注意 Basic Auth 的用户名密码和 NPM 管理员的账号密码是两套东西不要混用。如果你担心 HTTP 明文传输会泄露 Basic Auth 密码那更应该开启强制 HTTPS让整个链路都走加密通道。5.3 Nginx 限流与常见攻击拦截的配置参考NPM 的 UI 没有给出直观的限流开关但你可以通过自定义 Nginx 配置来实现。做法是在代理主机的 Advanced 标签页里加入一个 location 级的限流配置。比如你想限制某个登录接口的访问频率location ^~ /api/login { limit_req zonemylimit burst10 nodelay; proxy_pass http://api_app:9090; }然后在 NPM 对应位置的配置里需要先定义 zone但这个定义通常放在 http 上下文NPM 的自定义配置不一定支持这么放。这也是我要特别说明的在 NPM 里写自定义限流没有图形界面那么直观如果你对 Nginx 变量作用域不够熟悉建议先在单独的 Nginx 环境里验证再把完整可用的片段贴回来不要直接在生产环境上试错。如果你不想搞得太复杂可以优先把内置的 Block Common Exploits 开关打开它能拦截不少常见的路径扫描、可疑 UA、SQL 注入特征请求。对大多数内部工具来说这一层拦截已经能挡住非常多无差别攻击。安全是层层叠加的NPM 做边缘加固业务应用自己做鉴权两边不要互相替代。5.4 管理面板本身不要暴露到公网整个 NPM 系统里最危险的不是某一个代理服务而是它自己的管理面板。一旦 81 端口暴露在公网攻击者就能不断尝试登录如果密码不够强或者你用的是官方默认密码那整个服务器的代理入口就全在对方手里了。我的建议是如果服务器在公网上除非有明确需求否则不要让 81 端口对所有 IP 开放。可以在 docker-compose.yml 里把端口绑定改为这样ports: - 127.0.0.1:81:81这样只有服务器本机能访问管理面板。需要管理时再通过已经登录服务器的环境做端口转发或者干脆在服务器上本地执行 curl 来验证配置。除此之外还可以用系统防火墙直接限制来源 IP比如只允许某个办公网段的 IP 访问 81 端口。这个习惯能直接避免大量被扫到后的暴力破解问题。另外面板本身建议开启“双重认证”NPM 新版提供了相关的两步验证选项可以在 Settings 里启用。这和多一层密码是两回事它要求登录时输入一个动态验证码就算密码被泄露了没有验证码也进不去。6. 常见问题与故障排查实战记录6.1 502 Bad Gateway 排查清单502 应该是使用 NPM 时遇到最多的错误几乎每个人都见过。它背后的含义是 NPM 代理到了后端但后端没有正常返回响应。排查顺序我建议这样来第一确认后端服务真的在运行。用docker ps看看对应的容器是否还在 Up很多 502 的根源就是昨晚重启服务器时忘了把某些容器设置成自动重启。第二确认网络互通。在 NPM 容器里执行curl http://业务服务名:端口如果这里已经失败那问题在 Docker 网络而不是 NPM。第三确认 NPM 里填的协议、端口和真实后端一致。比如后端其实是 HTTPS但你填成了 HTTP就会收到异常响应后端端口是 9090你填成 9000那必然 502。症状常见原因解决要点NPM 容器内 curl 后端服务失败网络不互通、容器在不同 Docker 网络统一加入同一个外部网络填 127.0.0.1 但后端跑在宿主机NPM 容器内的 127.0.0.1 不是宿主机改用 Docker 网桥网关地址后端服务重启后 IP 变了容器动态 IP 变化优先使用容器名代替 IP后端服务本身不响应应用崩溃、依赖数据库未启动单独访问查看应用状态还有一点容易被忽略如果你的后端是一个比较老的程序不兼容 HTTP/1.1 的某些特性转发时也可能会异常。不过这类情况较少先从基础设施查起成功率最高。6.2 SSL 证书申请失败的那几次证书申请失败排在第一名的原因是域名解析问题。看到错误里写着No such host或者Could not resolve host基本可以确定是域名没有正确解析到当前服务器。先用curl http://你的域名试试如果访问不到就在 DNS 商的解析记录里检查 A 记录是否指向了正确 IP。第二个高频原因是 80 端口被占用或没有放行。NPM 的 Lets Encrypt 申请机制需要从公网访问到你的 80 端口如果你的 80 端口被占用、被防火墙拦截、或者被某条安全组规则挡住了证书申请就会一直卡在验证阶段。注意这里说的是公网能够访问 80 端口并不是只要本机能 curl 通就行。如果你拿不准可以用手机浏览器用流量访问一下http://你的域名能看到 NPM 的默认页面说明 80 端口对外是通的。还有一个容易被忽略的原因是同一域名短期内反复申请太多次触发了证书机构的频率限制。这种情况错误信息里一般会出现rate limit字样。解决方式就是停一段时间再试或者在测试阶段先不要急着反复点申请按钮。6.3 出现重定向循环怎么办如果你打开域名以后浏览器提示“将您重定向的次数过多”多半是 HTTP 到 HTTPS 的跳转链路出现了无限循环。最常见的场景是应用本身已经配置了强制 HTTPS而 NPM 这边又开了 Force SSL结果用户访问 HTTPNPM 302 到 HTTPSHTTPS 到达应用后应用又因为某种判断把请求重新 302 回 HTTP来回跳个没完。解决思路是统一整个链路的协议判断。要么在 NPM 层开 Force SSL同时关闭应用内部的强制 HTTPS 跳转要么就让 NPM 直接转发 HTTP 流量由应用自己处理 HTTPS 跳转但要注意 NPM 进站这一侧如何获取证书。最干净的方式还是让 NPM 承担 HTTPS 终止和跳转的职责应用内部一律用 HTTP并且要让应用知道外部用户实际使用的是 HTTPS。在这个问题上NPM 一般会带上X-Forwarded-Proto请求头应用可以根据它来判断原始协议。如果你的应用出现了协议判断异常可以检查自定义 Nginx 配置里有没有被覆盖掉这个头有的话把它纠正过来proxy_set_header X-Forwarded-Proto $scheme;改写后要重载一次代理配置才能生效。NPM 在保存配置时会自动重载所以一般只需要点一次保存。6.4 WebSocket 连接不稳定问题的定位如果代理的是实时类服务浏览器一直提示 WebSocket 连接失败先检查 NPM 里的 Websockets Support 开关有没有打开。这个开关一开始我提过它在代理主机的编辑页面里默认是关闭状态而很多实时应用恰恰依赖 WebSocket没有开启连接会在握手阶段就断掉。开启后依然不稳定接下来就看请求路径。如果你的应用使用了 wss也就是加密的 WebSocket而 NPM 后端转发的是普通 HTTP 端口这没有问题NPM 会在外层完成 TLS 终结。重点要检查应用的配置里是否把 WebSocket 地址写成了ws://而不是wss://。在页面是 HTTPS 加密访问的情况下浏览器会拒绝来自安全页面的非加密 WebSocket 连接。另外如果 WebSocket 连接是建立后又立刻断开通常要查两处一是 Nginx 的超时时间如果proxy_read_timeout设得太短长期闲置的连接可能被切断可以通过自定义配置调大二是应用本身有没有心跳机制长期没有消息的 WebSocket 连接需要靠 ping/pong 保活这不是 NPM 能直接解决的要在应用层处理。6.5 备份、升级与数据迁移经验NPM 的配置都落在 SQLite 数据库里证书文件在 letsencrypt 目录里。最简单可靠的备份方式就是把整个 docker-compose 文件所在的目录打个包尤其是里面的 data 和 letsencrypt 两个子目录。tar -czvf npm_backup_$(date %F).tar.gz -C /path/to/nginx-proxy-manager .恢复时也很简单把备份文件解压回原目录重新docker compose up -d即可。我做升级前一定会先打这个包因为镜像升级偶尔会带来数据库字段变化万一有异常还能直接回滚到旧版本。NPM 的 Settings 里也提供了 Backup 功能可以导出 JSON 备份它会包含代理主机、访问列表和证书信息。不过它导出的主要是配置证书文件不会包含在里面。如果原来的证书文件丢了恢复配置后还需要重新申请证书。所以完整备份还是要把 letsencrypt 目录一起带走两条腿走路才稳。升级操作本身非常标准docker compose pull docker compose up -d升级完成后打开面板确认数据还在代理状态正常。我一般会再随便访问一个代理域名确认证书链没问题才算完成一次升级。最后分享一个我自己的习惯给每个代理规则命名时不要用 IP 加端口这种毫无语义的写法而是直接写业务名称比如docs-prod、monitor-beta。NPM 虽然支持配置搜索但规则一多名称是否清晰直接影响排障速度。把内部工具和服务统一收敛到 NPM 之后我最大的感受就是再也不用来回翻笔记记端口了所有入口一眼望去都是清晰的域名证书到期也有自动续期兜底。如果你也正被端口号和多套配置折腾这套方案现在就可以拿来试试。
返回列表