
如果你在同一台 VPS 上运行多个服务——WordPress、Node.js 应用、Grafana、Portainer——你一定深知手动管理端口和 SSL 证书有多痛苦。每增加一个服务,就意味着要编辑 Nginx 配置文件、运行 Certbot,还要在半夜调试语法错误。Nginx Proxy Manager(NPM)彻底解决了这个问题。通过简洁的 Web 界面,你可以在两分钟内完成反向代理配置、绑定自定义域名并申请免费的 Let's Encrypt SSL 证书——完全不需要手动修改任何 Nginx 配置文件。
什么是 Nginx Proxy Manager?
Nginx Proxy Manager 是一个 Web 应用程序,为 Nginx 反向代理提供图形化管理界面。你不再需要手动编写 /etc/nginx/sites-available/mysite.conf,只需填写一个表单——域名、转发主机名、端口——点击保存,NPM 就会自动生成正确的 Nginx 配置并申请 SSL 证书。
NPM 的运行方式
NPM 以 Docker 镜像的形式发布,并附带一个 MariaDB 数据库容器。这使得安装变得简单、升级干净彻底、卸载也不会在文件系统中留下任何残余文件。
核心功能
- 代理主机(Proxy Hosts):几次点击即可将域名映射到后端服务并开启 SSL
- 免费 SSL:自动申请 Let's Encrypt 证书,包括通过 DNS Challenge 方式申请通配符证书
- 访问列表(Access Lists):按 IP 限制访问,或为任意代理主机添加 HTTP Basic 认证
- 重定向主机(Redirection Hosts):将一个域名重定向到另一个域名(301/302)
- 流(Streams):为非 HTTP 协议转发 TCP/UDP 流量
- 自定义 Nginx 配置:通过每个主机的"高级"选项卡添加任意 Nginx 指令
NPM 与其他方案的对比
- NPM vs 手动配置 Nginx:对于标准场景,NPM 的速度远快于手动配置。手动配置 Nginx 在边缘情况下能提供更细粒度的控制,但需要熟悉 Nginx 语法。
- NPM vs Traefik:Traefik 更适合配置频繁变化的 Kubernetes 和微服务环境。在普通 VPS 上,NPM 的简易性更胜一筹。
- NPM vs Caddy:Caddy 同样支持自动 HTTPS,但 NPM 提供了 Web 界面,无需任何终端操作,非开发人员也能轻松使用。
最适合以下场景:在 VPS 上运行两个或更多服务,每个服务都需要独立域名和 SSL 证书;团队希望在无需 SSH 访问的情况下委托他人管理代理配置;以及任何觉得 Nginx 配置语法令人头疼的用户。
前提条件
在安装 Nginx Proxy Manager 之前,请确保已具备以下条件。
- 运行 Ubuntu 22.04 或 20.04 的 VPS——两个版本均受支持
- 已安装 Docker 和 Docker Compose——如果尚未配置,请参阅 VPS 安装 Docker 指南
- 已将域名 DNS A 记录指向 VPS IP——DNS 必须完成解析,NPM 才能申请 SSL 证书
- 拥有服务器的 root 或 sudo 权限
- 至少 512 MB 内存(推荐 1 GB 或更多——NPM 会同时运行 MariaDB 数据库)
在 UFW 中开放所需端口
NPM 需要三个端口:80(HTTP)、443(HTTPS)和 81(NPM Web 界面)。使用以下命令开放这些端口:
# Open Port 80 (HTTP) and 443 (HTTPS) — required for reverse proxy and SSL ufw allow 80/tcp ufw allow 443/tcp # Open Port 81 for NPM Web UI — temporary; close it after setup ufw allow 81/tcp # Verify firewall status ufw status numbered
安全提示:端口 81 应仅在初始设置和维护期间开放。设置完成后,应从公网封锁端口 81,改用 SSH 隧道访问 NPM 界面(详见安全加固章节)。
使用 Docker Compose 安装 Nginx Proxy Manager
Docker Compose 是安装和管理 NPM 的推荐方式。它将所有内容集中在一个目录中,升级只需一条命令,备份也非常简便。
逐步安装指南
-
第一步:为 NPM 创建目录
mkdir -p ~/npm && cd ~/npm
-
第二步:创建 docker-compose.yml 文件
nano ~/npm/docker-compose.yml
粘贴以下内容,并将密码替换为您自己设置的强密码:
version: '3.8' services: app: image: 'jc21/nginx-proxy-manager:latest' restart: unless-stopped ports: - '80:80' - '443:443' - '81:81' volumes: - ./data:/data - ./letsencrypt:/etc/letsencrypt db: image: 'jc21/mariadb-aria:latest' restart: unless-stopped environment: MYSQL_ROOT_PASSWORD: 'npm_root_pw_changeme' MYSQL_DATABASE: 'npm' MYSQL_USER: 'npm' MYSQL_PASSWORD: 'npm_pw_changeme' volumes: - ./mysql:/var/lib/mysql -
第三步:启动容器
cd ~/npm && docker compose up -d
Docker 将拉取镜像并启动两个容器。首次运行时,镜像下载可能需要 1–2 分钟。
-
第四步:验证两个容器均已运行
cd ~/npm && docker compose ps
app和db容器的状态均应显示为Up。 -
第五步:访问 Web 界面
打开浏览器,访问
http://YOUR_VPS_IP:81(将YOUR_VPS_IP替换为您的 VPS 实际 IP 地址)。
端口冲突:如果 Nginx 或 Apache 已在运行并监听 80 或 443 端口,Docker 将无法绑定这些端口。请先停止并禁用冲突服务:systemctl stop nginx && systemctl disable nginx
首次登录与修改默认密码
首次打开 Web 界面时,使用以下默认凭据登录:
- 邮箱:
[email protected] - 密码:
changeme
NPM 会立即提示您更新邮箱地址并设置新密码。请不要跳过此步骤——默认凭据是公开信息,存在安全风险。
首次登录最佳实践
- 使用真实邮箱地址——NPM 和 Let's Encrypt 将用它发送证书到期通知
- 设置强密码——至少 16 个字符,混合大写字母、数字和符号
- 修改凭据后,NPM 会自动登出,请使用新信息重新登录
添加第一个代理主机
代理主机(Proxy Host)将域名映射到在特定端口上运行的后端服务(可在主机上运行,也可在同一网络的 Docker 容器内运行),并自动为该域名申请 SSL 证书。
如何添加代理主机
-
第一步:前往 Hosts → Proxy Hosts → Add Proxy Host
点击顶部的 Hosts 菜单,选择 Proxy Hosts,然后点击右侧绿色的 Add Proxy Host 按钮。
-
第二步:填写 Details 选项卡
- Domain Names:输入您的域名(如
app.example.com),按 Enter 确认 - Scheme:对于使用普通 HTTP 的服务,选择
http - Forward Hostname / IP:主机级服务填
localhost;若服务在同一 Docker 网络的容器中,填容器名称 - Forward Port:您的服务监听的端口(如
8080) - Block Common Exploits:建议为所有主机启用此选项
- Websockets Support:若服务使用 WebSockets(如 Socket.io、Grafana Live),请启用
- Domain Names:输入您的域名(如
-
第三步:在 SSL 选项卡中配置 SSL
- 点击 SSL 选项卡
- 在下拉菜单中选择 Request a new SSL Certificate
- 启用 Force SSL——自动将 HTTP 重定向至 HTTPS
- 启用 HTTP/2 Support——提升现代浏览器的性能
- 输入您的邮箱地址用于 Let's Encrypt 通知
- 勾选 I Agree to the Let's Encrypt Terms of Service
-
第四步:点击 Save
NPM 将生成 Nginx 配置并向 Let's Encrypt 申请 SSL 证书,大约需要 15–30 秒。完成后,代理主机行将变为绿色的 Online 状态。
DNS 必须先完成解析:Let's Encrypt 通过向您的域名发送 HTTP 请求来验证所有权。如果 DNS A 记录尚未指向您的 VPS IP,SSL 申请将失败。添加代理主机前,请先在 dnschecker.org 验证 DNS 传播情况。
实战示例:在一台 VPS 上运行 3 个服务
这是一个典型场景:WordPress、Grafana 和 Portainer 同时运行在同一台 VPS 上,每个服务都需要独立的域名和 SSL 证书。以下是在 NPM 中完成配置的方法。
目标域名映射
blog.example.com→localhost:8080(WordPress)monitor.example.com→localhost:3000(Grafana)docker.example.com→localhost:9000(Portainer)
WordPress(端口 8080)
# In NPM Web UI Domain Names: blog.example.com Scheme: http Forward Hostname / IP: localhost Forward Port: 8080 Block Common Exploits: ON Websockets Support: OFF SSL: Request new cert, Force SSL ON, HTTP/2 ON
Grafana(端口 3000)
# In NPM Web UI Domain Names: monitor.example.com Scheme: http Forward Hostname / IP: localhost Forward Port: 3000 Block Common Exploits: ON Websockets Support: ON ← Grafana uses WebSocket for live dashboards SSL: Request new cert, Force SSL ON, HTTP/2 ON
Portainer(端口 9000)
# In NPM Web UI Domain Names: docker.example.com Scheme: http Forward Hostname / IP: localhost Forward Port: 9000 Block Common Exploits: ON Websockets Support: ON ← Portainer uses WebSocket SSL: Request new cert, Force SSL ON, HTTP/2 ON
重要提示:当 NPM 和你的服务都以 Docker 容器方式运行时,将 localhost 用作转发主机名将无法正常工作——每个容器都有自己独立的网络命名空间。正确的做法是将所有容器加入同一个 Docker 网络,并使用容器名称作为主机名。详见下一章节的正确配置方式。
Docker 网络——容器间通信的正确方式
当 NPM 与后端服务均以 Docker 容器方式运行时,连接它们最可靠的方法是通过共享的 Docker 网络。同一网络上的容器名称可以互相解析为 IP 地址,且即使容器重启后底层 IP 发生变化,容器名称依然保持稳定。
创建共享 Docker 网络
# Create a network called npm_network docker network create npm_network
带网络配置的 NPM docker-compose.yml
编辑 ~/npm/docker-compose.yml,添加网络配置块:
version: '3.8'
services:
app:
image: 'jc21/nginx-proxy-manager:latest'
restart: unless-stopped
ports:
- '80:80'
- '443:443'
- '81:81'
volumes:
- ./data:/data
- ./letsencrypt:/etc/letsencrypt
networks:
- npm_network
db:
image: 'jc21/mariadb-aria:latest'
restart: unless-stopped
environment:
MYSQL_ROOT_PASSWORD: 'npm_root_pw_changeme'
MYSQL_DATABASE: 'npm'
MYSQL_USER: 'npm'
MYSQL_PASSWORD: 'npm_pw_changeme'
volumes:
- ./mysql:/var/lib/mysql
networks:
- npm_network
networks:
npm_network:
external: true后端服务 docker-compose.yml(以 WordPress 为例)
所有需要通过代理访问的服务,都必须加入同一个网络:
version: '3.8'
services:
wordpress:
image: wordpress:latest
restart: unless-stopped
container_name: wordpress # Use this name as the Forward Hostname in NPM
environment:
WORDPRESS_DB_HOST: wp_db
WORDPRESS_DB_NAME: wordpress
WORDPRESS_DB_USER: wp_user
WORDPRESS_DB_PASSWORD: wp_pass_changeme
volumes:
- ./wp_data:/var/www/html
networks:
- npm_network # Same network as NPM
wp_db:
image: mysql:8.0
restart: unless-stopped
container_name: wp_db
environment:
MYSQL_DATABASE: wordpress
MYSQL_USER: wp_user
MYSQL_PASSWORD: wp_pass_changeme
MYSQL_ROOT_PASSWORD: root_pass_changeme
volumes:
- ./wp_mysql:/var/lib/mysql
networks:
- npm_network
networks:
npm_network:
external: true完成上述配置后,在 NPM 中将 Forward Hostname 设置为 wordpress(容器名称),将 Forward Port 设置为 80——而非 8080,因为端口映射(主机端口:容器端口)仅适用于从 Docker 外部访问;在同一网络内,容器之间直接通过内部端口通信。
验证网络成员
# See which containers are connected to npm_network
docker network inspect npm_network
# List all running containers with their names and ports
docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"SSL 证书——Let's Encrypt 与通配符证书
NPM 无需单独安装 Certbot 即可管理完整的 SSL 证书生命周期。所有证书及其到期日期均可在 SSL Certificates 选项卡中查看。NPM 会自动处理续期,无需任何额外配置。
通配符证书(*.example.com)
通配符证书可用一张证书覆盖某个域名的所有子域——*.example.com 涵盖 blog.example.com、monitor.example.com、docker.example.com 以及你创建的任何新子域。这样每次新增服务时就无需再申请新证书。
通配符证书需要通过 DNS Challenge(而非标准的 HTTP Challenge)来证明你对域名 DNS 的控制权。NPM 支持 Cloudflare、Route 53、Namecheap 及多个其他 DNS 服务商。
通过 Cloudflare DNS Challenge 申请通配符证书
-
创建 Cloudflare API Token
登录 Cloudflare → Profile → API Tokens → Create Token → 使用"Edit zone DNS"模板,并将权限限制到你的特定区域。
-
在 NPM 中添加新 SSL 证书
前往 SSL Certificates → Add SSL Certificate → Let's Encrypt
- Domain Names:
*.example.com和example.com(两者都添加,以覆盖根域名) - Use a DNS Challenge:启用
- DNS Provider:Cloudflare
- Credentials File Content:
dns_cloudflare_api_token = YOUR_CF_API_TOKEN - Propagation Seconds:
120(等待 2 分钟以完成 DNS 传播)
- Domain Names:
-
点击 Save
NPM 将使用 API Token 在 Cloudflare 中创建 TXT 记录以证明域名所有权,然后从 Let's Encrypt 签发通配符证书。
-
将通配符证书应用到代理主机
创建或编辑代理主机时,在 SSL 选项卡中从下拉菜单选择已有的通配符证书,而非重新申请新证书。
自动续期
Let's Encrypt 证书有效期为 90 天。当证书剩余有效期不足 30 天时,NPM 会自动续期。无需配置定时任务,无需手动操作——自动完成。
访问列表——限制谁可以访问服务
访问列表(Access Lists)可让你控制谁能访问每个代理主机。最常见的两种用途是 IP 白名单和 HTTP Basic 认证。
创建访问列表
前往 Access Lists → Add Access List。
方式一:IP 白名单
将服务限制为特定 IP 地址访问——非常适合管理后台和内部工具。
# Access List Configuration Name: "Home and Office" Allow: 203.0.113.10 # your home IP Allow: 198.51.100.0/24 # your office subnet # Leave "Satisfy any" OFF to make this a strict whitelist
方式二:HTTP Basic 认证
为没有内置登录功能的服务(如 Netdata 仪表盘或简单 Web 应用)添加用户名/密码提示框。
# In the Access List — Authorization tab # Add Username and Password pairs # NPM auto-generates the htpasswd file behind the scenes
将访问列表应用到代理主机
编辑任意代理主机,在 Details 选项卡的 Access List 下拉菜单中选择你的访问列表。保存后 NPM 将立即强制执行该限制。
自定义 Nginx 配置(进阶)
每个代理主机都有一个 Advanced 选项卡,你可以在此粘贴标准界面未公开的原始 Nginx 指令。对于非常规需求,这是一个强大的逃生口。
常用自定义配置片段
增大 WordPress 上传文件大小限制
client_max_body_size 100m;
延长长时间请求的超时时间
proxy_read_timeout 300; proxy_connect_timeout 300; proxy_send_timeout 300;
将真实客户端 IP 传递给 WordPress(位于代理后端)
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_set_header X-Forwarded-Host $host;
添加安全响应头
add_header X-Frame-Options "SAMEORIGIN" always; add_header X-Content-Type-Options "nosniff" always; add_header Referrer-Policy "strict-origin-when-cross-origin" always; add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
禁用流式传输 / Server-Sent Events 的缓冲
proxy_buffering off; proxy_cache off;
注意:Advanced 选项卡只接受有效的 Nginx 指令。如果 NPM 检测到语法错误,将拒绝保存。如不确定,请先在普通 Nginx 配置中测试你的指令。
重定向主机
重定向主机(Redirection Hosts)可在无需完整代理主机的情况下处理简单的域名重定向——例如将 example.com 重定向到 www.example.com,或将旧域名指向新域名。
如何设置重定向
- 前往 Hosts → Redirection Hosts → Add Redirection Host
-
填写详情
- Domain Names:源域名,例如
example.com(不含 www) - Scheme:https
- Forward Domain Name:目标域名,例如
www.example.com - HTTP Code:
301表示永久重定向(推荐用于 SEO) - Preserve Path:启用后
/about将重定向到www.example.com/about
- Domain Names:源域名,例如
-
添加 SSL 证书
同样为源域名申请 SSL 证书,否则 HTTP 访问者将无法顺畅地重定向到 HTTPS。
流(Streams)——TCP/UDP 代理
流(Streams)用于转发非 HTTP 流量,例如数据库端口、邮件端口或游戏服务器连接——这些是标准反向代理无法处理的类型。NPM 通过 Streams 功能区来实现这一需求。
示例:转发 MySQL 端口
场景:你希望从本地机器上的数据库客户端(如 DBeaver)连接 Docker 容器内的 MySQL 实例,同时不将 3306 端口直接暴露到互联网。
# In Streams — Add Stream Incoming Port: 33060 # port clients connect to (avoid using 3306 directly) Forward Host: mysql_container # container name or IP Forward Port: 3306 TCP Forwarding: ON UDP Forwarding: OFF
安全警告:即便通过代理,将数据库端口暴露到互联网仍存在重大风险。请务必将流转发与访问列表结合使用,将访问限制为已知 IP 地址。
日志与故障排查
当代理主机显示错误状态或变为离线时,以下是诊断问题最有效的方法。
实时查看 NPM 容器日志
# Follow NPM app container logs in real time cd ~/npm && docker compose logs -f app # Show only the last 100 lines docker compose logs --tail=100 app # Check the database container logs docker compose logs -f db
常见错误及解决方法
502 Bad Gateway
NPM 无法连接后端服务。常见原因:
- 后端服务已停止或崩溃——使用
docker ps或systemctl status servicename检查 - 转发主机名有误——若使用容器名称,请确认拼写正确且容器在同一网络中
- 转发端口有误——请确认服务实际监听的端口
# Check running containers docker ps # Test connectivity from inside the NPM container docker exec npm-app-1 curl -s http://CONTAINER_NAME:PORT
SSL 证书申请失败
- DNS 尚未传播——验证 A 记录是否已解析到你的 VPS IP
- 端口 80 被封锁——Let's Encrypt HTTP Challenge 需要端口 80 可达
- 请求频率超限——Let's Encrypt 每个域名每周最多允许 5 次证书申请;多次失败可能触发频率限制
# Check that port 80 is reachable for your domain curl -I http://YOUR_DOMAIN # Verify DNS resolution dig +short YOUR_DOMAIN A
重定向次数过多
当 NPM 中启用了 Force SSL,而后端服务本身也将 HTTP 重定向到 HTTPS 时,会产生无限重定向循环。
# Fix option 1: Disable the redirect in the backend service's own config # Fix option 2: Set Scheme to "https" in the NPM Proxy Host if the backend # already serves HTTPS natively
连接被拒绝
NPM 转发的目标端口没有任何服务在监听,或后端容器未加入同一 Docker 网络。
# Verify containers in npm_network docker network inspect npm_network | grep -A2 '"Name"' # Connect a running container to npm_network at runtime docker network connect npm_network CONTAINER_NAME
NPM 安全加固
端口 81 上的 NPM Web 界面绝对不能从公共互联网直接访问。任何能访问它的人都可以修改你的所有代理配置。请采取以下一项或多项加固措施。
方法一:从公网封锁端口 81
# Block port 81 in UFW after initial setup is complete ufw deny 81 # Verify ufw status numbered
方法二:通过 SSH 隧道访问 NPM Web 界面
封锁端口 81 后,你仍可通过 SSH 隧道安全访问 NPM 界面。在你的本地机器上运行以下命令(不是在服务器上):
# Run on your local machine ssh -L 81:localhost:81 user@YOUR_VPS_IP # Then open in your browser http://localhost:81
这将创建一条加密隧道,通过 SSH 连接将本地机器的端口 81 转发到服务器的端口 81。服务器的端口 81 对公众仍保持关闭状态。
方法三:将端口 81 限制为仅你的 IP 可访问
# Allow only your IP (replace 203.0.113.10 with your actual IP) ufw allow from 203.0.113.10 to any port 81 # Block all other IPs from port 81 ufw deny 81
方法四:修改默认管理员凭据
请确认已将管理员邮箱和密码从默认值([email protected] / changeme)更改为自己的信息。若尚未修改,请立即前往 NPM 界面的 Admin → Profile 进行修改。
备份与更新
NPM 恢复正常运行所需的一切都保存在 ~/npm/ 下的三个子目录中。只需备份这些目录,你就可以在新服务器上重建整个 NPM 环境。
创建备份
# Stop containers first for a clean consistent backup cd ~/npm && docker compose stop # Copy data to a timestamped backup directory mkdir -p /backup/npm-$(date +%Y%m%d) cp -r ~/npm/data ~/npm/letsencrypt ~/npm/mysql /backup/npm-$(date +%Y%m%d)/ # Start containers again docker compose up -d # Confirm backup was created ls -la /backup/
将 NPM 更新至最新版本
# Pull the latest image cd ~/npm && docker compose pull # Recreate containers with the new image (minimal downtime) docker compose up -d # Confirm the update docker compose exec app nginx -v
更新前务必备份:尽管 NPM 更新通常顺畅无阻,但备份 data 目录只需数秒,却能在跨主版本升级时防止潜在的数据库迁移问题。
各目录存储内容
~/npm/data/— 所有 NPM 配置:代理主机、访问列表、SSL 设置~/npm/letsencrypt/— 所有 SSL 证书和私钥~/npm/mysql/— NPM 内部使用的 MariaDB 数据库
总结与后续步骤
Nginx Proxy Manager 是任何运行多个服务的 VPS 的优秀工具。它将原本需要 Nginx 专业知识才能完成的任务变成了任何人都能在浏览器中轻松搞定的事情,同时通过 Advanced 选项卡和自定义配置为高级用户保留了所需的灵活性。
以下情况 NPM 是最佳选择
- 在一台 VPS 上运行两个或更多服务,并希望每个服务都有独立的域名和 SSL 证书
- 希望使用 Let's Encrypt SSL,但不想学习 Certbot 命令行语法
- 需要将代理管理委托给没有 SSH 访问权限的团队成员
- 正在运行家庭实验室、自托管服务或开发环境
需要注意的局限性
- 需要 Docker——不适合不想安装或无法安装 Docker 的服务器
- 由于同时运行 MariaDB,至少需要 512 MB 内存(推荐 1 GB 或更多)
- 对于 Kubernetes 或大型微服务架构,Traefik 或 Ingress Controller 是更合适的选择
- NPM 是社区开源项目——没有正式的商业支持
推荐后续步骤
- 在单个 docker-compose 文件中部署 WordPress + NPM + SSL,亲眼看到所有组件协同工作
- 添加 Grafana + Prometheus 监控,并通过 NPM 结合访问列表对外暴露
- 安装 Portainer 并通过 NPM 代理,实现完全基于图形界面管理的 Docker 环境
- 探索 NPM REST API,在 CI/CD 流水线中自动化创建代理主机
准备好在 VPS 上运行 Nginx Proxy Manager 了吗?
AsiaGB VPS 开箱即提供完整 root 权限、1 GB 或更大内存以及 Docker 支持。数据中心位于泰国和新加坡,最低仅需 500 泰铢/月。
查看 AsiaGB VPS 套餐