URL 重写¶
- 目录
- URL 重写
模块:mod_rewrite
描述¶
内部重定向,URL 重写
注意:正则表达式匹配是针对完整的 REQUEST_URI 进行的,包括可选的 path-info 和查询字符串。
重写后的 URL 路径结果必须以 '/' 开头(自 1.4.50 版本起强制执行)
选项¶
url.rewrite-once
¶
在 Web 服务器处理之前,内部重写一组 URL。
url.rewrite-once = ( "<regex>" => "<relative-uri>" )
或对于多个规则
url.rewrite-once = ( "<regex1>" => "<relative-uri1>", "<regex2>" => "<relative-uri2>" )
url.rewrite-repeat
¶
在 Web 服务器处理之前,内部重写一组 URL
url.rewrite-repeat = ( "<regex>" => "<relative-uri>" )
这些选项之间的区别在于,
url.rewrite-repeat
允许连续应用多个(单独定义的)重写规则,而 url.rewrite-once
则会在表达式匹配成功时跳过后续的重写规则。因此,url.rewrite-once
的行为类似于 Apache 的 RewriteRule ... [L]:https://httpd.apache.ac.cn/docs/2.2/mod/mod_rewrite.html#rewriterule
url.rewrite-[repeat-]if-not-file
¶
在 Web 服务器处理之前,内部重写一组 URL,**除非**目标存在且是一个常规文件(不是目录、管道、套接字或其他)。这类似于 Apache 的 !-f
RewriteRule。
注意:所有 URL 末尾附加了 path-info 的 URL 都将被视为文件不存在。这在框架中很有用,Lighttpd 应该提供静态文件(存在且是文件的),而其他所有内容都应该重写以使用动态后端,例如 Drupal 或其他 /index.php
url.rewrite-if-not-file
是许多框架的简单解决方案
例如,如果文件存在,则让 Lighttpd 提供静态文件(如 .js、.css、.jpg 等),否则重写以将请求发送到 /index.php
url.rewrite-if-not-file = ( "" => "/index.php?path=${url.path}${qsa}" )
# 选项,如果 index.php 期望在查询字符串 'path=...' 中获取原始 URLurl.rewrite-if-not-file = ( "" => "/index.php${url.path}${qsa}" )
# 备选项,如果 index.php 期望在 PATH_INFO 中获取原始 URL
(作为 mod_rewrite 的替代方案,可以使用 mod_magnet Lua 代码来完全实现 Apache 的 -f 和 -d 解决方案。)
正则表达式¶
- 模式(“通配符”)与字符串进行匹配
- 特殊字符(参考 [https://regexper.cn/reference.html])
- . (句点) - 匹配任何字符
- * (星号) - 匹配前一个符号零次或多次
- + (加号) - 匹配前一个符号一次或多次
- ? (问号) - 匹配前一个符号零次或一次
- \? (反斜杠-某个字符) - 匹配特殊字符
- ^ (脱字号) - 匹配字符串的开头
- $ (美元符号) - 匹配字符串的结尾
- [set] - 匹配方括号内集合中的任一符号。
- [^set] - 匹配方括号内集合中**不**包含的任何符号。
- (pattern) - 分组,记住模式匹配到的内容作为一个特殊变量
- {n,m} - 匹配前一个字符 n 到 m 次(m 可以省略,表示 >=n 次)
- (?!expression) - 匹配当前位置的任何内容,**但不是**表达式。示例:
"^(/(?!(favicon.ico$|js/|images/)).*)" => "/fcgi/$1"
- 普通的字母数字字符被视为普通字符
替换模式¶
如果匹配的正则表达式包含括号中的组,替换模式中的 $1..$9 指的是捕获到的文本,其中
匹配组 "$1" 表示第一个组,"$2" 表示第二个,依此类推。
请注意,`url.rewrite-*` 目标中的 % 替换(如 %1、%2、%0 等)是允许的,但它们的含义**与** `evhost.path-pattern` 中的含义**不同**。如果 `url.rewrite-*` 在正则表达式条件中指定,% 模式将替换为条件正则表达式中对应的组。%1 替换为第一个子表达式,%2 替换为第二个,依此类推。%0 替换为匹配正则表达式的整个子字符串。请参阅下面使用“%0”的示例。
扩展替换模式¶
Lighttpd 提供了在花括号 %{...} 或 ${...} 中编码重定向和重写反向引用替换的方法(自 1.4.50 版本起)
最多保存九 (9) 个匹配项,用于 %{1} - %{9}。最多保存十九 (19) 个匹配项,用于 ${1} - ${19}。
除了 %1 和 $1 之外,现在还支持以下修饰符,后跟反向引用的数字,例如 ${esc:1}。
- ${noesc:...} 无转义
- ${esc:...} 转义所有非字母数字字符 - . _ ~ 包括双重转义 %
- ${escape:...} 转义所有非字母数字字符 - . _ ~ 包括双重转义 %
- ${escnde:...} 转义所有非字母数字字符 - . _ ~ 但不双重转义 %
- ${escpsnde:...} 转义所有非字母数字字符 - . _ ~ / 但不双重转义 %(保留字面上的 /)
- ${tolower:...} 转为小写
- ${toupper:...} 转为大写
- ${encb64u:...} 编码为 base64url 字符(无填充)
- ${decb64u:...} 从 base64url 字符解码
- %{noesc:...} 无转义
- %{esc:...} 转义所有非字母数字字符 - . _ ~ 包括双重转义 %
- %{escape:...} 转义所有非字母数字字符 - . _ ~ 包括双重转义 %
- %{escnde:...} 转义所有非字母数字字符 - . _ ~ 但不双重转义 %
- %{escpsnde:...} 转义所有非字母数字字符 - . _ ~ / 但不双重转义 %(保留字面上的 /)
- %{tolower:...} 转为小写
- %{toupper:...} 转为大写
- %{encb64u:...} 编码为 base64url 字符(无填充)
- %{decb64u:...} 从 base64url 字符解码
Lighttpd 提供了一些预定义的替换模式,用于 URI 部分,无需正则表达式匹配(自 1.4.50 版本起)
- ${url.scheme} 例如 https
- ${url.authority} 例如 example.com
- ${url.port} 例如 443
- ${url.path} 例如 /robots.txt
- ${url.query} 例如 name1=value1&name2=value2
- ${qsa} 会智能地转换为
?${url.query}
或&${url.query}
或空字符串,具体取决于源或目标 URL 中的查询字符串是否为空
替换模式可以与编码修饰符结合使用,例如 ${tolower:url.authority}。
示例¶
注意:正则表达式匹配是针对完整的 REQUEST_URI 进行的,包括可选的 path-info 和查询字符串。
# The following contrived example, is just simulating vhost by rewrite # (Note: you can not change document-root with mod_rewrite) # Use mod_simple_vhost, mod_evhost, or mod_vhostdb instead to make real mass-vhost # Since lighttpd 1.4.50 server.document-root = "/www/htdocs/" url.rewrite-once = ( "" => "/${url.authority}/$0" ) # The "" on the left side of => is special for mod_rewrite and always matches the full url, # and $0 contains the full url. # Before lighttpd 1.4.50 server.document-root = "/www/htdocs/" $HTTP["host"] =~ "^.*\.([^.]+\.com)$" { url.rewrite-once = ( "^/(.*)" => "/%0/$1" ) } # request: http://any.domain.com/url/ # before rewrite: REQUEST_URI="/www/htdocs/url/" # and DOCUMENT_ROOT="/www/htdocs/" %0="any.domain.com" $1="url/" # after rewrite: REQUEST_URI="/www/htdocs/any.domain.com/url/" # still, you have DOCUMENT_ROOT=/www/htdocs/ # please note, that we have two regular expressions: the one which # $HTTP["host"] is been compared with, and the one of the rewrite rule. # the numbered subexpressions available to build the relative uri are # being prefixed by '%' for subexpressions of the first regular expression # match and by '$' for subexpressions of the second one. # subexpression 0 interpolates the whole matching string: %0 for the whole # string matching the conditional, and $0 for the whole string matching the # rewrite rule. # if the rewrite rule is not included in a conditional # block, only the '$' prefixed variables are available. url.rewrite-once = ( "^/id/([0-9]+)$" => "/index.php?id=$1", "^/link/([a-zA-Z]+)" => "/index.php?link=$1" )
与 mod_redirect 配合使用¶
重写规则始终在重定向规则之前执行。无论模块加载顺序或配置中规则的顺序如何,这都成立(Lighttpd v1.4.13)。但是,mod_rewrite 提供了一种机制,可以使 URL 不被修改地通过:将 "$0" 指定为规则目标,但请确保规则匹配整个字符串,因为 $0 是整个匹配的字符串。
url.rewrite-once = ( "^/foo" => "$0", "^/(.*)" => "/handler/$1" ) url.redirect = ( "^/foo" => "http://foo.bar/" )
自版本 1.4.40 起,另一种替代方法是为重写规则指定一个空目标。这将使匹配的规则保持 URL 未修改,并跳过任何后续的重写规则。
url.rewrite-once = ( "^/foo" => "", # instead of (nonsensical) blank url, the url will not be modified "^/(.*)" => "/handler/$1" )
Windows 上“文件名过长”的解决方法¶
在 Windows 上运行 Lighttpd 时,如果计算出的文件名长度超过 255 个字符,您可能会收到 500 Internal Server Error
。
在错误日志中,它将显示为 (response.c.537) file not found ... or so: File name too long /very_looooong_path ->
。
作为变通方法,您可以使用 mod_rewrite
来避免此错误。
server.modules += ("mod_rewrite") url.rewrite-once = ( ".{250,}" => "/toolong.php" )
如果错误处理器是 PHP,$_SERVER['REQUEST_URI']
将包含完整的 URI。
传递/匹配查询字符串(GET 变量)¶
如果您想将查询字符串(?foo=bar
)传递到重写目标,您可以在 Lighttpd 的现代版本中使用 ${qsa}
("qsa" 是 "query-string-append" 的缩写)url.rewrite-once = ( "^/news/([^\?]+)" => "/news.php?title=$1${qsa}" )
对于 Lighttpd 的旧版本,您可能需要明确匹配查询字符串url.rewrite-once = ( "^/news/([^\?]+)(?:\?(.*))?" => "/news.php?title=$1&$2" )