htaccess 伪静态文件如何配置 常用网站重写规则参考

伪静态这件事,折腾过的人都知道,最让人抓狂的不是规则写不出来,而是你明明写对了,浏览器打开还是 404。明明照着教程一字不差抄的,偏偏别人能用你不行。所以与其上来就讲 RewriteRule 的正则怎么写,不如先把那个前置问题说清楚——你的 .htaccess 为什么根本没生效。

先确认文件名。这个听起来像废话,但实际翻车率极高。Windows 默认隐藏已知扩展名,你用记事本另存为的时候文件名输入 .htaccess,保存出来可能变成 .htaccess.txt。上传到服务器之后 Apache 根本不认这个文件。在 SSH 里用 ls -la 看一眼,确认文件名就是纯的 .htaccess,前面有个点,后面没有别的后缀。

htaccess 伪静态文件如何配置 常用网站重写规则参考

文件权限也有讲究。Apache 进程通常以 www-data 或 apache 用户运行,这个用户必须能读取你的 .htaccess 文件。权限设成 644 就够,所有者可读写,其他人只读。如果你是用 root 账户通过 SFTP 上传的,文件所有者可能变成了 root,Apache 读不了。chown www-data:www-data .htaccess 改一下属主,这个问题就解决了。

这两个都确认没问题之后,最容易被忽略的一层是 Apache 主配置里的 AllowOverride。如果这个参数设的是 None,Apache 会完全无视 .htaccess 文件的存在,你规则写得再漂亮也白搭。需要把网站目录对应的 块里的 AllowOverride 改成 All,然后重启 Apache。另外 mod_rewrite 模块本身也得启用,Ubuntu 下 sudo a2enmod rewrite,CentOS 下确认 httpd.conf 里有 LoadModule rewrite_module modules/mod_rewrite.so 这一行且没被注释掉。

前置条件都通了,再来看规则本身怎么写。

.htaccess 文件通常以三行开头。RewriteEngine On 打开重写引擎,这个不用解释。RewriteBase / 设基础路径,如果你的网站装在根目录,写 / 就行;装在子目录比如 /blog/ 里,那就写 /blog/。还有一行 把整段规则包起来,作用是万一服务器没装 rewrite 模块,Apache 会直接跳过这段内容而不是报 500 错误。这个保险值得加。

核心语法就两个指令:RewriteRule 和 RewriteCond。

RewriteRule 包含三个部分,用空格隔开。第一部分是匹配模式,一个正则表达式,用来判断用户请求的 URL 长什么样。第二部分是替换目标,匹配成功之后把请求转到哪里去。第三部分是可选标志,用方括号括起来。

举个最基础的例子。你把动态页面 /article.php?id=123 伪装成 /article/123 这种静态形式,规则这么写:

RewriteRule ^article/([0-9]+)$ article.php?id=$1 [L]

^article/([0-9]+)$ 这个正则说的是:URL 以 article/ 开头,后面跟一个或多个数字,然后结束。括号包住的 ([0-9]+) 把数字部分捕获下来,在替换目标里用 $1 引用。所以用户访问 /article/123 的时候,服务器内部实际执行的是 article.php?id=123。

方括号里的 [L] 意思是这是最后一条规则,匹配成功之后不再往下处理别的 RewriteRule。

如果 URL 里还带查询参数,比如 /product/123?color=red,你希望参数能传下去,加个 QSA 标志就行:[QSA,L]。QSA 的全称是 Query String Append,作用是把原始请求里的查询字符串拼接到重写后的 URL 后面,不会丢掉。

RewriteCond 是给 RewriteRule 加条件的。最常用的一个场景是:如果请求的文件或目录真实存在,就直接放行,别走重写。这个在几乎所有 CMS 的伪静态规则里都会出现:

RewriteCond %{REQUEST_FILENAME} !-f

RewriteCond %{REQUEST_FILENAME} !-d

RewriteRule ^ index.php [L]

前两行的意思是,请求的路径不是一个真实文件(!-f),也不是一个真实目录(!-d)。只有同时满足这两个条件,才把请求交给 index.php 去处理。如果不加这个条件,你网站上的 CSS、JS、图片这些静态资源全都会被重写到 index.php,页面样式直接崩掉。

再说几个常见的整站规则场景。

把 HTTP 全部跳到 HTTPS,很多人需要这个。规则就三行:

RewriteEngine On

RewriteCond %{HTTPS} off

RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]

%{HTTPS} 是服务器变量,值为 off 时说明当前是 HTTP 请求。%{HTTP_HOST} 是域名,%{REQUEST_URI} 是请求路径。R=301 告诉浏览器这是永久重定向。

去掉 URL 里的 www 或者反过来强制加 www,也是高频需求。强制加 www:

RewriteCond %{HTTP_HOST} !^www. [NC]

