我用Lnmp部署WordPress的第一天,把固定链接从默认改成/%postname%/,整站除了首页全变404。
这不是个例。WordPress官方文档默认按Apache的.htaccess写规则,Nginx没有.htaccess,这导致90%的新手在迁移后第一时间踩坑。
我这篇只讲Nginx server block里最容易踩的5个真实陷阱,每个都有我亲历的报错和修复命令。
陷阱一:try_files缺少/index.php导致固定链接全部404
**症状**:首页正常,所有文章页、分类页、标签页全部返回404 Not Found(Nginx默认404页面,不是WordPress主题的404)。
**根因**:Nginx的try_files指令决定了请求如何路由。WordPress使用前端控制器模式——所有非静态文件请求都应该交给index.php处理。少了/index.php这个fallback,Nginx找不到文件就直接返回404。
错误配置:
location / {
try_files $uri $uri/ =404; # ← 缺少PHP fallback
}
正确配置:
location / {
try_files $uri $uri/ /index.php?$args; # ← 关键:所有请求回退到index.php
}
$args不能省略。少了它,WordPress的查询参数(分页?page=2、搜索?s=keyword)全部丢失。
**验证**:改完配置后sudo nginx -t && sudo systemctl reload nginx,然后访问任意文章页,应该正常显示。
陷阱二:FastCGI缓存绕过规则不完整导致后台泄漏缓存页面
症状:管理员登录后台,看到的居然是访客的缓存页面——修改文章后前台更新了但后台还是旧的;WooCommerce购物车显示别人的商品。
**根因**:Nginx的fastcgi_cache默认忽视Cookie,所有请求一律缓存。WordPress通过wordpress_logged_in_* Cookie区分登录用户,但Nginx不知道。
**修复**:在http {}块或server {}块开头添加缓存绕过映射:
# 在 http {} 块中添加
map $http_cookie $skip_cache {
default 0;
~wordpress_logged_in 1;
~wp-postpass 1;
~woocommerce_items_in_cart 1;
}
# 在 server {} 块的 PHP location 中使用
location ~ \.php$ {
fastcgi_pass unix:/run/php/php8.3-fpm.sock;
fastcgi_cache WORDPRESS;
fastcgi_cache_bypass $skip_cache;
fastcgi_no_cache $skip_cache;
# ... 其他fastcgi参数
}
关键点:
- `fastcgi_cache_bypass`决定是否跳过缓存读取
- `fastcgi_no_cache`决定是否跳过缓存写入
- **两者必须同时设置**,只设一个还是会出问题
WooCommerce额外注意:购物车和结算页面必须额外排除:
map $uri $skip_cache_uri {
default 0;
~^/cart 1;
~^/checkout 1;
~^/my-account 1;
}
然后把变量合并:set $do_skip $skip_cache$skip_cache_uri;,用$do_skip替代原来的$skip_cache。
陷阱三:安全响应头遗漏让安全扫描全红
症状:securityheaders.com评分F,站长工具报"缺少安全头"警告。
**根因**:Nginx默认不添加任何安全响应头。WordPress自身也不管这事——它在Apache下靠.htaccess插件加,Nginx下必须手动配。
**一次配齐6个核心安全头**(在server {}块中添加):
# 防止MIME类型嗅探
add_header X-Content-Type-Options "nosniff" always;
# 防止点击劫持
add_header X-Frame-Options "SAMEORIGIN" always;
# HSTS — 只在SSL完整启用后才开启!
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
# 控制Referrer信息泄露
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
# 限制浏览器功能(摄像头/麦克风/定位等)
add_header Permissions-Policy "camera=(), microphone=(), geolocation=(), payment=()" always;
# XSS保护(兼容旧浏览器)
add_header X-XSS-Protection "1; mode=block" always;
⚠️ 三个真实陷阱:
1. **always关键字不能省**——不加的话Nginx只在200/301响应时返回头,404/500时不返回,安全扫描照样扣分。
2. HSTS只配在HTTPS的server block里——如果你把HSTS写在80端口(HTTP)的block里,浏览器根本不会处理。
3. **CSP(Content-Security-Policy)不要无脑抄**——WordPress核心+插件+主题会加载大量内联脚本,简单的script-src 'self'直接把后台搞白屏。建议先用Content-Security-Policy-Report-Only观察,再逐步收紧。
陷阱四:xmlrpc.php未封禁被暴力破解打满CPU
**症状**:服务器CPU突然飙到100%,tail -f /var/log/nginx/access.log看到大量POST /xmlrpc.php 200请求。
根因:WordPress的xmlrpc.php默认开放,攻击者用它进行XML-RPC爆破——一次请求可以尝试上千个密码,比传统的wp-login.php暴力破解高效100倍。
Nginx封禁(直接在server block中添加):
# 完全封禁xmlrpc.php
location = /xmlrpc.php {
deny all;
}
如果你用Jetpack:Jetpack依赖xmlrpc.php通信。改成只允许Jetpack IP:
location = /xmlrpc.php {
allow 192.0.64.0/18; # Jetpack/Automattic IP段
deny all;
fastcgi_pass unix:/run/php/php8.3-fpm.sock;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
**验证**:curl -I https://你的域名/xmlrpc.php,应该返回403 Forbidden。
陷阱五:PHP文件上传限制在Nginx层被截断
**症状**:上传主题或插件时提示"上传的文件尺寸超过php.ini中定义的upload_max_filesize值",但php.ini里明明已经改成64M了。
**根因**:Nginx有自己的请求体大小限制,默认只有1MB。PHP层的upload_max_filesize和post_max_size改再大也没用,Nginx在前面就把请求拦了。
修复:
# 在 http {} 或 server {} 块中
client_max_body_size 64M;
完整的上传三件套(Nginx + PHP都要改):
# Nginx层
client_max_body_size 64M;
; PHP层(/etc/php/8.3/fpm/php.ini)
upload_max_filesize = 64M
post_max_size = 64M
max_execution_time = 300
memory_limit = 256M
改完PHP配置后重启PHP-FPM:sudo systemctl restart php8.3-fpm。
我的完整server block参考
基于spinupwp/wordpress-nginx(GitHub上维护最好的WordPress-Nginx模板)和我自己的踩坑经历,以下是一个生产可用的单站配置骨架:
server {
listen 443 ssl http2;
server_name example.com;
root /var/www/example.com/public;
index index.php;
# SSL配置(Let's Encrypt)
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
# 安全头
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;
# 文件上传
client_max_body_size 64M;
# WordPress固定链接
location / {
try_files $uri $uri/ /index.php?$args;
}
# 封禁xmlrpc
location = /xmlrpc.php {
deny all;
}
# 禁止访问敏感文件
location ~ /\. { deny all; }
location ~* /wp-config.php { deny all; }
location ~* /(wp-admin/setup-config\.php|wp-admin/install\.php) {
# 首次安装后可封禁
}
# PHP处理 + FastCGI缓存
location ~ \.php$ {
fastcgi_pass unix:/run/php/php8.3-fpm.sock;
fastcgi_cache WORDPRESS;
fastcgi_cache_valid 200 301 302 60m;
fastcgi_cache_bypass $skip_cache;
fastcgi_no_cache $skip_cache;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
# 静态文件缓存
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
expires 365d;
add_header Cache-Control "public, immutable";
access_log off;
}
}
server {
listen 80;
server_name example.com;
return 301 https://$host$request_uri;
}
变量版本参考(2026年6月验证)
| 组件 | 版本 |
|---|---|
| Nginx | 1.28.2 stable(含CVE-2026-1642修复) |
| PHP-FPM | 8.3 |
| WordPress | 6.8 |
| Ubuntu | 24.04 LTS |
版本号会变,配置逻辑不变。建议用nginx -v和php -v确认你自己的环境。
---
这篇只讲了Nginx server block本身的坑。如果你还在找WordPress数据库优化的坑,可以看我的另一篇:WordPress wp_options autoload优化。
如果你的WordPress跑在Docker里,Docker层还有额外的端口映射和网络配置坑——那是另一个战场了。
⚠️ 本文包含联盟链接。如果你通过链接购买,我可能获得少量佣金,不影响你的价格。
📌 本文由 AI 辅助生成并经人工审核发布 | TechPassive — AI 驱动的内容测试站点,专注于效率工具与 SaaS 真实评测
🔗 精选推荐工具
使用以下链接支持我们持续产出高质量内容(点击可直接前往购买):