不能把 runtime 放在 public 下,因为会导致日志、缓存等敏感文件被直接访问下载,暴露数据库密码、SQL、用户 token 等;Web 服务器默认不拦截该目录,ThinkPHP 也不主动防护,必须通过 define('RUNTIME_PATH', '/绝对路径/') 提前定义并确保 CLI 与 Web 一致。

为什么不能把 runtime 放在 public 下
ThinkPHP 默认把日志、缓存、模板缓存全塞进 runtime 目录,而很多新手直接把整个项目丢进 Web 根目录(比如 Nginx 的 root /var/www/html),没做路径隔离。结果就是:访问 https://yoursite.com/runtime/log/202405/xxx.log 可能直接下载或显示日志内容——数据库密码、SQL 原文、用户 token 全暴露。
这不是理论风险。真实被扫出过 runtime/log/ 目录列表的案例,基本都源于没改路径 + Web 服务器未禁用该目录的访问。
- Web 服务器(Nginx/Apache)默认不拦截对
runtime的请求,除非你显式配置 deny - ThinkPHP 自身不阻止外部访问该目录,它只管写,不管“能不能被看到”
- 哪怕加了 .htaccess 或 location 阻断,也属于补救;根子上移走更干净
修改 runtime_path 的两种可靠方式
ThinkPHP 6+ 主要靠环境变量和配置联动控制,不是改一个文件就完事。必须同时满足框架加载前生效 + 运行时不被覆盖。
推荐用法:在入口文件 public/index.php 顶部,早于 require __DIR__.'/../vendor/autoload.php'; 执行:
立即学习“PHP免费学习笔记(深入)”;
define('RUNTIME_PATH', '/data/www/thinkphp-runtime/');
注意:/data/www/thinkphp-runtime/ 必须是 PHP 进程有读写权限的绝对路径,且不能在 Web 可访问范围内(比如别放在 /var/www/html 子目录里)。
- 不要用相对路径,如
../runtime—— CLI 和 Web 请求的当前工作目录可能不同,会错位 - 不要在
.env里设RUNTIME_PATH—— ThinkPHP 不从 .env 读这个常量,只认define()或配置文件硬编码 - 如果用了 Swoole 或 RoadRunner,还要确认工作进程启动时也能读到该常量(通常没问题,只要入口文件统一)
改完后必须检查的三件事
移路径不是改完 define() 就高枕无忧。常见翻车点都在验证环节。
- 手动创建目标目录并授权:
mkdir -p /data/www/thinkphp-runtime && chown -R www-data:www-data /data/www/thinkphp-runtime(用户组按实际 Web 进程用户调整) - 触发一次日志写入(比如故意抛个异常),然后看
/data/www/thinkphp-runtime/log/下是否有新文件,同时确认public/../runtime/不再生成日志 - 检查
storage目录(如果用了 ThinkPHP 的think-filesystem)是否也被误配到了 Web 下——它和runtime是两套东西,别漏掉
CLI 命令和定时任务的日志路径一致性
Web 请求走 public/index.php,但 php think xxx 命令走的是 think 文件,它默认不加载 public/index.php。如果你只改了入口文件,CLI 下日志仍会写回默认 runtime。
解决办法:统一在 think 文件顶部也加同样的 define(),位置在 #!/usr/bin/env php 下一行即可:
#!/usr/bin/env php
<?php
define('RUNTIME_PATH', '/data/www/thinkphp-runtime/');
// 后续是原有内容...
- 不改
think文件?那就得在每个命令前加环境变量:RUNTIME_PATH=/data/www/thinkphp-runtime php think queue:work—— 容易漏、难维护 - 有些部署用 Supervisor 管理队列,记得在它的
environment=配置里也加上RUNTIME_PATH - 日志路径不一致会导致排查问题时来回切换目录,尤其线上报错只出现在 CLI 场景下却去 Web 的 runtime 里翻,纯属浪费时间
路径挪得再远,权限和一致性没卡死,安全加固就等于贴了张创可贴


















