在 VPS 上安装并使用 Nginx Proxy Manager:反向代理与 SSL 的图形界面管理工具

如果你在同一台 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 数据库容器。这使得安装变得简单、升级干净彻底、卸载也不会在文件系统中留下任何残余文件。

核心功能

NPM 与其他方案的对比

最适合以下场景:在 VPS 上运行两个或更多服务,每个服务都需要独立域名和 SSL 证书;团队希望在无需 SSH 访问的情况下委托他人管理代理配置;以及任何觉得 Nginx 配置语法令人头疼的用户。

前提条件

在安装 Nginx Proxy Manager 之前,请确保已具备以下条件。

在 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 的推荐方式。它将所有内容集中在一个目录中,升级只需一条命令,备份也非常简便。

逐步安装指南

  1. 第一步:为 NPM 创建目录
    mkdir -p ~/npm && cd ~/npm
  2. 第二步:创建 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
  3. 第三步:启动容器
    cd ~/npm && docker compose up -d

    Docker 将拉取镜像并启动两个容器。首次运行时,镜像下载可能需要 1–2 分钟。

  4. 第四步:验证两个容器均已运行
    cd ~/npm && docker compose ps

    appdb 容器的状态均应显示为 Up

  5. 第五步:访问 Web 界面

    打开浏览器,访问 http://YOUR_VPS_IP:81(将 YOUR_VPS_IP 替换为您的 VPS 实际 IP 地址)。

端口冲突:如果 Nginx 或 Apache 已在运行并监听 80 或 443 端口,Docker 将无法绑定这些端口。请先停止并禁用冲突服务:systemctl stop nginx && systemctl disable nginx

首次登录与修改默认密码

首次打开 Web 界面时,使用以下默认凭据登录:

NPM 会立即提示您更新邮箱地址并设置新密码。请不要跳过此步骤——默认凭据是公开信息,存在安全风险。

首次登录最佳实践

添加第一个代理主机

代理主机(Proxy Host)将域名映射到在特定端口上运行的后端服务(可在主机上运行,也可在同一网络的 Docker 容器内运行),并自动为该域名申请 SSL 证书。

如何添加代理主机

  1. 第一步:前往 Hosts → Proxy Hosts → Add Proxy Host

    点击顶部的 Hosts 菜单,选择 Proxy Hosts,然后点击右侧绿色的 Add Proxy Host 按钮。

  2. 第二步:填写 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),请启用
  3. 第三步:在 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
  4. 第四步:点击 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 中完成配置的方法。

目标域名映射

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.commonitor.example.comdocker.example.com 以及你创建的任何新子域。这样每次新增服务时就无需再申请新证书。

通配符证书需要通过 DNS Challenge(而非标准的 HTTP Challenge)来证明你对域名 DNS 的控制权。NPM 支持 Cloudflare、Route 53、Namecheap 及多个其他 DNS 服务商。

通过 Cloudflare DNS Challenge 申请通配符证书

  1. 创建 Cloudflare API Token

    登录 Cloudflare → Profile → API Tokens → Create Token → 使用"Edit zone DNS"模板,并将权限限制到你的特定区域。

  2. 在 NPM 中添加新 SSL 证书

    前往 SSL Certificates → Add SSL Certificate → Let's Encrypt

    • Domain Names:*.example.comexample.com(两者都添加,以覆盖根域名)
    • Use a DNS Challenge:启用
    • DNS Provider:Cloudflare
    • Credentials File Content:dns_cloudflare_api_token = YOUR_CF_API_TOKEN
    • Propagation Seconds:120(等待 2 分钟以完成 DNS 传播)
  3. 点击 Save

    NPM 将使用 API Token 在 Cloudflare 中创建 TXT 记录以证明域名所有权,然后从 Let's Encrypt 签发通配符证书。

  4. 将通配符证书应用到代理主机

    创建或编辑代理主机时,在 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,或将旧域名指向新域名。

如何设置重定向

  1. 前往 Hosts → Redirection Hosts → Add Redirection Host
  2. 填写详情
    • Domain Names:源域名,例如 example.com(不含 www)
    • Scheme:https
    • Forward Domain Name:目标域名,例如 www.example.com
    • HTTP Code:301 表示永久重定向(推荐用于 SEO)
    • Preserve Path:启用后 /about 将重定向到 www.example.com/about
  3. 添加 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 无法连接后端服务。常见原因:

# Check running containers
docker ps

# Test connectivity from inside the NPM container
docker exec npm-app-1 curl -s http://CONTAINER_NAME:PORT

SSL 证书申请失败

# 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 目录只需数秒,却能在跨主版本升级时防止潜在的数据库迁移问题。

各目录存储内容

总结与后续步骤

Nginx Proxy Manager 是任何运行多个服务的 VPS 的优秀工具。它将原本需要 Nginx 专业知识才能完成的任务变成了任何人都能在浏览器中轻松搞定的事情,同时通过 Advanced 选项卡和自定义配置为高级用户保留了所需的灵活性。

以下情况 NPM 是最佳选择

需要注意的局限性

推荐后续步骤

准备好在 VPS 上运行 Nginx Proxy Manager 了吗?

AsiaGB VPS 开箱即提供完整 root 权限、1 GB 或更大内存以及 Docker 支持。数据中心位于泰国和新加坡,最低仅需 500 泰铢/月。

查看 AsiaGB VPS 套餐