RewriteRule ^(.*)$ https://www.%{HTTP_HOST}/$1 [R=301,L]

!^www. 表示域名不是以 www. 开头,[NC] 忽略大小写。匹配到之后拼上 www. 再跳转回去。

禁止别人盗链你的图片资源,也可以用 RewriteCond 配合:

RewriteCond %{HTTP_REFERER} !^$

RewriteCond %{HTTP_REFERER} !^https?://(www.)?你的域名.com [NC]

RewriteRule .(jpg|jpeg|png|gif)$ - [F,L]

意思是如果来源页不是空的(空 referer 可能是用户直接输网址访问),也不是你自己的域名,那就把请求图片的访问直接禁止掉,[F] 就是 Forbidden。

不同 CMS 的伪静态规则长得不一样,但套路都是上面那些。把几个典型的贴出来。

WordPress 的规则你其实不用手写,后台设置固定链接之后它会自动生成:


<IfModule mod_rewrite.c> 
RewriteEngine On 
RewriteBase / 
RewriteRule ^index.php$ - [L] 
RewriteCond %{REQUEST_FILENAME} !-f 
RewriteCond %{REQUEST_FILENAME} !-d 
RewriteRule . /index.php [L] </IfModule>

RewriteRule ^index.php$ - [L] 这一行的意思是,如果请求本身就是 index.php,替换目标用 - 表示不做任何替换,直接停止处理。如果你的 WordPress 装在子目录里,比如 /wordpress/,RewriteBase 和最后那行的 /index.php 都要带上子目录前缀,这个很多人会忘。

Discuz 的规则条目多一点,按页面类型分开写:


RewriteEngine On RewriteBase / RewriteRule ^forum-([0-9]+)-([0-9]+).html$ forumdisplay.php?fid=$1&page=$2 [L] RewriteRule ^thread-([0-9]+)-([0-9]+)-([0-9]+).html$ viewthread.php?tid=$1&extra=page%3D$3&page=$2 [L] RewriteRule ^space-(username|uid)-(.+).html$ space.php?$1=$2 [L]

注意 Discuz 后台还得先去“全局”下面的“SEO设置”里把 URL 静态化开关打开,光配 .htaccess 不打开后台开关同样没用。

Typecho 的规则非常简洁,跟 WordPress 的思路类似但更短:


<IfModule mod_rewrite.c> 
RewriteEngine On 
RewriteBase / 
RewriteCond %{REQUEST_FILENAME} !-f 
RewriteCond %{REQUEST_FILENAME} !-d 
RewriteRule ^(.*)$ /index.php/$1 [L] 
</IfModule>

跟 WordPress 的区别在于最后一行。WordPress 是 /index.php,Typecho 用的是 /index.php/$1,把原始路径作为 PATH_INFO 传给入口文件。PBootCMS 的规则把路径作为参数 p 传进去:


<IfModule mod_rewrite.c>
Options +FollowSymlinks
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ index.php?p=$1 [QSA,PT,L]
</IfModule>

这里的 [PT] 标志意思是把重写后的 URL 再交给 Apache 做进一步处理,[QSA] 保留原始查询字符串。规则配好了但还是不生效的时候,别急着一遍遍改 .htaccess,先打开 Apache 的重写调试日志看看它到底在干什么。在 Apache 主配置里加一行 LogLevel alert rewrite:trace3,重启之后去看 error.log,里面会详细记录每一条 RewriteRule 的匹配过程和结果。这个日志能告诉你规则到底执行到了哪一步、匹配成功了没有、最终重写成了什么地址,比盲猜快得多。看完记得把日志级别调回去,trace3 级别的日志量很大,生产环境一直开着会撑爆磁盘。还有一个新手常踩的坑是重定向循环。比如你把 HTTP 跳到 HTTPS 的规则写成了 RewriteRule ^(.*)$ https://... [R=301,L] 但没有加 RewriteCond %{HTTPS} off 这个条件,那 HTTPS 请求也会被重写,重写之后又变成 HTTPS,再重写一次,浏览器就报 “重定向次数过多”。看到这个提示,第一个怀疑对象就是自己的 RewriteCond 条件写漏了。子目录安装的程序还有一个容易混淆的地方。假设 WordPress 装在 /blog/ 里,你把 .htaccess 放在 /blog/ 下面,RewriteBase /blog/,这是对的。但如果你把 .htaccess 放在网站根目录,想统一管理所有子目录的规则,那 RewriteRule 里的路径就要从根目录写起,不能只写 index.php 而应该是 /blog/index.php。两条路选一条,别混着来,混着来很容易出现规则匹配上了但目标路径不对的情况。

免责声明:本文提供的方法与建议仅供技术参考,实际部署效果受机房环境、运营商线路、系统配置等多重因素影响。请在专业人员指导下操作,因自行操作导致的业务损失本站不承担责任。

相关推荐