绝大多数情况是路由匹配失败,根本原因在于 frankenphp 默认把所有请求都转发给 public/index.php 但未保留原始 path_info 或未正确配置 caddy 的 try_files 规则,导致框架无法解析子路由而返回 404。

绝大多数情况是路由匹配失败,根本原因在于 FrankenPHP 默认把所有请求都转发给 public/index.php,但没保留原始 PATH_INFO 或未正确处理重写逻辑,导致除根路径外的路由全被 Laravel/Symfony 当作 404 处理。
确认是否真由 FrankenPHP 路由机制导致
先排除框架自身问题:在本地用 php -S localhost:8000 -t public 启动内置服务器,访问相同路由(如 /login)——如果能打开,基本锁定是 FrankenPHP 的请求透传或 PATH_INFO 丢失问题;如果也 404,则先查路由缓存、中间件或控制器是否存在。
- 运行
php artisan route:list(Laravel)或php bin/console debug:router(Symfony),确认目标路由确实已注册 - 检查
APP_ENV=production下是否误删了routes/web.php或config/routes/dev/被错误加载 - 临时在
public/index.php开头加一行:error_log('REQUEST_URI: ' . ($_SERVER['REQUEST_URI'] ?? 'MISSING'));,对比访问/和/login时日志输出是否一致
Caddyfile 中 try_files 配置不完整
FrankenPHP 依赖 Caddy 的 try_files 规则决定是否交由 PHP 处理。若配置只写了 try_files {path} /index.php,而没显式传递查询参数和路径信息,$_SERVER['PATH_INFO'] 就会为空,框架无法解析子路由。
- 必须使用带
uri重写的写法,例如:php_server {<br> try_files {path} /index.php?{query}<br>} - 若项目用了子目录部署(如
/myapp/),需额外设置uri strip_prefix /myapp,否则{path}值含前缀但框架没做相应调整 - 避免用
reverse_proxy直连frankenphp serve—— 它不模拟传统 FastCGI 环境,SCRIPT_NAME和PATH_INFO极易错乱
Symfony/Laravel 对 $_SERVER 变量的强依赖未满足
两个框架默认假设 $_SERVER['SCRIPT_NAME'] 和 $_SERVER['REQUEST_URI'] 已由 Web 服务器规范化。FrankenPHP 经典模式虽尽力模拟,但在某些重写组合下仍会返回异常值,比如 REQUEST_URI 缺少 query string 或 SCRIPT_NAME 多出 /index.php。
- Laravel 用户可在
bootstrap/app.php顶部加:$_SERVER['SCRIPT_NAME'] = '/index.php';<br>$_SERVER['PATH_INFO'] = $_SERVER['REQUEST_URI'] ?? '';
- Symfony 用户需确保
config/packages/framework.yaml中trusted_hosts和strict_requirements不过度拦截 - 关键验证点:在控制器里 dump
request()->getRequestUri()和request()->getBaseUrl(),看是否与实际访问 URL 匹配
静态资源干扰导致路由 fallback 失败
当 public/js/app.js 这类文件真实存在时,Caddy 会直接返回它;但如果该路径不存在,又没配置好 fallback,请求就可能卡在 Caddy 层,根本没进 PHP。此时你看到的“打不开”其实是 404,但浏览器没报错,因为 HTML 框架页(如 Vue/React)仍在加载,只是 API 接口全挂了。
- 用浏览器 DevTools 的 Network 面板,过滤 XHR/Fetch,看所有接口请求是否都返回 404 或空响应
- 临时在 Caddyfile 的站点块中加一行:
log {<br> output stderr<br>},再访问一个子路由,观察日志里是否有try_files匹配失败记录 - 确认
public/index.php文件权限为可读,且没被 Caddy 的file_server规则意外拦截(比如加了file_server但没设hide)
最常被忽略的是:Caddy 的 try_files 规则一旦匹配到某个路径(哪怕是个空文件),就不会 fallback 到 index.php;而 FrankenPHP 自身对 .htaccess 或 Nginx rewrite 规则完全无感知,所有重写逻辑必须显式落在 Caddyfile 里。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











