phpEnv是一款Windows平台下常用的PHP集成开发环境,自带Nginx、Apache、MySQL等组件,部署起来非常方便。不过很多朋友把它用在线上或者对外访问的环境时,会发现一个明显的问题:默认配置几乎没有任何请求频率限制,只要有人用工具对站点发起CC攻击,也就是短时间内大量伪造正常HTTP请求,服务器CPU立刻飙高,数据库被拖垮,网站直接打不开。这篇文章就来聊聊在phpEnv里怎么配置一套简单但实用的防CC方案,核心思路是利用phpEnv自带的Nginx做请求限流,再配合一些辅助手段把恶意流量挡在外面。

一、理解CC攻击的原理才能有效防护
CC攻击的本质是攻击者控制大量肉鸡或者使用代理IP,模拟正常用户不断访问网站中比较消耗资源的页面,比如带数据库查询的动态页面、搜索功能等。和DDoS不同,CC攻击的流量并不大,但每一个请求都是完整有效的HTTP请求,服务器必须完整处理,所以几个Mbps的攻击流量就可能把一台配置不错的服务器打趴下。
在phpEnv环境下,请求链路一般是浏览器到Nginx,再由Nginx通过FastCGI转给php Study里的PHP进程处理。如果PHP进程数有限,比如默认只开了几个php-cgi进程,攻击者只需要并发几十个请求占满这些进程,正常用户的请求就全部排队超时。所以防护的重点有两个:一是让单个IP不能无限制发请求,二是让缓存层尽可能吸收掉重复请求,减少真正到达PHP的量。
另外要注意,CC攻击的请求特征往往和正常用户很接近,所以不能简单粗暴地一棍子打死,比如限制太严格会把搜索引擎爬虫和正常访客也挡掉。理想的策略是分层:先限流,再验证,最后对确认恶意的IP做封禁。
二、在phpEnv的Nginx中配置请求限流
phpEnv默认使用Nginx作为Web服务器,Nginx自带的限流模块limit_req和limit_conn是防CC最直接的工具。打开phpEnv安装目录,找到Nginx的配置文件夹,一般在conf目录下,编辑主配置文件nginx.conf,在http块中添加限流区域定义。
http {
# 定义一个限速区域,以客户端IP为key,分配10MB共享内存
# 大约可以记录16万个IP地址的状态
limit_req_zone $binary_remote_addr zone=perip:10m rate=10r/s;
# 限制单个IP同时保持的连接数
limit_conn_zone $binary_remote_addr zone=connperip:10m;
server {
listen 80;
server_name localhost;
# 单个IP每秒最多10个请求,允许突发5个排队,多余的直接返回503
location / {
limit_req zone=perip burst=5 nodelay;
limit_conn connperip 20;
limit_req_status 503;
limit_conn_status 503;
proxy_pass http://127.0.0.1:8080;
}
}
}
上面这段配置里,rate=10r/s表示每个IP每秒最多允许10个请求,这个数值要根据自己的站点情况调整。普通的企业站、博客类站点,一个正常用户每秒点击几次顶天了,10r/s已经很宽松;如果你的网站有大量Ajax轮询或者接口调用,可以适当放宽到20r/s。burst=5表示允许短时间的突发请求排队,加上nodelay参数后,突发的请求不会被延迟处理,超出的直接拒绝,这样体验和防护可以兼顾。
配置完成后,在phpEnv面板里重启Nginx让规则生效。验证方法很简单,可以用命令行工具对站点连续发起请求,观察返回状态,如果出现503就说明限流已经生效。需要提醒的是,如果网站前面还挂了CDN或者反向代理,$binary_remote_addr拿到的会是CDN节点的IP,这时候需要改用$http_x_forwarded_for并且配合realip模块才能拿到真实客户端IP,否则限流会把整个CDN节点给限死,误伤所有用户。
三、针对动态页面和异常UA做精细化拦截
单纯的限流是全局一刀切,实际攻击往往集中在特定页面,比如搜索页、登录页、验证码接口。这些页面可以单独加一层更严格的规则。同时在CC攻击中,很多攻击工具的User-Agent是固定的,或者干脆是空的,可以在Nginx层直接判断拦截。
server {
listen 80;
# 屏蔽常见攻击工具和空UA的请求
if ($http_user_agent ~* "python-requests|curl|wget|HttpClient|java|scrapy") {
return 403;
}
if ($http_user_agent = "") {
return 403;
}
# 对消耗资源的动态路径做更严格的限制
location ~ ^/(search|login|api)\.php$ {
limit_req zone=perip burst=2 nodelay;
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
include fastcgi.conf;
}
# 静态资源放宽限制,交给缓存处理
location ~* \.(jpg|png|css|js|gif|ico)$ {
expires 7d;
access_log off;
}
}
这种按路径区分对待的写法,好处是把有限的防护资源用在刀刃上。动态PHP页面的限流阈值设得很紧,而图片、CSS、JS这类静态资源基本不消耗PHP进程,可以放宽甚至直接开启浏览器缓存,这样即使被攻击,页面加载静态资源也不会受影响。UA判断虽然简单,但拦截掉相当一部分低级攻击脚本效果立竿见影,因为很多CC工具默认UA就是python-requests或者curl。
不过要小心误伤问题,有些正常的监控服务、健康检查工具也用curl访问站点,如果你的站点有这类需求,可以把它们的来源IP加白名单处理,用geo模块定义信任IP段,在限流规则里对白名单IP跳过限制。
四、配合缓存和系统防火墙构建多层防线
Nginx限流只能控制频率,如果攻击者把频率控制在阈值边缘,照样能持续消耗资源,这时候需要缓存来兜底。对于不经常变化的页面,可以开启Nginx的fastcgi_cache,把PHP的输出缓存起来,攻击者反复刷同一个页面时,Nginx直接返回缓存内容,PHP进程完全不用参与,攻击成本被降到几乎为零。
http {
# 在http块定义缓存路径和key
fastcgi_cache_path D:/phpEnv/nginx/cache levels=1:2 keys_zone=fcgicache:50m inactive=10m;
server {
location ~ \.php$ {
fastcgi_pass 127.0.0.1:9000;
include fastcgi.conf;
fastcgi_cache fcgicache;
fastcgi_cache_valid 200 10m;
fastcgi_cache_key $request_method$request_uri;
add_header X-Cache $upstream_cache_status;
}
}
}
配置后通过浏览器开发者工具查看响应头,X-Cache字段显示HIT就说明命中了缓存。注意缓存路径要改成你自己的phpEnv实际安装盘符,且该目录需要有写入权限。对于登录用户这类不能缓存的动态内容,就不适合这套方案,需要靠前面的限流和后面的验证机制来保护。
最后在系统层面加一道保险。Windows自带的防火墙可以写规则限制单IP的并发连接数,也可以借助phpEnv面板查看访问日志,找出请求量异常的IP段手动封禁。如果站点规模较大,建议再配合云服务商的高防或者WAF产品,本地防护做到限流加缓存这一层,专业清洗交给云端,整体防御效果会好很多。日常还要养成定期查看Nginx访问日志的习惯,攻击发生初期往往有明显的特征,早发现早处理,比事后补救轻松得多。