page contents

Apache 和 Nginx 下的 URL 重写

URL 重写是将页面映射到本站另一页面, 而重定向则是将页面映射到另一主机(域名). 其中临时重定向(R=302)和永久重定向(R=301)都是亲搜索引擎的, 是 SEO 的重要技术. 通过重定向, 浏览器和搜索引擎都意思到页面发生了变化, 从而分别改变地址栏显示的地址和更新搜索引擎索引.

attachments-2020-04-v3OK0pEb5e8e82925cbd0.jpg

URL 重写和重定向


URL 重写是将页面映射到本站另一页面, 而重定向则是将页面映射到另一主机(域名). 其中临时重定向(R=302)和永久重定向(R=301)都是亲搜索引擎的, 是 SEO 的重要技术. 通过重定向, 浏览器和搜索引擎都意思到页面发生了变化, 从而分别改变地址栏显示的地址和更新搜索引擎索引。


Apache 中的 URL 重写和重定向


启用Apache 的 URL 重写功能, 需要开启mod_rewrite模块.。然后在服务器配置文件或htaccess中修改服务配置:
AllowOverride all
Options FollowSysLinks
服务器配置文件和htaccess文件中都可以配置 URL 重写,前者是服务器级别, 后者是目录级别。

开启重写日志


2.4 以下版本

在Apache 服务器配置文件中追加:
RewriteLog "自定义日志路径"
# 设置日志等级
RewriteLogLevel 9
等级0 为关闭, 9 表示输出最详细信息。


2.4 及其以上版本

在Apache 服务器配置文件中追加:
LogLevel alert rewrite:trace3
其中trace3 可以替换为 trace1 到 trace8, 数值越大输出的信息越详细. 最后输出的日志信息在apache_error.log文件中.
注意: 日志级别越高, 越影响服务器性能. 生产环境下应关闭重写日志.

重写指令


RewriteEngine

RewriteEngine: 是否启用重写引擎. On启用, Off不启用.

RewriteBase

RewriteBase: 设置重写的基准目录. 基准目录的根目录就是网站的根目录.

RewriteCond

RewriteCond: 设置重写条件.
语法: 
RewriteCond TestString CondPattern [flags]

RewriteCond 指令定义一条规则条件。

在一条 RewriteRule 指令前面可能会有一条或多条 RewriteCond 指令,只有 TestString 符合 CondPattern 时规则才被应用于当前URL处理。


1) TestString是一个纯文本的字符串,除了包含普通的字符外,还可以包括下列的可扩展结构:

$N: RewriteRule 后向引用,其中(0 <= N <= 9) . $N引用紧跟在 RewriteCond 后面的 RewriteRule 中模板中的括号中的模板在当前 URL 中匹配的数据。

%N: RewriteCond 后向引用, 其中(0 <= N <= 9). %N引用最后一个 RewriteCond 的模板中的括号中的模板在当前 URL 中匹配的数据。

${mapname:key|default}:RewriteMap 扩展。


2) CondPattern是条件 pattern, 即一个应用于当前实例TestString 的正则表达式, 即 TestString 将会被计算然后与CondPattern 匹配. 作为一个标准的扩展正则式, CondPattern 有以下补充:

可以在模板串前增加一个!前缀,以表示不匹配模板。但并不是所有的 test 都可以加!前缀。

CondPattern中可以使用以下特殊变量:

>CondPattern: 大于, 将 CondPattern 当作一个普通字符串, 将它和 TestString 进行比较, 当T estString 的字符大于CondPattern 为真.=CondPattern: 等于, 将 CondPattern 当作一个普通字符串,将它和TestString进行比较, 当 TestString 与 CondPattern 完全相同时为真. 如果 CondPattern 只是 "" (两个引号紧挨在一起) 此时需 TestString 为空字符串方为真

-d: 是否为目录, 将 TestString 当作一个目录名, 检查它否存在以及是否是一个目录.

