
PHP应用中表单action指向src/等非Web可访问目录时触发404,本质是Web服务器(如Apache)拒绝执行位于文档根目录之外的PHP脚本——需通过路由重写、入口统一或路径重构实现安全调用。
php应用中表单`action`指向`src/`等非web可访问目录时触发404,本质是web服务器(如apache)拒绝执行位于文档根目录之外的php脚本——需通过路由重写、入口统一或路径重构实现安全调用。
在典型的现代PHP项目结构中(如你所示:public/为Web根目录,src/存放业务逻辑),直接将表单action设为src/create.php必然导致404错误。这是因为Web服务器(如Apache或Nginx)仅允许执行位于其DocumentRoot(如/var/www/html/public或C:\xampp\htdocs\myapp\public)之下的PHP文件。而src/目录通常位于public同级或上级,不在Web可访问路径内——浏览器发起的HTTP请求无法“穿透”到该目录,服务器直接返回404,根本不会触达PHP解析器。
✅ 正确做法:统一入口 + 路由分发(推荐)
所有用户请求必须经由public/下的入口文件(如index.php或专用API路由文件)处理,再由PHP内部include/require加载src/中的逻辑。这是安全、标准且框架通用的实践。
步骤1:修改HTML表单,指向public内可控入口
<!-- public/index.php 中的表单 --> <form method="POST" action="process.php"> <label>Task:</label> <input name="title" type="text" placeholder="Name your task..." required> <label>Task description:</label> <input name="description" type="text" placeholder="Description" required> <input type="submit" name="submit" value="Create a task"> </form>
步骤2:在public/下创建process.php作为安全入口
立即学习“PHP免费学习笔记(深入)”;
<?php
// public/process.php
// 1. 明确禁止直接访问(可选防御)
if (basename(__FILE__) === basename($_SERVER['PHP_SELF'])) {
http_response_code(403);
exit('Forbidden');
}
// 2. 引入数据库连接(注意路径:从public出发向上跳转)
require_once __DIR__ . '/../database/conn_db.php';
// 3. 复用原有create.php核心逻辑(不复制粘贴,而是封装复用)
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['submit'])) {
$taskTitle = mysqli_real_escape_string($conn, $_POST['title'] ?? '');
$taskDescription = mysqli_real_escape_string($conn, $_POST['description'] ?? '');
if (empty($taskTitle)) {
header("Location: index.php?create=error&msg=" . urlencode("Invalid title"));
exit();
}
if (empty($taskDescription)) {
header("Location: index.php?create=error&msg=" . urlencode("Invalid description"));
exit();
}
$sql = "INSERT INTO todolist.tasks (title, description) VALUES (?, ?)";
$stmt = mysqli_prepare($conn, $sql);
mysqli_stmt_bind_param($stmt, "ss", $taskTitle, $taskDescription);
mysqli_stmt_execute($stmt);
header("Location: index.php?create=success");
exit();
}? 关键说明:
require_once __DIR__ . '/../database/conn_db.php'中的__DIR__指向public/目录,因此../database/conn_db.php正确解析为项目根目录下的database/conn_db.php。PHP脚本内部包含是服务端行为,不受Web服务器路径限制。
⚠️ 为什么action="src/create.php"一定失败?
- Apache/Nginx 的
DocumentRoot默认设置为public/(XAMPP/WAMP等集成环境亦然); - 浏览器请求
http://localhost/src/create.php→ Web服务器在public/下查找src/create.php→ 文件不存在 → 返回HTTP 404; - 即使你手动把
src/移到public/下(如public/src/create.php),也违背了安全最佳实践:业务逻辑文件不应暴露在Web可访问路径中,否则可能被直接下载源码或触发未预期执行。
? 其他常见误区与修正
| 误区 | 风险/问题 | 正确方案 |
|---|---|---|
action="../src/create.php" |
浏览器尝试访问父目录,违反同源策略且服务器拒绝 | ❌ 绝对不可行;路径是URL,不是文件系统路径 |
在.htaccess中添加RewriteRule强行暴露src/
|
极高安全风险:泄露敏感逻辑、配置、密钥 | ❌ 禁止!src/、database/等目录应严格禁止Web访问 |
使用<base href>试图改变相对路径基准 |
仅影响资源链接(CSS/JS),不影响表单提交目标 | ❌ 无效且易引发其他路径混乱 |
✅ 部署前必检清单
- ✅ Apache中确认
DocumentRoot指向public/目录(非项目根目录); - ✅
.htaccess(如使用)中无规则意外拦截process.php; - ✅
public/目录权限为755,PHP文件为644,确保Web用户(如www-data)有读取权; - ✅ 数据库连接文件
conn_db.php中绝不硬编码密码,应使用环境变量或配置中心; - ✅ 使用预处理语句(如上例
mysqli_prepare)替代字符串拼接,彻底杜绝SQL注入。
遵循此结构,你的应用既符合PSR-4自动加载规范雏形,又具备生产环境安全性与可维护性。记住:404不是PHP报的,是Web服务器说“这个URL我找不到对应文件”——而你的任务,是让所有合法请求都落在它能找到的、且受控的PHP入口上。



















