PHP如何禁止单个IP及IP段访问?代码实现方法有哪些?
- 虚拟主机
- 2025-12-17
- 4
在PHP中禁止单个IP或IP段访问是网站安全防护的常见需求,主要通过获取客户端IP地址并与预设的黑名单进行匹配实现,以下是具体的实现方法、代码示例及注意事项,涵盖基础实现、IP段匹配、Nginx/Apache环境配置及优化方案。
获取客户端真实IP地址
客户端IP可能通过$_SERVER变量中的多个字段传递,需优先检查HTTP_X_FORWARDED_FOR(代理服务器转发)和REMOTE_ADDR(直接连接),但需注意HTTP_X_FORWARDED_FOR可能杜撰,以下是获取真实IP的函数:
function getClientIp() { $ip = ''; if (isset($_SERVER['HTTP_X_FORWARDED_FOR']) && !empty($_SERVER['HTTP_X_FORWARDED_FOR'])) { $ip = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR'])[0]; } elseif (isset($_SERVER['REMOTE_ADDR'])) { $ip = $_SERVER['REMOTE_ADDR']; } return filter_var($ip, FILTER_VALIDATE_IP) ? $ip : '0.0.0.0'; }
禁止单个IP访问
通过比较客户端IP与黑名单中的单个IP实现,代码如下:
$blockedIps = ['192.168.1.100', '10.0.0.50']; $clientIp = getClientIp(); if (in_array($clientIp, $blockedIps)) { header('HTTP/1.1 403 Forbidden'); die('Access Denied: Your IP has been blocked.'); }
注意事项:

- 黑名单应存储在数据库或配置文件中,避免硬编码。
- 考虑使用filter_var($ip, FILTER_VALIDATE_IP)验证IP格式,防止无效IP匹配。
禁止IP段访问
IP段匹配需将客户端IP与CIDR格式的网段(如168.1.0/24)比较,核心是判断IP是否属于网段范围,以下是IP段匹配函数及实现:
function ipInNetwork($ip, $network) { $network = explode('/', $network); $netIp = $network[0]; $mask = $network[1] ?? 32; $ipLong = ip2long($ip); $netLong = ip2long($netIp); $maskLong = bindec(str_repeat('1', $mask) . str_repeat('0', 32 $mask)); return ($ipLong & $maskLong) === ($netLong & $maskLong); } $blockedNetworks = ['192.168.1.0/24', '10.0.0.0/16']; $clientIp = getClientIp(); foreach ($blockedNetworks as $network) { if (ipInNetwork($clientIp, $network)) { header('HTTP/1.1 403 Forbidden'); die('Access Denied: Your IP range has been blocked.'); } }
关键点:
- ip2long将IP转换为长整型,通过位运算快速判断网段包含关系。
- 支持IPv4,IPv6需额外处理(如使用inet_pton)。
- CIDR掩码范围应为132,否则可能引发错误。
综合实现与优化
结合单个IP和IP段的黑名单,并增加缓存提升性能:

优化建议:
- 缓存黑名单:使用Redis或Memcached存储黑名单,减少重复计算。
- 异步更新:通过后台脚本定期更新黑名单,避免阻塞请求。
- 日志记录:记录被拦截的IP及时间,便于分析攻破行为。
服务器层配置
对于高性能场景,可在Nginx或Apache中直接配置IP黑名单,减少PHP处理开销。
Nginx配置示例
location / { deny 192.168.1.100; deny 10.0.0.0/16; allow all; # ...其他配置 }
Apache配置示例(.htaccess)
Require all denied Require ip 192.168.1.100 Require not ip 10.0.0.0/16
对比:
| 方案 | 优点 | 缺点 |
||||
| PHP实现 | 灵活,可动态更新黑名单 | 性能较低,需每次请求执行 |
| 服务器层配置 | 高性能,减少PHP负载 | 需手动修改配置文件,不够灵活 |

常见问题与解决方案
-
IP获取不准确
问题:通过代理服务器时,REMOTE_ADDR可能为代理IP而非真实IP。
解决:优先检查HTTP_X_FORWARDED_FOR,但需结合信任的代理IP列表验证,避免杜撰。
-
IPv6支持缺失
问题:ip2long仅支持IPv4,IPv6需转换处理。
解决:使用inet_pton和inet_ntop函数,
相关问答FAQs
Q1: 如何动态更新IP黑名单而不重启服务器?
A1: 可将黑名单存储在数据库或Redis中,PHP脚本每次请求时读取最新数据,使用Redis的EXPIRE设置过期时间,并通过后台脚本定期更新,对于Nginx,可通过ngx_http_geo_module模块结合外部数据源实现动态更新。
Q2: 禁止IP访问后,如何允许特定用户通过?
A2: 可结合用户认证机制,在黑名单检查前验证用户身份。
if (isIpBlocked($clientIp) && !isLoggedIn()) { die('Access Denied'); } // 或为白名单IP跳过检查 if (!in_array($clientIp, $whitelistedIps)) { if (isIpBlocked($clientIp)) { die('Access Denied'); } }