-f: 是否是 regular file, 将 TestString 当作一个文件名, 检查它是否存在以及是否是一个 regular 文件.

-s: 是否为长度不为0的 regular文件, 将 TestString 当作一个文件名, 检查它是否存在以及是否是一个长度大于0的 regular 文件.

-l: 是否为 symbolic link, 将 TestString 当作一个文件名, 检查它是否存在以及是否是一个 symbolic link.

-F: 通过 subrequest 来检查某文件是否可访问. 检查 TestString 是否是一个合法的文件, 而且通过服务器范围内的当前设置的访问控制进行访问. 这个检查是通过一个内部 subrequest 完成的, 因此需要小心使用这个功能以降低服务器的性能.

-U: 通过 subrequest 来检查某个 URL 是否存在. 检查 TestString 是否是一个合法的URL, 而且通过服务器范围内的当前设置的访问控制进行访问. 这个检查是通过一个内部 subrequest 完成的, 因此需要小心使用这个功能以降低服务器的性能.


3) [flags]是第三个参数,多个标志之间用逗号分隔。

nocase|NC: 不区分大小写. 在扩展后的 TestString 和 CondPattern 中, 比较时不区分文本的大小写. 注意, 这个标志对文件系统和 subrequest 检查没有影响.

ornext|OR: 建立与下一个条件的或的关系. 默认的情况下,二个条件之间是AND的关系, 用这个标志将关系改为OR. 例如:

RewriteCond %{REMOTE_HOST} ^host1.* [OR] RewriteCond %{REMOTE_HOST} ^host2.* [OR] RewriteCond %{REMOTE_HOST} ^host3.* RewriteRule …

如果没有[OR]标志,需要写三个条件/规则。


RewriteRule

RewriteRule: 设置重写规则.

语法
RewriteRule Pattern Substitution [flags].

1) Pattern是一个作用于当前 URL 的兼容 perl 的正则表达式. 这里的“当前”是指该规则生效时的 URL 的值。

2) Substitution是当原始 URL 与 Pattern 相匹配时, 用以替代(或替换)的字符串. (RewriteRule 中也会进行一次判断, 被捕获的 URL 是否与 Pattern相匹配)

3) Substitution还可以追加特殊标记[flags]作为 RewriteRule 指令的第三个参数。 Flags 是一个包含以逗号分隔的下列标记的列表:


1.redirect|R[=code] (强制重定向 redirect)
强制性执行一个外部重定向. 如果code没有指定, 则默认302. 如果需要使用在 300-400 范围内的其他响应代码, 只需在此指定这个数值即可。

另外, 还可以使用下列符号名称之一: temp(默认的), permanent, seeother.

注意: 在使用这个标记时,务必确保该替换字段是一个有效的 URL ! 并且要记住,如果希望停止重写操作而立即重定向,则还需要使用L标记。


2.forbidden|F(强制 URL 为被禁止的 forbidden )

强制当前URL 为被禁止的, 立即反馈一个 403. 使用这个标记, 可以链接若干 RewriteConds 以有条件地阻塞某些 URL。


3.gone|G(强制 URL 为已废弃的 gone )

强制当前URL 为已废弃的, 立即反馈一个 410. 使用这个标记, 可以标明页面已经被废弃而不存在了。


4.proxy|P(强制为代理 proxy )
此标记使替换成分被内部强制为代理请求, 并立即(重写规则处理立即中断)把处理移交给代理模块. 你必须确保此替换串是一个有效的, 能够为 Apache 代理模块所处理的 URI. 使用这个标记, 可以把某些远程成分映射到本地服务器名称空间, 从而增强了 ProxyPass 指令的功能。
注意: 要使用这个功能, 代理模块必须编译在Apache服务器中. 不能确定, 可以检查httpd -l的输出中是否有mod_proxy.c. 如果有, 则mod_rewrite可以使用这个功能; 如果没有, 则必须启用mod_proxy并重新编译httpd程序.


