
本文讲解如何在不依赖 .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免费学习笔记(深入)”;