引言
在容器化环境中部署 WordPress 网站时,使用 Docker Compose 编排 Nginx、WordPress 和 MySQL 是一种常见且高效的实践。然而,由于涉及多层网络通信与协议转换,部署过程中往往面临一系列复杂的技术挑战。
本文基于一次实际部署与排障经历,梳理了从端口无法访问到网站样式丢失的完整问题链,并针对每个故障点提供了详细的技术解析和可操作的解决方案。
故障点一:容器运行正常,但外部无法访问端口
现象docker ps显示容器状态为Up,且端口映射0.0.0.0:8000->80/tcp正常,但在浏览器中访问http://[Server_IP]:8000时,连接超时或失败。与此同时,同一服务器上的 Grafana(3000 端口)和 Prometheus(9090 端口)却可以正常访问。
技术分析与诊断
端口无法访问通常可从以下三层进行排查:
- Docker 端口映射:通过
docker ps确认端口映射已生效,排除 Docker 层面问题。 - 操作系统防火墙:检查
ufw status或firewall-cmd --list-all,确认防火墙未阻止 8000 端口。在本案例中,系统防火墙未启用或未安装,因此排除此因素。 - 云平台安全组:这是最常见的外部访问限制来源。云服务商的安全组(Security Groups)通常默认仅允许 SSH、HTTP/HTTPS 等常见端口,8000 端口可能未被放行。
解决方案
登录云服务商控制台,找到对应实例的安全组配置,添加一条入站规则,允许 TCP 8000 端口的流量。
故障点二:端口可访问,但返回 404 或 400 Bad Request
现象
端口开放后,访问http://[Server_IP]:8000仍无法加载 WordPress 页面,有时返回 404,有时 Nginx 日志中出现400 Bad Request并伴随大量乱码。
技术分析与诊断400 Bad Request及乱码往往是 HTTPS 握手数据被发送到 HTTP 端口所致,原因可能是浏览器缓存或 HSTS 策略强制跳转 HTTPS。
同时,Nginx 配置中server_name localhost;也是一个关键问题:当请求的Host头不是localhost时,Nginx 可能无法匹配到正确的虚拟主机,从而返回默认的 404 页面。
解决方案
- 修正 Nginx 配置:将
server_name改为服务器 IP 或使用通配符_:
server { listen 80; server_name _; # 或改为您的服务器 IP/域名 # ... }2.强制使用 HTTP 协议访问:在浏览器中完整输入http://前缀,并清除缓存或使用隐身模式访问,避免浏览器自动跳转 HTTPS。
故障点三:绑定域名后样式丢失
现象
成功将服务切换至标准 80 端口并绑定域名后,浏览器能加载 HTML 内容,但所有 CSS、图片和 JavaScript 资源均无法加载,页面失去样式。控制台报错:“Mixed Content: The page was loaded over HTTPS, but requested an insecure stylesheet 'http://...'”。
技术分析与诊断
这是典型的“混合内容”问题。当用户通过 HTTPS 访问域名时,Nginx 将请求转发给 WordPress(内部仍使用 HTTP)。WordPress 感知到运行在 HTTP 环境下,因此生成的资源链接仍为http://。浏览器基于安全策略,阻止加载非 HTTPS 资源,导致样式丢失。
解决方案
需要同时调整 Nginx 和 WordPress 配置,确保 WordPress 能识别外部实际使用的协议。
- Nginx 转发协议头:在 Nginx 配置中添加以下头部,告知 WordPress 外部请求的协议:
location / { proxy_pass http://wordpress:80; 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; }2.WordPress 配置 HTTPS 并信任代理:在wp-config.php中强制定义站点 URL 为 HTTPS,并启用代理信任逻辑:
// 在 /* Add any custom values between this line and the "stop editing" line. */ 下方添加 define('WP_HOME', 'https://domain name '); define('WP_SITEURL', 'https://domain name '); // 信任 Nginx 传递的 X-Forwarded-Proto 头(通常 Docker WordPress 镜像已包含此代码) if (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && strpos($_SERVER['HTTP_X_FORWARDED_PROTO'], 'https') !== false) { $_SERVER['HTTPS'] = 'on'; }注:在域名解析生效前,可暂时使用http://Server_IP:port进行测试。
故障点四:经 NPM 代理后出现“重定向次数过多”
现象
完成上述配置后,若前端使用 Nginx Proxy Manager(NPM)进行反向代理,访问https://domain.com可能出现“重定向次数过多”的错误。
技术分析与诊断
此时流量路径为:
客户端 → NPM → Nginx → WordPress
若 Nginx 仍基于$scheme(来自 NPM 的 HTTP 流量)设置X-Forwarded-Proto,WordPress 可能认为仍在 HTTP 环境,从而触发重定向至 HTTPS,形成循环。
解决方案
将 Nginx 配置中的X-Forwarded-Proto显式设为https,并添加 SSL 标识,以匹配外部实际使用的协议:
proxy_set_header X-Forwarded-Proto https; proxy_set_header X-Forwarded-SSL on;架构优化建议
实际上,最优的架构是简化流量路径,将 Nginx 替换为 NPM,直接由 NPM 代理至 WordPress 容器:
- 原路径:客户端 → NPM → Nginx → WordPress
- 优化后:客户端 → NPM → WordPress
这样可以减少一层代理转发,降低配置复杂度,避免因多层协议转换导致的重定向和头部传递问题,提升整体稳定性和可维护性。
通过以上分步诊断与调整,可系统性地解决在 Docker Compose 环境中部署 WordPress 时常见的网络、协议与样式加载问题,最终实现稳定、安全的网站访问。
下面是更改后的配置文件:
docker-compose.yml
services: # 应用 wordpress: depends_on: #确保先启动db和redis容器才启动wordpress,避免出现wordpress启动时链接不上数据库或缓存错误 - db - redis image: wordpress:latest #拉取最新wordpress官方镜像 container_name: myapp-wp #指定容器名字 environment: #配置环境变量 WORDPRESS_DB_HOST: db:3306 WORDPRESS_DB_USER: wordpress WORDPRESS_DB_PASSWORD: wordpress WORDPRESS_DB_NAME: wordpress WORDPRESS_URL: http://locahost:prot TZ: Asia/Shanghai #设置上海时区 volumes: - ./wp:/var/www/html #将宿主机当前目录下wp目录映射到容器内部 restart: always #自动重启 #数据库 db: image: mysql:8.0 #拉取Mysql8.0官方镜像 container_name: myapp-db #指定容器名字 environment: #数据库初始数据 MYSQL_ROOT_PASSWORD: somewordpress MYSQL_DATABASE: wordpress MYSQL_USER: wordpress MYSQL_PASSWORD: wordpress TZ: Asia/Shanghai volumes: #将db_data映射到容器内部/var/lib/mysql 持久化数据卷 - db_data:/var/lib/mysql restart: always healthcheck: #定义数据库是否健康 test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "-p$MYSQL_ROOT_PASSWORD"] interval: 10s timeout: 5s retries: 5 start_period: 30s #缓存 redis: image: redis:alpine #拉取redis轻量化镜像 container_name: myapp-redis #容器名字 environment: TZ: Asia/Shanghai restart: always #nginx反向代理 nginx: image: nginx:alpine #拉取nginx轻量化镜像 container_name: myapp-nginx #容器名字 ports: #把宿主机6060端口映射到容器80端口 - "6060:80" volumes: - ./nginx.conf:/etc/nginx/nginx.conf:ro #映射宿主机自定义的nginx.conf配置文件映射容器内部 - /home/yxwa/wordpress/wp:/var/www/html:rw #再次映射wordpress网站文件,提供静态资源访问 depends_on: #确保wordpress启用后再启动nginx - wordpress environment: TZ: Asia/Shanghai restart: always #采集和存储指标数据 prometheus: image: prom/prometheus:latest volumes: - ./prometheus.yml:/etc/prometheus/prometheus.yml:ro #映射宿主机配置文件 - prometheus_data:/prometheus #持久化存储采集数据 command: #告诉prometheus使用那个配置文件以及数据存储路径 - '--config.file=/etc/prometheus/prometheus.yml' - '--storage.tsdb.path=/prometheus' ports: - "9090:9090" environment: TZ: Asia/Shanghai restart: always #数据可视化 grafana: image: grafana/grafana:latest ports: - "3000:3000" volumes: - grafana_data:/var/lib/grafana environment: TZ: Asia/Shanghai GF_AUTH_ANONYMOUS_ENABLED: "false" #禁用匿名访问,需要登录才能看到仪表盘 restart: always volumes: db_data: {} #存储mysql数据库所有文件 prometheus_data: {} #存储Prometheus采集的时间序列数据 grafana_data: {} #存储grafana的配置、仪表盘等数据nginx.conf
events { worker_connections 1024; #设置每个worker进程打开最大连接数,1024是默认值 } http { server { listen 80; #监听容器80端口 server_name locahost:prot; #定义服务器名称。当请求的 Host 头与此匹配时,此 server 块将被使用。需要将其更改为实际的域名或 IP 地址,例如 localhost 或 192.168.1.100。 location / { # 匹配所有进入的请求 (/),并定义如何处理它们 proxy_pass http://wordpress:80; #所有请求转发到wordpress服务的80端口 proxy_set_header Host $host; #将原始请求HOST头信息传递给后端wordpress容器 proxy_set_header X-Real-IP $remote_addr;#传递客服端真实IP地址 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;#传递列表,帮助识别流量来源 proxy_set_header X-Forwarded-Proto https;#告诉后端wordpress程序,尽管nginx通过http链接到wordpress但是最初使用的https连接到nginx proxy_set_header X-Forwarded-SSL on; } } }prometheus.yml
global: scrape_interval: 15s #全局抓取间隔,15s抓取一次数据 scrape_configs: - job_name: 'prometheus' #定义监控任务,监控自身状态和性能 static_configs: - targets: ['prometheus:9090'] #告诉抓取prometheus服务的9090端口,prometheus会暴露自己的健康指标再这个端口上