5.last|L(最后一个规则 last)
立即停止重写操作, 并不再应用其他重写规则. 它对应于 Perl中 的last命令或C语言中的break命令. 这个标记可以阻止当前已被重写的 URL 为其后继的规则所重写.


6.next|N(重新执行 next round)
重新执行重写操作(从第一个规则重新开始). 这时再次进行处理的URL已经不是原始的URL, 而是经最后一个重写规则处理的URL. 它对应于 Perl 中的next命令或C语言中的continue命令. 此标记可以重新开始重写操作, 即立即回到循环的头部.


7.chain|C(与下一个规则相链接 chained)
此标记使当前规则与下一个(其本身又可以与其后继规则相链接的, 并可以如此反复的)规则相链接. 它产生这样一个效果: 如果一个规则被匹配, 通常会继续处理其后继规则, 即这个标记不起作用; 如果规则不能被匹配, 则其后继的链接的规则会被忽略. 比如, 在执行一个外部重定向时, 对一个目录级规则集, 你可能需要删除".www" (此处不应该出现".www"的).


8.type|T=MIME-type(强制MIME类型 type)
强制目标文件的MIME 类型为 MIME-type. 比如, 它可以用于模拟 mod_alias 中的 ScriptAlias 指令, 在内部强制被映射目录中的所有文件的 MIME 类型为"application/x-httpd-cgi".


