作为 Write for DOnations 计划的一部分,作者选择了 Diversity in Tech Fund 来接受捐赠。
介绍
Apache 的 mod_rewrite
模块让您可以更干净地重写 URL,将人类可读的路径转换为代码友好的查询字符串。 它还使您能够根据条件重写 URL。
.htaccess
文件允许您创建和应用重写规则,而无需访问服务器配置文件。 通过将 .htaccess
文件放在您网站的根目录中,您可以在每个站点或每个目录的基础上管理重写。
在本教程中,您将启用 mod_rewrite
并使用 .htaccess
文件来创建基本的 URL 重定向,然后探索几个高级用例。
先决条件
要遵循本教程,您将需要:
- 按照Ubuntu 20.04初始服务器设置指南设置一个Ubuntu 20.04服务器,包括sudo非root用户和防火墙。
- 按照 如何在 Ubuntu 20.04 上安装 Linux、Apache、MySQL、PHP (LAMP) 堆栈的第 1 步安装 Apache。
第 1 步 - 启用 mod_rewrite
为了让 Apache 理解重写规则,我们首先需要激活 mod_rewrite
。 它已经安装,但在默认的 Apache 安装中被禁用。 使用 a2enmod
命令启用模块:
sudo a2enmod rewrite
这将激活模块或提醒您该模块已启用。 要使这些更改生效,请重新启动 Apache。
sudo systemctl restart apache2
mod_rewrite
现在已完全启用。 在下一步中,我们将设置一个 .htaccess
文件,我们将使用它来定义重定向的重写规则。
第 2 步 — 设置 .htaccess
.htaccess
文件允许我们在不访问服务器配置文件的情况下修改我们的重写规则。 因此,.htaccess
对您的 Web 应用程序的安全性至关重要。 文件名前面的句点确保文件被隐藏。
注意: 可以放入.htaccess
文件的任何规则也可以直接放入服务器配置文件中。 事实上, Apache 官方文档 建议使用服务器配置文件而不是 .htaccess
,因为 Apache 以这种方式处理它的速度更快。
但是,在这个简单的示例中,性能提升可以忽略不计。 此外,在.htaccess
中设置规则很方便,尤其是在同一台服务器上有多个网站时。 它不需要重新启动服务器即可使更改生效,并且不需要 root 权限来编辑这些规则,从而简化了维护并可以使用非特权帐户进行更改。 一些流行的开源软件,如 WordPress 和 Joomla,通常依赖 .htaccess
文件让软件根据需要修改和创建附加规则。
在开始使用 .htaccess
文件之前,您需要设置和保护更多设置。
默认情况下,Apache 禁止使用 .htaccess
文件来应用重写规则,因此首先您需要允许对文件进行更改。 使用 nano
或您喜欢的文本编辑器打开默认的 Apache 配置文件。
sudo nano /etc/apache2/sites-available/000-default.conf
在该文件中,您会发现从第一行开始的 <VirtualHost *:80>
块。 在该块内,添加以下新块,使您的配置文件如下所示。 确保所有块都正确缩进。
/etc/apache2/sites-available/000-default.conf
<VirtualHost *:80> <Directory /var/www/html> Options Indexes FollowSymLinks AllowOverride All Require all granted </Directory> . . . </VirtualHost>
保存并关闭文件。 要使这些更改生效,请重新启动 Apache。
sudo systemctl restart apache2
现在,在 webroot 中创建一个 .htaccess
文件。
sudo nano /var/www/html/.htaccess
在新文件的顶部添加这一行以激活重写引擎。
/var/www/html/.htaccess
RewriteEngine on
保存文件并退出。
我们现在有一个可操作的 .htaccess
文件,我们可以使用它来管理我们的 Web 应用程序的路由规则。 在下一步中,我们将创建用于演示重写规则的示例网站文件。
第 3 步 — 配置 URL 重写
在这里,我们将设置一个基本的 URL 重写,将漂亮的 URL 转换为页面的实际路径。 具体来说,我们将允许用户访问 http://your_server_ip/about
,但显示一个名为 about.html
的页面。
首先在 webroot 中创建一个名为 about.html
的文件。
sudo nano /var/www/html/about.html
将以下 HTML 代码复制到文件中,然后保存并关闭它。
/var/www/html/about.html
<html> <head> <title>About Us</title> </head> <body> <h1>About Us</h1> </body> </html>
您可以通过 http://your_server_ip/about.html
访问此页面,但请注意,如果您尝试访问 http://your_server_ip/about
,您将看到 404 Not Found 错误。 要改为使用 /about
访问页面,我们将创建一个重写规则。
所有 RewriteRules
都遵循以下格式:
通用 RewriteRule 结构
RewriteRule pattern substitution [flags]
RewriteRule
指定指令。pattern
是一个 正则表达式 ,它匹配来自 URL 的所需字符串,这是查看器在浏览器中键入的内容。substitution
是实际 URL 的路径,即文件 Apache 服务器的路径。flags
是可选参数,可以修改规则的工作方式。
让我们创建我们的 URL 重写规则。 打开 .htaccess
文件。
sudo nano /var/www/html/.htaccess
在第一行之后,添加突出显示的 RewriteRule
并保存文件。
/var/www/html/.htaccess
RewriteEngine on RewriteRule ^about$ about.html [NC]
在这种情况下,^about$
是模式,about.html
是替换,[NC]
是标志。 我们的示例使用了一些具有特殊含义的字符:
^
表示your_server_ip/
之后的 URL 的开始。$
表示 URL 的结束。about
匹配字符串“about”。about.html
是用户访问的实际文件。[NC]
是使规则不区分大小写的标志。
您现在可以在浏览器中访问 http://your_server_ip/about
。 事实上,使用上面显示的规则,以下 URL 将指向 about.html
:
http://your_server_ip/about
,因为规则定义。http://your_server_ip/About
,因为规则不区分大小写。http://your_server_ip/about.html
,因为原始正确的文件名将始终有效。
但是,以下内容将不起作用:
http://your_server_ip/about/
,因为规则明确指出about
之后可能没有任何内容,因为$
字符出现在about
之后。http://your_server_ip/contact
,因为它不会匹配规则中的about
字符串。
您现在拥有一个可操作的 .htaccess
文件,其中包含可以根据需要修改和扩展的基本规则。 在以下部分中,我们将展示常用指令的另外两个示例。
示例 1 - 使用 RewriteRule 简化查询字符串
Web 应用程序经常使用 查询字符串 ,这些字符串在地址后使用问号 (?
) 附加到 URL。 使用 & 符号 (&
) 分隔单独的参数。 查询字符串可用于在各个应用程序页面之间传递附加数据。
例如,用 PHP 编写的搜索结果页面可能使用 http://example.com/results.php?item=shirt&season=summer
之类的 URL。 在此示例中,将两个附加参数传递给虚构的 result.php
应用程序脚本:item
,值为 shirt
,season
值为 summer
。 应用程序可以使用查询字符串信息为访问者构建正确的页面。
Apache 重写规则通常用于将上述长且令人不快的链接简化为 友好的 URL ,这些 URL 更易于输入和视觉解释。 在本例中,我们希望将上述链接简化为 http://example.com/shirt/summer
。 shirt
和 summer
参数值仍在地址中,但没有查询字符串和脚本名称。
这是实现这一点的一个规则:
简单替换
RewriteRule ^shirt/summer$ results.php?item=shirt&season=summer [QSA]
shirt/summer
在请求的地址中显式匹配,并且 Apache 被告知要为 results.php?item=shirt&season=summer
提供服务。
[QSA]
标志通常用于重写规则。 他们告诉 Apache 将任何额外的查询字符串附加到所服务的 URL,因此如果访问者键入 http://example.com/shirt/summer?page=2
,服务器将响应 results.php?item=shirt&season=summer&page=2
。 没有它,额外的查询字符串将被丢弃。
虽然这种方法达到了预期的效果,但项目名称和季节都被硬编码到规则中。 这意味着该规则不适用于任何其他项目,例如 pants
,或季节,例如 winter
。
为了使规则更通用,我们可以使用 正则表达式 来匹配原始地址的部分,并在替换模式中使用这些部分。 修改后的规则将如下所示:
简单替换
RewriteRule ^([A-Za-z0-9]+)/(summer|winter|fall|spring) results.php?item=$1&season=$2 [QSA]
括号中的第一个正则表达式组匹配包含字母数字字符和数字的字符串,例如 shirt
或 pants
,并将匹配的片段保存为 $1
变量。 括号中的第二个正则表达式组完全匹配 summer
、winter
、fall
或 spring
,同样将匹配的片段保存为 [X162X ]。
然后在 item
和 season
变量中的结果 URL 中使用匹配的片段,而不是我们之前使用的硬编码 shirt
和 summer
值。
例如,上面会将 http://example.com/pants/summer
转换为 http://example.com/results.php?item=pants&season=summer
。 这个例子也是未来的证明,允许使用单个规则正确重写多个项目和季节。
示例 2 - 使用 RewriteConds 使用逻辑添加条件
重写规则不一定总是一一评估而没有任何限制。 RewriteCond
指令让我们可以在重写规则中添加条件来控制规则何时被处理。 所有 RewriteConds
都遵循以下格式:
通用 RewriteCond 结构
RewriteCond TestString Condition [Flags]
RewriteCond
指定RewriteCond
指令。TestString
是要测试的字符串。Condition
是要匹配的模式或条件。Flags
是可选参数,可以修改条件和评估规则。
如果 RewriteCond
计算结果为真,则将考虑紧随其后的 RewriteRule
。 如果不是,则该规则将被丢弃。 多个 RewriteCond
可以一个接一个地使用,并且在默认行为下,所有这些都必须评估为 true 才能考虑以下规则。
例如,假设您希望将所有对站点上不存在的文件或目录的请求重定向回主页,而不是显示标准的 404 Not Found 错误页面。 这可以通过以下条件规则来实现:
将所有对不存在的文件和目录的请求重定向到主页
RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule . / [R=301]
有了以上内容:
%{REQUEST_FILENAME}
是要检查的字符串。 在这种情况下,它是请求的文件名,它是可用于每个请求的系统变量。-f
是一个内置条件,用于验证请求的名称是否存在于磁盘上并且是一个文件。!
是一个否定运算符。 结合起来,!-f
仅在指定名称不存在或不是文件时才计算为真。- 类似地,
!-d
仅在指定名称不存在或不是目录时才计算为真。
最后一行的 RewriteRule
仅对不存在的文件或目录的请求生效。 RewriteRule
本身就很简单。 模式中的点 .
匹配任何内容,并且替换将每个请求定向到 /
网站根目录。
此外,[R=301]
标志告诉 Apache 返回 301 Moved Permanently 重定向 HTTP 响应代码到浏览器,导致浏览器知道重定向发生并显式获取网站根目录而不是请求的 URL ,更改会反映在浏览器地址栏上。
如果没有这个标志,Apache 将返回网站根目录,但浏览器仍会认为请求的页面 URL 存在,并会在地址栏上显示最初请求的地址。
结论
mod_rewrite
允许您创建人类可读的 URL。 在本教程中,您使用了 RewriteRule
指令来重定向 URL,包括带有查询字符串的 URL。 您还使用 RewriteCond
指令编写了有条件地重定向 URL。
如果您想了解有关 mod_rewrite
的更多信息,请查看 Apache 的 mod_rewrite Introduction 和 Apache 的 mod_rewrite 官方文档。