静态映射
/contact
,/about
将几个页面名称简化为内部文件方案是最简单的:
RewriteRule ^contact$ templ/contact.html
RewriteRule ^about$ about.php
数字标识符
/object/123
http://example.com/article/531
向现有的PHP脚本等引入快捷方式也很容易。可以将数字占位符重新映射到$_GET
参数:
RewriteRule ^article/(\d+)$ article-show.php?id=$1
# └───────────────────────────┘
子弹型占位符
/article/with-some-title-slug
您可以轻松地扩展该规则以允许/article/title-string
占位符:
RewriteRule ^article/([\w-]+)$ article-show.php?title=$1
# └────────────────────────────────┘
请注意,您的脚本 必须能够(或适应)将这些标题映射回数据库ID。仅RewriteRules不能凭空创造或猜测信息。
带有数字前缀的子弹
/readable/123-plus-title
因此,您经常会看到/article/529-title-slug
实践中使用的混合路径:
RewriteRule ^article/(\d+)-([\w-]+)$ article.php?id=$1&title=$2
# └───────────────────────────────┘
现在您title=$2
无论如何都可以跳过传递,因为您的脚本通常无论如何都会依赖于数据库ID。在-title-slug
已经成为任意URL装饰。
备选清单的一致性
/foo/…
/bar/…
/baz/…
如果您对多个虚拟页面路径具有相似的规则,则可以使用|
备用列表进行匹配和压缩。再次将它们重新分配给内部GET参数:
# ┌─────────────────────────┐
RewriteRule ^(blog|post|user)/(\w+)$ disp.php?type=$1&id=$2
# └───────────────────────────────────┘
如果RewriteRule
太复杂,可以将它们拆分为单个。
将相关的URL分配到不同的后端
/date/SWITCH/backend
替代列表的更实际用途是将请求路径映射到不同的脚本。例如,根据日期为较旧和较新的Web应用程序提供统一的URL:
# ┌─────────────────────────────┐
# │ ┌───────────┼───────────────┐
RewriteRule ^blog/(2009|2010|2011)/([\d-]+)/?$ old/blog.php?date=$2
RewriteRule ^blog/(\d+)/([\d-]+)/?$ modern/blog/index.php?start=$2
# └──────────────────────────────────────┘
这只是将2009-2011年的帖子重新映射到一个脚本,而所有其他年份都隐式地映射到另一个处理程序。请注意,更具体的规则排在第一位。每个脚本可能使用不同的GET参数。
除/
路径斜杠以外的其他定界符
/user-123-name
您最常见的情况是看到RewriteRules来模拟虚拟目录结构。但是,您不会被迫独创。您也可以使用-
连字符进行分段或结构化。
RewriteRule ^user-(\d+)$ show.php?what=user&id=$1
# └──────────────────────────────┘
# This could use `(\w+)` alternatively for user names instead of ids.
对于同样常见的/wiki:section:Page_Name
方案:
RewriteRule ^wiki:(\w+):(\w+)$ wiki.php?sect=$1&page=$2
# └─────┼────────────────────┘ │
# └────────────────────────────┘
有时,在- /
定界符之间和/ :
或甚至.
在同一规则中交替也是合适的。或者再次使用两个RewriteRules将变体映射到不同的脚本。
可选的尾部/
斜杠
/dir
=/dir/
选择目录样式的路径时,无论是否使用最终形式,都可以使其到达 /
RewriteRule ^blog/([\w-]+)/?$ blog/show.php?id=$1
# ┗┛
现在,这将同时处理http://example.com/blog/123
和/blog/123/
。而且该/?$
方法很容易附加到任何其他RewriteRule上。
虚拟路径的灵活细分
.*/.*/.*/.*
您将遇到的大多数规则将一组受约束的/…/
资源路径段映射到各个GET参数。但是,某些脚本处理可变数量的选项。Apache regexp引擎不允许任意选择它们。但是您可以自己轻松地将其扩展为规则块:
Rewriterule ^(\w+)/?$ in.php?a=$1
Rewriterule ^(\w+)/(\w+)/?$ in.php?a=$1&b=$2
Rewriterule ^(\w+)/(\w+)/(\w+)/?$ in.php?a=$1&b=$2&c=$3
# └─────┴─────┴───────────────────┴────┴────┘
如果最多需要五个路径段,则将此方案复制到五个规则中。您当然可以分别使用更具体的[^/]+
占位符。这里的顺序并不重要,因为两者都不重叠。因此首先拥有最常用的路径是可以的。
另外,您可以?p[]=$1&p[]=$2&p[]=3
在此处通过查询字符串使用PHPs数组参数-如果您的脚本仅希望将它们预分割。(尽管更常见的是只使用包罗万象的规则,然后让脚本本身将这些段扩展到REQUEST_URI之外。)
另请参阅:如何将URL路径段转换为查询字符串键值对?
可选细分
prefix/opt?/.*
一个常见的变化是在规则中具有可选的前缀。如果您有静态字符串或更多受约束的占位符,通常这是有意义的:
RewriteRule ^(\w+)(?:/([^/]+))?/(\w+)$ ?main=$1&opt=$2&suffix=$3
现在,(?:/([^/])+)?
那里更复杂的模式只是包装了一个非捕获 (?:…)
组,并使其成为可选项)?
。包含的占位符([^/]+)
将是替换模式$2
,但如果没有中间/…/
路径,则为空。
捕获剩余的
/prefix/123-capture/…/*/…whatever…
如前所述,您通常不需要太通用的重写模式。但是,.*
有时将静态比较和特定比较相结合确实有意义。
RewriteRule ^(specific)/prefix/(\d+)(/.*)?$ speci.php?id=$2&otherparams=$2
这是可选的所有/…/…/…
尾随路径段。然后当然需要处理脚本将其分离,并variabl-IFY提取的参数(这就是Web的“MVC”的框架做)。
尾随文件“扩展名”
/old/path.HTML
网址实际上没有文件扩展名。整个引用所针对的是(URL是虚拟定位符,不一定是直接文件系统映像)。但是,如果以前有1:1文件映射,则可以制定更简单的规则:
RewriteRule ^styles/([\w\.\-]+)\.css$ sass-cache.php?old_fn_base=$1
RewriteRule ^images/([\w\.\-]+)\.gif$ png-converter.php?load_from=$2
其他常见用法是将过期的.html
路径重新映射到较新的.php
处理程序,或者仅为单独的(实际/实际)文件别名目录名称。
乒乓球(统一重定向和重写)
/ugly.html
←→/pretty
因此,在某些时候,您要重写HTML页面,使其仅包含漂亮的链接(如deceze概述)。同时,您仍然会收到旧路径的请求,有时甚至来自书签。至于解决方法,你可以乒乓浏览器显示/建立新网址。
这个常见的技巧涉及到只要传入URL遵循过时/难看的命名方案,就发送30x / Location 重定向。然后,浏览器将重新请求新的/漂亮的URL,然后将其(仅在内部)重写为原始或新的位置。
# redirect browser for old/ugly incoming paths
RewriteRule ^old/teams\.html$ /teams [R=301,QSA,END]
# internally remap already-pretty incoming request
RewriteRule ^teams$ teams.php [QSA,END]
请注意,此示例仅用于[END]
代替[L]
安全地进行替代。对于较旧的Apache 2.2版本,除了重新映射查询字符串参数外,您还可以使用其他解决方法:
将丑陋重定向到漂亮的URL,重新映射回丑陋的路径,而没有无限循环
␣模式中的空格
/this+that+
浏览器地址栏中的效果不是很好,但是您可以在URL中使用空格。对于重写模式,请使用反斜杠转义的\␣
空格。否则- "
引用整个模式或替换:
RewriteRule "^this [\w ]+/(.*)$" "index.php?id=$1" [L]
客户端使用+
或序列化URL %20
。但是在RewriteRules中,它们使用所有相对路径段的文字字符进行解释。
启用mod_rewrite
和.htaccess
要在每个目录的配置文件中实际使用RewriteRules,您必须:
检查您的服务器是否已AllowOverride All
启用。否则,您的按目录.htaccess
指令将被忽略,并且RewriteRules将不起作用。
显然在模块部分已mod_rewrite
启用httpd.conf
。
在每个规则列表前添加RewriteEngine On
静止图像。尽管mod_rewrite在<VirtualHost>
和<Directory>
部分中是隐式活动的,但每个目录的.htaccess
文件都需要单独召唤它。
前导斜杠^/
不匹配
您不应该.htaccess
使用以下^/
常规方式启动RewriteRule模式:
RewriteRule ^/article/\d+$ …
↑
这在旧教程中很常见。而且它过去对于古老的Apache 1.x版本是正确的。如今,在RewriteRules中,请求路径相对于目录是完全方便的.htaccess
。只是离开领导/
。
·请注意,<VirtualHost>
尽管前面的斜杠在各节中仍然是正确的。这就是为什么您经常看到它^/?
对于规则奇偶校验是可选的。
·或者使用时,RewriteCond %{REQUEST_URI}
您仍然可以匹配领先者/
。
·另请参阅Webmaster.SE:什么时候在mod_rewrite模式中需要斜杠(/)?
<IfModule *>
包装器即将消失!
您可能已经在许多示例中看到了这一点:
<IfModule mod_rewrite.c>
Rewrite…
</IfModule>
- 这确实是有意义的
<VirtualHost>
部分-如果它是另一个后备选项,如ScriptAliasMatch结合。(但是没有人这样做)。
- 而且它通常用于
.htaccess
具有许多开源项目的默认规则集。在那里,这只是作为备用广告,并保留“丑陋”的URL作为默认URL。
但是,您通常不希望在自己的.htaccess
文件中使用它。
- 首先,mod_rewrite不会随机脱离。(如果这样做,您将遇到更大的问题)。
- 如果确实被禁用,您的RewriteRules仍然不管用。
- 这是为了防止HTTP
500
错误。它通常完成的工作是使用户遭受HTTP 404
错误的困扰。(没有那么多的用户更友好,如果你考虑一下吧。)
- 实际上,它只是抑制更有用的日志条目或服务器通知邮件。你会毫无收获,为什么你从来没有的RewriteRules工作。
看起来诱人的通用防护措施通常在实践中成为障碍。
RewriteBase
除非需要,否则不要使用
许多复制+粘贴示例都包含一个RewriteBase /
指令。无论如何,这恰好是隐式默认值。因此,您实际上不需要此。这是一种不错的VirtualHost重写方案的解决方法,并且为某些共享托管者误导了DOCUMENT_ROOT路径。
在更深的子目录中使用单个Web应用程序很有意义。在这种情况下,它可以缩短RewriteRule模式。通常,最好在每个目录规则集中使用相对路径说明符。
另请参见RewriteBase如何在.htaccess中工作
MultiViews
虚拟路径重叠时禁用
URL重写主要用于支持虚拟传入路径。通常你只需要一个调度程序脚本(index.php
)或个别几个处理程序(articles.php
,blog.php
,wiki.php
,...)。后者可能与类似的虚拟RewriteRule路径发生冲突。
一种用于请求/article/123
例如可以映射到article.php
与/123
PATH_INFO隐含。您要么必须使用commonplace RewriteCond
!-f
+ 来保护自己的规则,然后!-d
/或者禁用PATH_INFO支持,或者只是禁用Options -MultiViews
。
这并不是说您总是必须这样做。内容协商只是虚拟资源的自动化。
订购很重要
如果尚未,请参阅有关mod_rewrite的所有知识。组合多个RewriteRules通常会导致交互。这并不是每个[L]
标志习惯性地防止的东西,而是您一旦熟悉就会采用的方案。您可以重新编写从一条规则到另一条规则的虚拟路径,直到它到达实际的目标处理程序为止。
不过你会经常想拥有最具体的规则(固定字符串/forum/…
模式,或更严格的占位符[^/.]+
的)早期规则。泛泛的所有规则(.*
)最好留给后面的规则。(例外是将RewriteCond -f/-d
守卫作为主要障碍。)
样式表和图像停止工作
引入虚拟目录结构时,/blog/article/123
这会影响HTML中的相对资源引用(例如<img src=mouse.png>
)。可以通过以下方法解决:
- 仅使用服务器绝对引用
href="https://stackoverflow.com/old.html"
或src="/logo.png"
- 通常,只需将其添加
<base href="https://stackoverflow.com/index">
到HTML <head>
部分即可。这隐式地将相对引用重新绑定到以前的内容。
您也可以制作其他RewriteRules来重新绑定.css
或.png
将其复制到原始位置。但这都是不必要的,否则会导致额外的重定向并妨碍缓存。
另请参阅:CSS,JS和图像无法以漂亮的网址显示
RewriteConds仅掩盖一个RewriteRule
一个常见的误插是RewriteCond会阻止多个RewriteRules(因为它们在视觉上排列在一起):
RewriteCond %{SERVER_NAME} localhost
RewriteRule ^secret admin/tools.php
RewriteRule ^hidden sqladmin.cgi
默认情况下不是这样。您可以使用该标志链接它们[S=2]
。否则,您将不得不重复它们。尽管有时您可以制定“反向”主要规则,以便尽早[END]进行重写处理。
QUERY_STRING免于RewriteRules
您无法匹配RewriteRule index.php\?x=y
,因为默认情况下mod_rewrite仅与相对路径进行比较。您可以通过以下方式分别匹配它们:
RewriteCond %{QUERY_STRING} \b(?:param)=([^&]+)(?:&|$)
RewriteRule ^add/(.+)$ add/%1/$1 # ←──﹪₁──┘
另请参阅如何将查询字符串变量与mod_rewrite匹配?
.htaccess
与 <VirtualHost>
如果在每个目录的配置文件中使用RewriteRules,则担心正则表达式的性能毫无意义。Apache使用通用路由框架将已编译的PCRE模式保留的时间比PHP流程更长。对于高流量站点,一旦经过严格测试,您应该考虑将规则集移入vhost服务器配置。
在这种情况下,最好使用可选的^/?
目录分隔符前缀。这允许在PerDir和服务器配置文件之间自由移动RewriteRules。
每当某事不起作用
不用。
比较access.log
并error.log
通常,仅查看您error.log
和您就可以弄清楚RewriteRule的行为方式access.log
。关联访问时间,以查看最初进入哪个请求路径以及Apache无法解析的路径/文件(错误404/500)。
这并不能告诉您哪个RewriteRule是罪魁祸首。但是,无法访问的最终路径/docroot/21-.itle?index.php
可能会放弃进一步检查的地方。否则,请禁用规则,直到获得一些可预测的路径。
启用重写日志
请参阅Apache RewriteLog文档。要进行调试,您可以在vhost部分中启用它:
# Apache 2.2
RewriteLogLevel 5
RewriteLog /tmp/rewrite.log
# Apache 2.4
LogLevel alert rewrite:trace5
#ErrorLog /tmp/rewrite.log
得出有关每个规则如何修改传入请求路径的详细摘要:
[..] applying pattern '^test_.*$' to uri 'index.php'
[..] strip per-dir prefix: /srv/www/vhosts/hc-profi/index.php -> index.php
[..] applying pattern '^index\.php$' to uri 'index.php'
这有助于缩小过于通用的规则和正则表达式的不幸。
另请参阅:
· .htaccess不起作用(mod_rewrite)
· 调试.htaccess重写规则的提示
在问自己的问题之前
您可能知道,Stack Overflow非常适合在mod_rewrite上提问。
通过包括先前的研究和尝试(避免重复的答案)使它们成为主题,展示基本知识正则表达式 理解,以及:
- 包括输入URL的完整示例,错误重写的目标路径以及您的真实目录结构。
- 完整的RewriteRule集,但也可以选择假定的缺陷集。
- Apache和PHP版本,操作系统类型,文件系统,DOCUMENT_ROOT和PHP
$_SERVER
环境,如果这与参数不匹配有关。
- 摘录自
access.log
和,error.log
以验证现有规则解决的问题。更好的是,rewrite.log
总结。
这样可以更快更准确地得出答案,并使它们对其他人更有用。
评论你的 .htaccess
如果您是从某处复制示例,请小心添加# comment and origin link
。尽管忽略归因只是一种不好的举止,但它通常确实会在以后损害维护。记录任何代码或教程源。尤其是当您不熟悉它时,您应该对不将它们像魔术黑匣子那样对待更加感兴趣。
不是“ SEO” -URL
免责声明:只是一个宠物。您经常听到漂亮的URL重写方案,称为“ SEO”链接或其他内容。尽管这对于谷歌搜索示例很有用,但它已过时。
没有现代的搜索引擎真正受到路径段.html
和.php
路径段或?id=123
查询字符串的干扰。诸如AltaVista之类的旧搜索引擎的确避免了对具有潜在歧义访问路径的网站进行爬网。现代的爬虫通常甚至渴望获得深层的Web资源。
概念上应使用的“漂亮” URL是使网站变得用户友好。
- 具有可读且明显的资源方案。
- 确保URL是长期有效的(又称为永久链接)。
- 通过提供可发现性
/common/tree/nesting
。
但是,不要牺牲对顺从性的独特要求。