本文完整阅读约需 7 分钟,如时间较长请考虑收藏后慢慢阅读~

在部署ThinkPHP业务时,我们经常需要将Web服务器根目录指向ThinkPHP源码的public目录,因为该目录内有ThinkPHP的启动脚本,即index.php。但在某些情况下(虚拟主机或Docker甚至Kubernetes等环境下),我们无法直接修改配置文件来将根目录切换到public
但如果这个时候刚好使用Apache作为Web服务器,就可以利用其Rewrite配置来轻松解决这一问题。

0x01

上面已经介绍过,我们需要在不修改配置文件的情况下实现让根目录指向public目录,这就需要使用Apache的一个杀手锏:动态配置。

如果下载过PHP源码(或其他语言源码)的读者,应该对.htaccess文件不陌生,这就是Apache的动态配置文件,即Hypertext Access,超文本入口。

该文件描述了该目录(包含子目录,除非子目录中有另外的显式配置)下的访问规则,通常包含以下配置:
– 声明错误页
– 重定向(301、302等)
– URL重写
– 密码保护(使用HTTP认证)
– 通过IP地址禁用访客
– 通过来源禁用访客(即反盗链)
– 禁止爬虫(通过UserAgent判断)
– 首页文件
– 新增/修改MIME类别
– 启用服务端内嵌脚本语言(即SSI)
– 启用CGI支持
– 开启/关闭列出目录功能
– 设置服务器语言/时区
– 避免访问框架、密匙等隐藏文件
– 避免静态目录中出现脚本执行漏洞
– 区分媒体文件的下载与播放
– 设置输出编码
– 屏蔽包含特殊字符的请求,增强安全性
……

我们在这里需要使用的就是URL重写这一功能。

0x02

首先我们需要在源码根目录下放置一个.htaccess文件,设置该文件的拥有者为httpd(根据你环境的Apache进程所属用户不同,可能有所变化):

touch ./.htaccess
chown -R httpd:httpd ./.htaccess

然后使用你喜欢的编辑器打开该文件,在其中写入如下配置:

<IfModule mod_rewrite.c>
    RewriteEngine on
    RewriteBase /
    RewriteCond %{REQUEST_URI} !^/public/
    RewriteRule ^(.*)$ public/$1?Rewrite [L,QSA]
</IfModule>

再次打开根目录,即发现不需要访问public子目录也可以实现访问业务。

0x03

功能实现了,但原理还未说明,接下来我来解释一下上文.htaccess文件的结构与原理:

<IfModule mod_rewrite.c>
    RewriteEngine On
    ...
</IfModule>

这一段不用多说,即检测是否安装mod_rewrite模块,如果安装即可启动URL重写功能。

RewriteBase /

该段的作用是对什么子目录应用伪静态,通常为/,但如果是需要针对某个目录(虚拟目录)应用不同的伪静态配置,可以指定为某个虚拟目录,注意要以/开头,结尾不需要/

RewriteCond %{REQUEST_URI} !^/public/

该段的作用是针对非访问/public目录的所有URI应用伪静态配置,可以理解为编程语言中if的作用。

RewriteRule ^(.*)$ public/$1?Rewrite [L,QSA]

该段的作用是对满足以上所有条件的URI应用伪静态规则,这部分的规则极为复杂,具体可参考Apache关于该部分的文档

事实上.htaccess文件的格式和httpd.conf基本一致,因此很多需要限定作用域(例如目录作用域)的配置都可以放在该文件里以实现动态配置。