WordPress 伪静态规则完全指南:nginx、Apache、IIS 与宝塔配置
伪静态配错的症状只有两种:要么除首页外全部 404,要么一保存固定链接就 500。前者是规则没生效,后者多半是规则写进了不该写的层级——最典型的是把 try_files 塞进 location ~ .php$,让 index.php 自己重写自己,nginx 报 rewrite cycle,整站 500。下面按 nginx、Apache、IIS、宝塔四条线给出可直接粘贴的规则,再讲清 404 和 500 分别指向哪一类错误。
先明确一件事:WordPress 只负责生成好看的链接,能不能访问由 web 服务器决定。后台「设置 → 固定链接」选完结构,WordPress 会尝试写 .htaccess,但 nginx 根本不读 .htaccess。所以 nginx 站点必须有 location / 里的 try_files,或者用面板自带的伪静态模板,二者选一。
动手之前先做三个判断,能省掉一半试错:
- 访问
/index.php?p=123能打开,访问/文章别名/404 —— 纯伪静态问题,PHP 和数据库都正常。 - 首页正常、内页 500 —— 规则里有循环重写或者语法错误,用
nginx -t能直接看到行号。 - 后台能进、前台全 404,且换浏览器依旧 —— 规则生效了,但被 CDN 或缓存插件挡在旧版本上。
一、nginx:location / 段完整写法
下面是宝塔 LNMP 环境下一个站点的最小可用配置,PHP 8.1,路径按自己的站点替换。这段可以整段贴进站点配置文件,也可以只取 location 部分。
server {
listen 80;
server_name example.com www.example.com;
root /www/wwwroot/example.com;
index index.php index.html;
location / {
try_files $uri $uri/ /index.php?$args;
}
location ~ .php$ {
fastcgi_pass unix:/tmp/php-cgi-81.sock;
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
location ~* .(js|css|png|jpg|jpeg|gif|ico|webp|svg|woff2)$ {
expires 30d;
access_log off;
}
location ~ /.(htaccess|user.ini|git) {
deny all;
}
}
try_files 那一段,四个部分各管什么
整条规则只有一行,但漏掉任何一段都会出问题,逐段拆开看:
| 片段 | 作用 | 写错的表现 |
|---|---|---|
| $uri | 按请求的原始路径去磁盘找同名文件,如 /about/ 对应 about 目录 | 删掉后所有页面都交给 PHP 处理,静态资源也走 PHP,速度明显下降 |
| $uri/ | 按目录找,命中目录时交给 index 指令 | 缺这段时,以斜杠结尾的分类页、标签页会 404 |
| /index.php | 前两者都没命中,把请求交给 WordPress 入口 | 写成 /index.php 不带参数,分页 /page/2/ 会全部跳回首页第一页 |
| ?$args | 把原始查询串原样带过去 | 漏掉后 ?s=关键词 搜索、?p=123 短链接全部失效 |
还有两个隐蔽的坑。第一,location ~ .php$ 里的点必须转义,写成 location ~ .php$ 时,正则里的点匹配任意字符,/about/php-notes/ 这种带 php 字样的普通页面会被当 PHP 执行,返回 No input file specified。第二,try_files 只应该出现在 location / 里。放进 PHP 段之后,请求 /index.php 又匹配到 PHP 段,再重写回 /index.php,形成内部循环,错误日志里是 rewrite or internal redirection cycle while internally redirecting to "/index.php",用户看到的就是 500。
二、Apache 与 IIS:.htaccess 和 web.config
Apache:只有四行有效
Apache 2.4 + PHP-FPM 的组合下,WordPress 会自己在网站根目录生成 .htaccess。如果后台提示「无法写入 .htaccess」,把文件权限改成 644、属主改成 www,再回后台保存一次固定链接。
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^index.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]
</IfModule>
两个前提缺一不可:mod_rewrite 已启用(Debian/Ubuntu 下 a2enmod rewrite),虚拟主机配置里 AllowOverride All。AllowOverride 是 None 时 .htaccess 被整个忽略,症状和 nginx 没写规则一模一样——首页正常,内页 404,而且改 .htaccess 毫无反应。
RewriteBase / 在子目录安装时必须改。站点装在 /blog/ 下就写 RewriteBase /blog/,漏改的典型表现是后台登录成功后跳回一个 404 页面,登录 cookie 其实是正常的。
IIS:先装 URL Rewrite 模块
Windows Server + IIS 8.5 以上,先确认装了 URL Rewrite 模块,否则 web.config 里的 rewrite 节点会被静默忽略,不报错也不生效。装完后把下面这份放到网站根目录:
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<system.webServer>
<rewrite>
<rules>
<rule name="WordPress" stopProcessing="true">
<match url="^(.*)$" />
<conditions logicalGrouping="MatchAll">
<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
<add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
</conditions>
<action type="Rewrite" url="index.php" />
</rule>
</rules>
</rewrite>
<defaultDocument>
<files>
<clear />
<add value="index.php" />
<add value="index.html" />
</files>
</defaultDocument>
</system.webServer>
</configuration>
defaultDocument 这一段是 IIS 特有的:默认文档里没有 index.php 时,访问根目录会列出文件列表或直接 403,而内页却正常,很容易误判成权限问题。另外 PHP 要走 FastCGI 模式,ISAPI 模式在 PHP 7 之后已经不再支持。
三、宝塔面板:位置比规则本身更重要
宝塔 8.x 的路径是:网站 → 找到站点 → 设置 → 伪静态 → 下拉选「WordPress」→ 保存。保存后规则写到 /www/server/panel/vhost/rewrite/域名.conf,站点主配置里用 include 引进去。
三个实际操作中的注意点:
- 不要在「配置文件」里手改完,又去伪静态页点保存,两处会互相覆盖。要自定义就只在伪静态框里改。
- 切换 PHP 版本、改域名、开 HTTPS 之后,都回伪静态页确认模板还在。有些版本重写站点配置时会把 include 丢掉。
- 站点开了「防跨站攻击(open_basedir)」时,规则完全正确也可能 500,日志里是
open_basedir restriction in effect,这时要放宽 open_basedir 而不是改伪静态。
四、404 与 500 分别说明什么
把日志关键词和现象对上,基本不用猜。
| 现象 | 日志关键词 | 原因 | 处理 |
|---|---|---|---|
| 除首页外全 404 | 无异常 | 伪静态规则未生效 | 检查 location / 是否有 try_files,或面板伪静态是否选中 |
| 内页 404,但 /index.php?p=123 正常 | 请求返回 404,未打到 index.php | 规则缺失,或被面板模板覆盖 | 重新保存伪静态,重载 nginx |
| 全站 500 | rewrite or internal redirection cycle | try_files 写进了 PHP 段,形成循环 | 从 location ~ .php$ 里删掉 try_files |
| 全站 500 | unexpected “}” 或 unknown directive | 配置语法错误,多一个括号或分号 | nginx -t 按提示行号改 |
| 页面能开但样式全丢 | css/js 请求返回 404 | 规则把静态资源也转给了 PHP | 加 location ~* .(js|css|…) 并放在 PHP 段之前 |
一句话记法:404 是「请求没被转给 WordPress」,属于规则缺失;500 是「转过去之后服务器自己崩了」,属于规则写错或权限、扩展问题。这两类的排查方向完全不同,别混着试。
还要分清一件事:伪静态只影响前台路由,不改数据库里存的链接。有人以为换套规则文章地址会自动变短,其实变的是固定链接结构,这两件事各管一段。规则改完前台还是旧地址,去后台看固定链接设置,不要继续在 nginx 里找原因。
五、四种非标准场景
站点装在二级目录
WordPress 装在 /www/wwwroot/example.com/blog/ 时,要么把 root 直接指到 /blog,要么保留站点根目录、把规则写成 location /blog/ { try_files $uri $uri/ /blog/index.php?$args; }。后者最容易漏掉 index.php 前面的 /blog 前缀,症状是首页能开、内页全跳到站点根目录的 404。Apache 对应的是 RewriteBase /blog/,同一个道理。
多站点子目录模式
开启子目录模式的多站点后,规则要换成 WordPress 官方给的多站点版本,比单站点多一条针对 /wp-admin/ 的判断。直接用面板默认的 WordPress 模板会漏这一段,表现是新开的子站后台打不开、文章页 404。子域名模式还要额外加泛解析,并单独申请泛域名证书。
强制 HTTPS 与伪静态抢规则
80 跳 443 的 return 301 要写在 location / 之前。写在 include 伪静态之后时,可能被 try_files 先命中,跳转不生效但页面正常,很难发现。验证方法:curl -I http://域名/,返回 301 且 Location 指向 https 才算对。
六、改完之后的验证顺序
- 命令行执行
nginx -t,通过再nginx -s reload;Apache 用apachectl configtest。 - 清掉缓存插件的缓存,再清 CDN 缓存,两处都清。只清一处经常看到旧结果,CDN 的缓存键和 URL 编码相关,细节见 CDN 加速。
- 用无痕窗口依次测:首页、分类页、文章页、分页 /page/2/、搜索结果页、404 页。分页和搜索是最容易漏的两项。
- 看
access.log,确认这些请求确实落到了 index.php,状态码是 200 而不是 301 循环。 - 顺手确认 wp-admin 和 wp-login.php 正常,避免规则把后台也重写了。
虚拟主机环境没有 nginx.conf 权限,只能在面板伪静态里选模板,或者让主机商加规则。如果主机连 rewrite 都不支持,改结构这条路就走不通,得回到 安装部署 层面重新选环境。规则生效但内页仍然打不开的,可以对照 404 处理 里的分类继续查;如果是固定链接结构本身选错导致的跳转混乱,看 固定链接 那一篇。
信科网络在淄博只做 WordPress,做了 11 年,这类配置问题看一眼报错原文基本能定位。手上站点起不来的,把 nginx -t 的输出和 access.log 最后 20 行发到 web@wordpressx.com,或者直接打 13280692153 说现象。