9.nosubreq|NS(仅用于不对内部子请求进行处理 no internal sub-request)
在当前请求是一个内部子请求时, 此标记强制重写引擎跳过该重写规则. 比如, 在 mod_include 试图搜索可能的目录默认文件(http://index.xxx)时, Apache 会在内部产生子请求. 对子请求, 它不一定有用的, 而且如果整个规则集都起作用, 它甚至可能会引发错误. 所以, 可以用这个标记来排除某些规则.


10.nocase|NC(忽略大小写 no case)
它使Pattern 忽略大小写。


11.qsappend|QSA(追加请求串 query string append)

此标记强制重写引擎在已有的替换串中追加一个请求串, 而不是简单的替换. 如果需要通过重写规则在请求串中增加信息, 就可以使用这个标记。


12.noescape|NE(在输出中不对URI作转义 no URI escaping)

此标记阻止mod_rewrite 对重写结果应用常规的 URI 转义规则. 一般情况下, 特殊字符(如%, $, ;等)会被转义为等值的十六进制编码. 此标记可以阻止这样的转义, 允许百分号等符号出现在输出中,如:
RewriteRule /foo/(.*) /bar?arg=P1=$1 [R,NE] 可以使’/foo/zed’转向到一个安全的请求’/bar?arg=P1=zed’.


13.passthrough|PT(移交给下一个处理器 pass through)

让mod_rewrite 模块将重写的 URL 传回给 Apache 做进一步处理。


14.skip|S=num(跳过后继的规则 skip)

如果满足某指定的条件,则跳过后面第num 调规则。


15.env|E=variable:value(设置环境变量 environment variable)

此标记使环境变量variable 的值为 value, VAL可以包含可扩展的反向引用的正则表达式$N和%N. 此标记可以多次使用以设置多个变量。这些变量可以在后继的 RewriteCond 指令的pattern 中通过%{ENV:VAR}作引用 。


16.cookie|CO=NAME:VAL:domain[:lifetime[:path]](设置cookie)

在客户端浏览器上设置一个cookie. cookie 的名称是 NAME, 值是 VAL. domain字段是该 cookie 的域, 比如'.apache.org', 可选的 lifetime 是 cookie 生命期的分钟数, 可选的 path 是 cookie 的路径。

attachments-2020-04-5a3z3CG05e8e82e678784.jpg

在.htaccess 文件中配置重写规则


在.htaccess文件中使用重写功能时, RewriteRule 负责匹配的 URI 是相对.htaccess所在的目录而言的。
例如访问http://example.com/subdir1/subdir2/subdir3:
  • 如果.htaccess在网站根目录下, 那么RewriteRule捕获的 URI 是subdir1/subdir2/subdir3.
  • 如果.htaccess在 subdir1 目录下, RewriteRule捕获的 URI 是subdir2/subdir3.

RewriteRule重写 URI 后的基准目录也是以.htaccess所在的目录为准. 例如: 访问 http://example.com/foo

RewriteRule ^foo$ bar.php [L]
如果.htaccess在根目录下, 重写后访问 http://example.com/bar.php. 如果在 subdir1 目录下, 重写后访问 http://example.com/subdir1/bar.php.

例1:

<IfModule mod_rewrite.c># 启用rewrite引擎RewriteEngine On# 重写规则: 匹配任意以htm后缀的文件, 将htm替换成php. ^(.*)\.htm$ 是一个正则表达式, 表示需要重写的部分, 此处指以任意字符开头, 以.htm结尾的部分. $1.php 是一个重写规则, $1 表示匹配到正则表达式中第一个子模式的字符串. [NC]: 表示重写规则如何应用, 该处表示不区分大小写. 整条规则即重写以任意字符开头, 以.htm结尾的部分, 重写为由匹配到的第一个子模式字符串和.php拼接成的字符串。RewriteRule ^(.*)\.htm$ $1.php [NC]</IfModule>

IfModule 命令用于判断是否安装了 mod_rewrite 模块。


例2:
<IfModule mod_rewrite.c>RewriteEngine On# 设置目录级重写的基准URIRewriteBase /subdir1/RewriteRule ^(.*)\.htm$ $1.php [NC,L,R]</IfModule>
  • RewriteBase设置了重写的基准目录. 如果上例中.htaccess位于网站根目录下, 访问的 http://example.com/foo.htm, 原本重写后的基准目录是网站根目录/, 设置了RewriteBase后变为/subdir1/, 重写后实际访问 http://example.com/subdir1/foo.php.
  • 规则标志L: 表示如果可以匹配本条规则, 则不再继续往下匹配.
  • 规则标志R: 表示临时重定向, 即 302, 相当于[R=302]。


例3:
<IfModule mod_rewrite.c><IfModule mod_rewrite.c>RewriteEngine OnRewriteCond %{HTTP_HOST} (localhost:81)RewriteRule ^(.*) http://localhost:82 [R=301]</IfModule>
  • [R=301]: 表示永久性重定向, 用户发送的所有请求都会被发送到新域名处理,永久重定向到另一个域名使用了绝对 URI, 因此不用再设置 RewriteBase。
  • %{HTTP_HOST}:表示引用 Apache 服务器变量 HTTP_HOST。
  • 该条RewriteCond 表示判断 HTTP_HOST 是否是 localhost:81。

在httpd.conf 中配置重写规则

在服务器配置文件中配置URL 重写的指令与 .htaccess 文件中的格式相同,需要写在<Directory>内.


Nginx

Nginx 的 URL 重写功能不需要额外的支持. Nginx 本身只支持在服务器配置文件中配置 URL 重写规则, 不支持 .htaccess 文件. 但可以通过在服务器配置文件中引入外部文件的方式, 实现对 .htaccess 文件的支持. 这会严重影响 Nginx 的性能。


重写指令


rewrite

根据重写规则, 重写 URL。
语法:
 rewrite regex replacement flag
应用环境: server, location, if
可应用的flag 标记包括:
  • last: 相当于 Apache 中的L标记。
  • break: 终止匹配, 不再匹配后面的规则。
  • redirect: 临时重定向, 返回 302。
  • permanent: 永久性重定向, 返回 301。
示例:
rewrite ^(.*)\.html$ $1.php last;
注意: 如果访问的是 http://localhost/dir/file, rewrite 中的正则表达式匹配的是 /dir/file, 包括最左边的正斜杠.


if 指令


语法: 
if (condition){...}
注意if之后紧跟的空格不能省略。
应用环境: server, location
判断一个条件是否符合, 如果符合, 则执行大括号内语句. if 指令不支持嵌套, 不支持&&和||操作。

condition中可以包含如下判断表示:

  • ~: 区分大小写进行匹配
  • ~*: 不区分大小写进行匹配
  • !~: 区分大小写进行非匹配
  • !~*: 不区分大小写进行非匹配
  • -f: 如果文件存在
  • !-f: 如果文件不存在
  • -d: 如果目录存在
  • !-d: 如果目录不存在
  • -e: 如果文件或目录存
  • !-e: 如果文件或目录不存在
  • -x: 如果文件可执行
  • !-x: 如果文件不可执行
示例:
location ~ \.html$ {if (!-f $request_filename){rewrite (.*)\.html$ $1.php last;}}
访问的URL 以.html结尾时, 判断是否存在该文件, 如果不存在就重写为访问同名的 PHP 脚本。


return 指令


语法:
 return code复制代码
应用环境: server, location, if
该指令用于结束规则的执行并返回状态码给客户端。
例如:
return 403;


location 指令


语法: 
location [flags] regex
应用环境: server

flags 包括:

  • ~: 执行一个正则匹配, 区分大小写
  • ~*: 执行一个正则匹配, 不区分大小写
  • ^~: 表示普通字符匹配. 使用前缀匹配成功. 如果匹配成功, 则不再匹配其他 location.
  • =: 进行普通字符完全匹配.
优先级:
  • =的优先级最高. 匹配成功后, 不再匹配其他项.
  • ^~类型表达式. 匹配成功后, 不再匹配其他项.
  • ~和~*. 如果有多个 location 的正则匹配.
  • 常规字符串匹配类型, 按前缀匹配.


rewrite_log 指令


语法: 
rewrite_log on|off
应用环境: server, location, if
启用时, 将在error_log中记录 notice 级别的重写日志.


set 指令


语法:
set variable value
应用环境: server, location, if
为给定的变量设置一个特定值。


Nginx 内置的全局变量


$args: 这个变量等于请求行中的参数, 同 $query_string.
  • $content_length: 请求头中的 Content-length 字段.
  • $content_type: 请求头中的 Content-Type 字段.
  • $document_root: 当前请求在 root 指令中指定的值.
  • $host: 请求主机头字段,否则为服务器名称.
  • $http_user_agent: 客户端 agent 信息.
  • $http_cookie: 客户端 cookie 信息.
  • $limit_rate: 这个变量可以限制连接速率.
  • $request_method: 客户端请求的动作,通常为 GET 或 POST.
  • $remote_addr: 客户端的 IP 地址.
  • $remote_port: 客户端的端口.
  • $remote_user: 已经经过 Auth Basic Module 验证的用户名.
  • $request_filename: 当前请求的文件路径, 由 root 或 alias 指令与 URI 请求生成.
  • $scheme: HTTP 方法(如 http, https).
  • $server_protocol: 请求使用的协议, 通常是HTTP/1.0 或 HTTP/1.1.
  • $server_addr: 服务器地址, 在完成一次系统调用后可以确定这个值.
  • $server_name: 服务器名称.
  • $server_port: 请求到达服务器的端口号.
  • $request_uri: 包含请求参数的原始 URI, 不包含主机名, 如: ”/foo/bar.php?arg=baz”.
  • $uri: 不带请求参数的当前 URI, $uri 不包含主机名, 如 "/foo/bar.html".
  • $document_uri: 与$uri相同.


1


  • 发表于 2020-04-09 09:29
  • 阅读 ( 451 )

你可能感兴趣的文章

相关问题

0 条评论

请先 登录 后评论
Pack
Pack

1135 篇文章

作家榜 »

  1. 轩辕小不懂 2403 文章
  2. 小柒 1474 文章
  3. Pack 1135 文章
  4. Nen 576 文章
  5. 王昭君 209 文章
  6. 文双 71 文章
  7. 小威 64 文章
  8. Cara 36 文章