phpEnv是一款集成了Nginx、PHP、MySQL的Windows下的Web集成环境,很多开发者在本地调试或小规模部署时会用到它。有时候你会发现一些奇怪的域名解析到了你的服务器IP上,或者团队内部希望屏蔽某些测试域名不让别人随意访问,这时候就需要在Nginx层面配置黑名单来禁止特定域名的访问。本文将结合phpEnv的目录结构和Nginx配置语法,详细介绍几种实用的拦截方案。

一、理解phpEnv中Nginx配置文件的位置
在动手配置之前,先要找到正确的配置文件位置。phpEnv的Nginx配置大致分为几层:主配置文件、vhost虚拟主机配置、以及可选的自定义扩展配置。通常主配置文件在phpEnv安装目录的nginx\conf\nginx.conf,而每个网站的虚拟主机配置则存放在nginx\conf\vhost目录下,每个站点一个.conf文件。
拦截特定域名有两种思路:一种是在默认虚拟主机(也就是没有被其他server块匹配到的请求)里做拦截,另一种是新建一个专门的server块来接收黑名单域名并拒绝。理解这一点很关键,因为Nginx处理请求时,会根据请求头中的Host字段去匹配server_name,如果没有任何server块的server_name匹配成功,请求就会落到默认server上。
在phpEnv中修改配置后,需要在面板里点击重载或者重启Nginx才能生效。如果修改后启动报错,多半是语法问题,可以在phpEnv自带的命令行中执行nginx -t检查配置语法,定位到具体出错的行号再修改。
二、使用return 444直接断开黑名单域名的连接
最直接粗暴的方式是利用Nginx的444状态码。444是Nginx特有的非标准状态码,含义是不返回任何响应直接关闭连接,对恶意域名解析来说非常有效,因为对方连一个错误页面都拿不到。
具体做法是新建一个server块,把黑名单域名作为server_name写进去,所有命中这个server块的请求全部返回444:
server {
listen 80;
# 黑名单域名,多个域名用空格分隔
server_name bad-domain.com www.bad-domain.com spam-site.net;
# 直接断开连接,不返回任何内容
return 444;
}把这段配置保存为一个独立的.conf文件放进vhost目录,比如命名为block.conf,然后在phpEnv面板里重载Nginx即可。这种方式的优点是配置简单、拦截彻底,而且独立成文件便于管理,以后要增加或删除黑名单域名只需要改这一个文件。
需要注意的是,server_name匹配优先级的问题。Nginx会优先选择完全匹配的server_name,其次才是通配符和正则匹配。所以只要黑名单server块里的域名是精确写出的,就会优先于其他使用了通配符的站点配置被命中,不会出现拦截失败的情况。
三、用if条件配合正则实现灵活拦截
如果黑名单域名有一定规律,比如要屏蔽所有带某类关键词的域名,用正则匹配会更省事。可以在任意server块内使用if指令对Host进行判断:
server {
listen 80 default_server;
server_name _;
# 匹配Host中包含敏感关键词的请求
if ($host ~* (bad-domain|spam|illegal)) {
return 444;
}
# 其余未配置的域名也一并拒绝
return 403;
}上面这个配置同时扮演了默认虚拟主机的角色。任何一个没有被正常站点匹配到的域名,只要Host里含有bad-domain、spam或illegal这些关键词,连接会被直接掐断;即使是其他未知的域名,也会收到403拒绝访问,而不是落到某个默认站点上展示内容。这种策略适合服务器有公网IP、担心别人恶意解析域名过来的场景。
不过要提醒一点,Nginx的if指令在某些上下文里的行为比较怪异,官方也建议谨慎使用。在server块级别用if做return是安全的用法,但不要在if里写rewrite以外的复杂逻辑,否则可能出现难以预料的结果。判断条件尽量简单,拦截动作只用return,这样最稳妥。
四、借助map指令批量管理黑名单
当黑名单域名数量多、且分散在多个server块中都需要判断时,用map指令是更优雅的方案。map可以在http级别把Host映射成一个自定义变量,然后在各处引用这个变量做判断:
# 放在http块中,nginx.conf或conf.d下的公共文件里
map $host $is_blocked {
default 0;
bad-domain.com 1;
spam-site.net 1;
~*illegal.* 1;
}
server {
listen 80;
server_name _;
# 根据映射结果决定是否拦截
if ($is_blocked) {
return 444;
}
return 403;
}map的好处是把黑名单清单集中在一处维护,支持精确匹配和正则匹配混用,可读性比一堆if判断好得多。而且map是在请求处理早期就完成计算的,性能开销几乎可以忽略。phpEnv用户可以把这段map写进nginx.conf的http块里,也可以单独建一个conf.d目录并include进来,看个人习惯。
如果黑名单域名特别多,还可以把域名列表放到一个单独的文本文件里,用include的方式引入。不过要注意include进来的内容必须是合法的Nginx配置片段,不能是纯文本列表,这一点和某些面板的做法不同,不要想当然地写一个纯列表文件。
五、常见问题排查与注意事项
配置完成后如果发现拦截没生效,首先检查是不是改错了文件。phpEnv的vhost目录下每个站点一个文件,如果请求命中的是别的server块,你在自己站点里加的if判断自然不会执行。可以在浏览器里直接用黑名单域名访问,观察返回结果,也可以临时把return改成return 200 "blocked";方便确认配置是否被命中。
其次注意本地测试的场景。如果你是在本地电脑上测试,浏览器访问域名依赖于hosts文件的解析,需要先把测试域名指向127.0.0.1才能看到效果。改完hosts后如果浏览器有缓存,建议开无痕窗口或者清一下DNS缓存再试。
最后说说HTTPS的情况。如果你的站点开启了443监听,黑名单拦截也要单独配置一个监听443的server块,否则攻击者通过HTTPS请求还是可能落到正常站点上。对于拦截用的server块,证书可以随意配一个自签证书甚至不配证书,因为return 444在TLS握手阶段之后就会被处理,浏览器端表现就是连接被重置,达不到窃取内容的目的。另外,如果phpEnv中配置修改后Nginx无法启动,先用nginx -t验证语法,重点检查分号、花括号是否闭合,以及中文输入法带来的全角字符问题,这类低级错误占了配置失败原因的大多数。