从 public_html 以外的文件夹发布
默认情况下,一个域名会发布你主目录中的 public_html。现代 PHP
框架的构建方式恰恰相反:应用位于一个文件夹里,
而只有其内部的一个 public/ 子文件夹才应该可以从 Web 访问到。
.env 文件——数据库密码、应用密钥、支付凭据——
被刻意地放在上一级。
如果你改为发布整个项目,/.env 就成了任何人都能请求的 URL。
选择文档根目录正是你避免这种情况的方法。
设置它
域名 → 该域名所在的行 → 文档根目录(Document root)。
系统会要求你填一个 你主目录内部 的文件夹,相对于主目录 书写:
app/public
而不是 /home/you/app/public。留空即可回到 public_html。
面板会在文件夹尚不存在时创建它,重写 Web 服务器 配置并重新加载。完成后,它会告诉你它现在正在发布的 完整路径,如果它不得不创建了该文件夹,也会明确说明。
之后一个典型的布局:
/home/you/
├── app/ <- the whole project: composer.json, .env, vendor/
│ └── public/ <- the only part served to the web
└── public_html/ <- the default, no longer used
检查它是否生效
加载站点。然后请求你之前想保护的那个文件:
curl -sI https://yourdomain.com/.env
403 是你想要的答案。200 意味着文档根目录仍然在
.env 之上,变更并未生效——重新加载 域名 页面并确认
该行显示的是你输入的文件夹。
哪些域名有这个功能
- 主域名、附加域名和子域名 各自有各自的文档 根目录以及各自的 文档根目录(Document root) 按钮。
- 停靠(别名)域名 没有。别名总是服务于你的主 网站,这正是它之所以为别名的原因。面板会这样说明:“A parked domain always serves your primary website. Change the primary document root instead, or convert this to an addon domain.”
- 重定向 没有。重定向本身不服务任何内容。
面板拒绝什么,以及为什么
该值在到达任何有特权的环节之前会被检查,并在 另一侧再次检查。被拒绝时会以页面上的一条消息返回,且不改变 任何东西。
| 你输入的 | 为什么被拒绝 |
|---|---|
/var/www/html、~/app |
它必须相对于你的主目录。绝对路径和 ~ 不被接受。 |
../../etc、app/../.. |
文件夹名不能以点开头,正是这一点使得 .. 不可能出现。凭构造,你主目录之外的任何东西都无法触及。 |
.ssh、.git、.config |
同一条规则。发布 .ssh 会把你的密钥放到 Web 上;发布 .git 会把你的源代码历史放到那里。 |
tmp |
你的 PHP 会话文件放在那里。发布它意味着发布你访客的会话。 |
logs、etc、mail、ssl、backups |
面板为你的账户所使用。 |
a/b/c/d/e |
最多四级深。 |
| 任何超过 128 个字符的内容 | 太长。 |
服务器本身上还会发生一项检查:文件夹会被穿过任何
符号链接进行解析并重新检查。一个指向你主目录之外的符号链接会被拒绝,
而不是被跟随,因此 ln -s / ~/app 不会变成一个被对外服务的文件系统。
证书
无需做任何事。证书是为 域名 而不是文件夹签发的,而 你设置的文档根目录会在写入证书的那一刻被重新读取——因此 在 HTTPS 正在设置时更改它是安全的。一个已经有 HTTPS 的 附加域名会保留它。
当有什么看起来不对时
站点显示一个目录列表或 “index of /”。 新文件夹里还没有
index.php 或 index.html。上传应用,或者把文档根目录
指向确实有这类文件的那个文件夹。
每个页面都返回 403,而不只是 .env。 文件夹存在但 Web 服务器
读不进去。用文件管理器检查该文件夹及其父目录
是否属于你的账户,尤其是如果你以另一个用户的身份通过 SSH 把文件
放到那里的话。
每个页面都返回 404,而站点一分钟前还在正常工作。 重新加载 域名
页面并读该行显示的文件夹。打错的字会被原封不动地按输入发布
——面板是创建文件夹而不是猜测,因此一个空的新文件夹
是通常的原因。把它设置回 public_html,或者设成正确的文件夹。
每个页面都返回 500,并带有关于缺少密钥的框架错误。 那是
应用的问题,不是文档根目录的问题——框架已经被访问到了。对
Laravel 而言,这通常是一个空的 APP_KEY。