
本文讲解如何在不依赖 .htaccess 和 HTTP 请求的前提下,安全、高效地在 PHP 原生路由(如 index.php)中判断请求路径对应本地文件是否真实存在,避免递归请求、超时及安全隐患,并提供可直接复用的健壮实现方案。
本文讲解如何在不依赖 .htaccess 和 HTTP 请求的前提下,安全、高效地在 PHP 原生路由(如 index.php)中判断请求路径对应**本地文件是否真实存在**,避免递归请求、超时及安全隐患,并提供可直接复用的健壮实现方案。
在基于 index.php 的单入口原生 PHP 路由中,常见需求是:当用户访问 /assets/style.css 或 /pages/about.php 时,若该路径对应服务器上真实存在的静态/动态文件,则直接交付(如 readfile() 或 include),否则交由路由逻辑处理(如渲染 404 页面)。但需特别注意:绝不能使用 get_headers($url) 检查 http://... 形式的 URL——这会触发一次完整的 HTTP 回环请求(例如 http://localhost/pages/home.php → 再次命中 index.php),造成递归、超时、500 错误及严重性能损耗,正如问题中出现的 failed to open stream: HTTP request failed 警告。
正确做法是:将 URL 路径映射为服务器本地文件系统路径,再用 file_exists() 进行判断。这是零网络开销、毫秒级响应、完全可控的安全方案。
✅ 推荐实现(安全、简洁、可扩展)
// index.php —— 单入口路由主文件
$unauthorized_file_types = ['php', 'env', 'log', 'ini']; // 禁止直接暴露的扩展名
// 1. 解析请求 URI(去除查询参数和锚点)
$request_uri = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
// 2. 规范化路径(防止目录遍历攻击)
$clean_path = realpath(__DIR__ . $request_uri);
// 3. 验证路径是否仍在项目根目录内(关键防护!)
if ($clean_path === false || strpos($clean_path, __DIR__) !== 0) {
http_response_code(403);
die('Forbidden');
}
// 4. 检查文件是否存在且为常规文件
if (file_exists($clean_path) && is_file($clean_path)) {
$ext = strtolower(pathinfo($clean_path, PATHINFO_EXTENSION));
// 拒绝敏感文件类型(如 .php 不应被直接下载源码)
if (in_array($ext, $unauthorized_file_types)) {
http_response_code(404);
include __DIR__ . '/404.php';
exit;
}
// 安全交付静态资源(自动设置 Content-Type)
$mime_types = [
'css' => 'text/css',
'js' => 'application/javascript',
'png' => 'image/png',
'jpg' => 'image/jpeg',
'gif' => 'image/gif',
'svg' => 'image/svg+xml',
'html' => 'text/html; charset=utf-8'
];
$mime = $mime_types[$ext] ?? 'application/octet-stream';
header('Content-Type: ' . $mime);
header('Cache-Control: public, max-age=31536000'); // 长缓存(静态资源适用)
readfile($clean_path);
exit;
}
// 文件不存在 → 进入 MVC 路由逻辑(如解析 /user/123 → 控制器处理)
// ... your routing code here ...⚠️ 关键注意事项
- 禁止路径遍历:必须使用 realpath() + strpos($clean_path, __DIR__) === 0 双重校验,防止 ../../../etc/passwd 类攻击;
- 区分 file_exists() 与 is_file():前者对目录也返回 true,务必补 is_file() 确保是文件;
- 不要用 header("Location: $url") 跳转:这仍会引发 HTTP 循环;应直接 readfile() 或 include;
- .php 文件处理策略:通常不应被直接 readfile()(会输出源码),而应 include 执行(需确保业务逻辑允许)或一律 404;
- 性能优势:file_exists() 是底层系统调用,比 HTTP 请求快 100 倍以上,无并发瓶颈。
✅ 总结
在原生 PHP 路由中判断“文件是否存在”,本质是路径映射 + 本地文件系统检查,而非网络探测。摒弃 get_headers(),拥抱 realpath() + file_exists() + is_file() 组合,即可构建出安全、极速、符合生产环境要求的静态资源路由层。此方案无需 Apache/Nginx 配置,完全兼容 CLI Server、Docker 及各类托管环境。
立即学习“PHP免费学习笔记(深入)”;











