惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

小众软件
小众软件
N
News and Events Feed by Topic
A
About on SuperTechFans
aimingoo的专栏
aimingoo的专栏
The Cloudflare Blog
H
Heimdal Security Blog
Schneier on Security
Schneier on Security
Engineering at Meta
Engineering at Meta
Google Online Security Blog
Google Online Security Blog
宝玉的分享
宝玉的分享
AI
AI
The GitHub Blog
The GitHub Blog
MongoDB | Blog
MongoDB | Blog
www.infosecurity-magazine.com
www.infosecurity-magazine.com
The Last Watchdog
The Last Watchdog
T
Troy Hunt's Blog
S
Security @ Cisco Blogs
H
Hacker News: Front Page
F
Fortinet All Blogs
博客园_首页
S
Secure Thoughts
N
News and Events Feed by Topic
P
Proofpoint News Feed
Microsoft Azure Blog
Microsoft Azure Blog
I
InfoQ
Spread Privacy
Spread Privacy
Hacker News - Newest:
Hacker News - Newest: "LLM"
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
C
Check Point Blog
Hugging Face - Blog
Hugging Face - Blog
Hacker News: Ask HN
Hacker News: Ask HN
C
CXSECURITY Database RSS Feed - CXSecurity.com
酷 壳 – CoolShell
酷 壳 – CoolShell
Stack Overflow Blog
Stack Overflow Blog
L
LINUX DO - 最新话题
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
S
Schneier on Security
Know Your Adversary
Know Your Adversary
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Scott Helme
Scott Helme
P
Privacy & Cybersecurity Law Blog
S
Securelist
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
O
OpenAI News
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
PCI Perspectives
PCI Perspectives
L
LangChain Blog
雷峰网
雷峰网
Security Archives - TechRepublic
Security Archives - TechRepublic
V2EX - 技术
V2EX - 技术

Nicksxs's Blog

浅析一下jpeg图片格式及其来源 关于github拉取下载加速的另一个方式 关于适合什么模型,推荐下llmfit 看看目前本地能跑什么模型,使用llama.cpp 最近使用vibe coding的一些感悟 使用php的inotify扩展来监听文件变更 一些设计模式的记忆点 使用xiaomi mimo大模型api运行Hermes Agent 结合Obsidian的cli的一体化体验 开始尝试使用obsidian作为笔记软件 学习下大神的知识库 体验下微软开源的Markdown转换工具Markitdown 学习下git的worktree 一些架构师知识点的记录 解答一下关于traefik的一点疑惑 记录一下迁移服务器需要使用的一些命令 较早代iPhone更换新iPhone的一些小指南 如何查看mac的路由表和网关等信息 如何开启mysql的optimizer trace 浅析下mysql的索引的基数与可选择性 分享个加速github访问的方法 学习体验下Koupleless框架-浅析代码 学习体验下Koupleless框架 体验下nocodb这个神奇的系统 学习下MDC的机制 重新复习理解下java的类加载器 记录下ffmpeg命令 尝试学习理解下Claude Code的实现原理和细节 在Antigravity中对比体验Claude Sonnet 4.5模型 来看下我装备了5060TI显卡的gpt-oss模型表现 来看下google最新力作Antigravity的水平如何
从 app.test 到小锁:valet 本地 HTTPS 的完整链路
2026-06-28 · via Nicksxs's Blog

之前在用到valet的时候就觉得这个工具很厉害,因为本地部署很多时候都是比较费劲的,也比较简陋,就直接localhost启动下,但是有时候需要验一下回调的,就需要有域名跟https,当然之前也没想明白的是在本地其实用本地的地址也是可以的,意思就是如果回调地址没限制必须要域名,也可以用127.0.0.1或者localhost,当然如果需要域名和https那就正好是valet的用武之地,所以想把 Valet 这类工具背后的逻辑捋一下。

平时用 Valet 的时候体验是非常顺的:

1
2
valet park
valet secure demo

然后浏览器里访问:

1
https://demo.test

就能看到本地项目,而且还有 HTTPS 的小锁。

这个体验乍一看很像魔法。一个本地域名,既不用手动写 hosts,也不用自己配 Nginx,还不用去公网 CA 申请证书,怎么就能跑起来了呢?

其实拆开之后,Valet 并没有发明什么新协议,它只是把本地开发里几件比较麻烦的事情串起来了:

1
2
3
4
5
6
本地域名解析
Nginx 入口
PHP-FPM 执行 PHP
项目目录映射
本地 CA 和站点证书
框架 driver 判断入口文件

先看完整链路

先放一个整体流程,后面再一段一段拆。

假设我们访问:

1
https://demo.test/users/1

整个过程大概是:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
浏览器访问 https://demo.test/users/1
|
v
macOS 发现 .test 域名要走 /etc/resolver/test
|
v
本地 dnsmasq 把 demo.test 解析到 127.0.0.1
|
v
请求到达本机 443 端口
|
v
Nginx 接收请求,并使用 demo.test 的证书完成 HTTPS
|
v
Nginx 把请求交给 Valet 的 server.php
|
v
server.php 根据 Host 找到 demo 对应的本地目录
|
v
Valet driver 判断这是 Laravel 项目
|
v
静态文件直接返回,非静态请求交给 public/index.php
|
v
Laravel 应用开始处理路由

所以可以先得出一个粗略结论:

1
2
3
4
5
6
DNS 负责把域名带到本机
Nginx 负责接住 HTTP/HTTPS 请求
server.php 负责把请求分发到具体项目
driver 负责判断不同框架的入口规则
PHP-FPM 负责真正执行 PHP
本地 CA 负责让浏览器信任本地证书

这个结论很重要,因为很多时候我们排查 Valet 问题,就是在判断链路断在哪一层。

比如:

1
2
3
4
5
域名打不开,可能是 DNS/dnsmasq 问题
80/443 端口不通,可能是 Nginx 问题
项目找不到,可能是 park/link 映射问题
PHP 报错,可能是 PHP-FPM 或项目本身问题
HTTPS 不被信任,可能是证书或系统信任问题

源码里可以先看 Configuration.php

Valet 会在用户目录下维护一个自己的配置目录:

1
~/.config/valet

这个目录下面会有几类东西:

1
2
3
4
5
6
7
~/.config/valet/config.json
~/.config/valet/Sites
~/.config/valet/Drivers
~/.config/valet/Nginx
~/.config/valet/Log
~/.config/valet/Certificates
~/.config/valet/CA

从源码看,Configuration::install() 做的事情就是创建这些基础目录,并确保基础配置存在。

其中最核心的是:

1
config.json

它大概长这样:

1
2
3
4
5
{
"tld": "test",
"loopback": "127.0.0.1",
"paths": []
}

这几个字段分别代表:

1
2
3
tld       本地域名后缀,默认是 test
loopback 本地回环地址,默认是 127.0.0.1
paths 被 park 的目录列表

所以 Valet 的状态不是凭空来的,它有一个非常明确的落盘位置。

比如 valet park 之后,当前目录会被写到 paths 里。

比如 valet link demo 之后,~/.config/valet/Sites/demo 里会出现一个指向真实项目目录的符号链接。

比如 valet secure demo 之后,证书会进入:

1
~/.config/valet/Certificates

对应的 Nginx 站点配置会进入:

1
~/.config/valet/Nginx

理解这个目录很有用,因为后面很多问题都可以直接去这里看。

valet install 大概做了什么

我们平时执行:

1
valet install

它背后不是单纯装一个命令,而是在本机准备一整套本地 Web 开发环境。

从源码结构上看,大体可以分成几部分:

1
2
3
4
5
Configuration.php  创建 ~/.config/valet 相关目录和 config.json
DnsMasq.php 安装和配置 dnsmasq
Nginx.php 安装和配置 Nginx
PhpFpm.php 配置 PHP-FPM 和 valet.sock
Valet.php 处理 valet 命令本身的链接和 sudoers 等辅助事情

这里先不展开 PHP 版本管理和 sudoers 等细节,只看主链路。

Valet 安装完成后,本机基本会形成这么一个结构:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
macOS DNS
|
v
dnsmasq
|
v
127.0.0.1:80 / 127.0.0.1:443
|
v
Nginx
|
v
Valet server.php
|
v
具体项目

也就是说,Valet 的轻量不是说它什么服务都没有,而是它复用了本机的 Nginx、PHP-FPM、dnsmasq,不需要为每个项目单独开一个虚拟机或容器。

demo.test 为什么会到本机

先看第一个问题:

1
demo.test 怎么就指向 127.0.0.1 了?

如果不用 Valet,我们最容易想到的方法是改 /etc/hosts

1
127.0.0.1 demo.test

但这样有个问题:每加一个项目,就要写一行。

Valet 不这么做。它用的是 dnsmasq 加 macOS resolver。

源码里对应的是 DnsMasq.php

DnsMasq::install() 的流程大概是:

1
2
3
4
5
确保 dnsmasq 已安装
让 dnsmasq 加载额外配置目录
创建 .test 后缀对应的 dnsmasq 配置
创建 /etc/resolver/test
重启 dnsmasq

这里有两个关键文件。

第一个是类似这样的 dnsmasq 配置:

1
2
3
address=/.test/127.0.0.1
address=/.test/::1
listen-address=127.0.0.1

它的意思是:

1
所有 .test 结尾的域名,都解析到本机

所以这些域名都可以成立:

1
2
3
4
demo.test
blog.test
api.test
anything.test

第二个是:

1
/etc/resolver/test

内容大概是:

1
nameserver 127.0.0.1

这个文件告诉 macOS:

1
遇到 .test 这个后缀,不要走普通 DNS,交给 127.0.0.1 上的 DNS 服务处理

然后本地的 dnsmasq 再把它解析回 127.0.0.1。

这里要注意一件事:DNS 这一层只解决了“域名到 IP”的问题。

也就是说:

1
2
3
demo.test -> 127.0.0.1
blog.test -> 127.0.0.1
api.test -> 127.0.0.1

但是 DNS 并不知道:

1
2
demo.test 对应哪个项目目录
blog.test 对应哪个项目目录

这个问题要留给后面的 Nginx 和 Valet 自己处理。

请求到本机后谁来接

域名解析到 127.0.0.1 之后,浏览器会根据协议和端口发请求。

如果是 HTTP:

1
http://demo.test -> 127.0.0.1:80

如果是 HTTPS:

1
https://demo.test -> 127.0.0.1:443

这时接请求的是 Nginx。

源码里对应的是 Nginx.php

Nginx::install() 大致做几件事:

1
2
3
4
确保 Nginx 已安装
写入 nginx.conf
写入 Valet 的 valet.conf
创建 ~/.config/valet/Nginx 目录

这里比较关键的是 Valet 的 Nginx 不是简单地给某一个项目写死 root。

它会准备一个统一入口,把请求转给 Valet 自己的 server.php

可以粗略理解为:

1
2
3
4
5
6
7
Nginx 接住所有本地 .test 请求
|
v
统一转给 Valet server.php
|
v
server.php 再决定这个请求属于哪个项目

这一点是 Valet 设计里比较关键的地方。

如果完全靠 Nginx,每个项目都要生成一个完整的 server block:

1
2
demo.test -> /Users/me/code/demo/public
blog.test -> /Users/me/code/blog/public

Valet 当然也会为 HTTPS、proxy、PHP 隔离这类特殊场景生成站点配置,但普通场景下,它更核心的思路是:

1
2
Nginx 做入口
PHP 层的 server.php 做动态分发

这样它才能做到 valet park 之后,目录下面新增一个项目,不需要手工改 Nginx 配置也能访问。

接着看项目目录映射。

Valet 常用两个命令:

1
2
valet park
valet link

它们都能让本地域名对应到项目,但思路不一样。

park 是登记一个父目录

假设我们在这个目录执行:

1
2
cd ~/Sites
valet park

然后目录结构是:

1
2
3
~/Sites/demo
~/Sites/blog
~/Sites/shop

Valet 就可以让这些域名工作:

1
2
3
demo.test -> ~/Sites/demo
blog.test -> ~/Sites/blog
shop.test -> ~/Sites/shop

从配置角度看,park 主要就是把 ~/Sites 这个路径写入 config.jsonpaths

之后 Valet 查找站点时,会扫描这些 parked path 下面的子目录。

所以 park 的心智模型是:

1
2
我把一个工作区交给 Valet
工作区下每个子目录都是一个站点

link 更像是给当前目录起一个名字。

比如:

1
2
cd ~/code/some-long-project-name
valet link demo

它会创建类似这样的符号链接:

1
~/.config/valet/Sites/demo -> ~/code/some-long-project-name

之后:

1
demo.test

就对应这个真实项目目录。

源码里 Site::link() 做的事情也很直接:

1
2
3
确保 ~/.config/valet/Sites 存在
把 Sites 目录加入配置路径
创建一个符号链接

所以可以这样理解:

1
2
park 是目录扫描
link 是显式别名

这也是排查时很好用的判断。

如果是 park 出来的站点,要看父目录有没有在 config.jsonpaths 里。

如果是 link 出来的站点,要看 ~/.config/valet/Sites 下的符号链接是否存在、是否指向正确目录。

每次请求真正进入的是 server.php

现在 DNS 和 Nginx 都说完了,请求已经进入 Valet。

真正处理请求分发的是仓库根目录下的:

1
server.php

这段源码非常值得看,因为它基本把 Valet 的请求生命周期写清楚了。

它做的事情可以简化成这样:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$config = read_valet_config();

$uri = parse_request_uri();
$siteName = site_name_from_http_host($_SERVER['HTTP_HOST']);
$sitePath = find_site_path($siteName);

$driver = ValetDriver::assign($sitePath, $siteName, $uri);

if ($driver->isStaticFile(...)) {
return $driver->serveStaticFile(...);
}

$frontController = $driver->frontControllerPath(...);

require $frontController;

这里面有几个关键点。

第一个,siteName 来自 HTTP Host。

访问:

1
demo.test

Valet 会从 Host 里解析出:

1
demo

第二个,sitePath 来自 Valet 的站点映射。

也就是前面说的:

1
2
parked paths
linked sites

第三个,Valet 不会一上来就假设这是 Laravel 项目,而是会通过 driver 选择。

这一层是 Valet 能支持很多框架的关键。

driver 是 Valet 的框架适配层

源码里 ValetDriver.php 定义了几个关键方法:

1
2
3
serves()
isStaticFile()
frontControllerPath()

这三个方法基本就回答了三个问题:

1
2
3
这个 driver 能不能处理当前项目?
当前请求是不是静态文件?
如果不是静态文件,入口文件在哪里?

比如 Laravel 项目的 driver,也就是 LaravelValetDriver.php,判断逻辑可以概括成:

1
2
3
项目目录下有 public/index.php
并且项目目录下有 artisan
那就认为这是 Laravel 项目

这就很符合 Laravel 项目的基本结构。

对于静态文件,它会优先找:

1
2
项目目录/public/当前 URI
项目目录/storage/app/public/当前 URI

如果请求的是:

1
/css/app.css

并且真实文件存在:

1
public/css/app.css

那就直接返回静态文件。

如果请求的是:

1
/users/1

这显然不是一个真实静态文件,那就交给:

1
public/index.php

然后 Laravel 自己的路由系统再接着处理。

这就是 Valet driver 的意义。

Nginx 并不知道 Laravel、WordPress、Statamic、普通 PHP 项目的入口规则有什么区别。Valet 通过 driver 把这些规则封装起来。

所以 Valet 支持多框架的本质不是 Nginx 有多聪明,而是:

1
2
server.php 统一接管请求
driver 负责识别项目类型和入口文件

静态文件为什么不是 PHP 直接读出来

Valet 处理静态文件还有一个有意思的细节。

在 driver 判断某个请求是静态文件后,它不是简单地在 PHP 里 readfile()

Valet driver 会通过一个内部跳转机制,让 Nginx 去返回真实文件。

也就是类似:

1
2
3
4
5
6
7
PHP 判断这是静态文件
|
v
告诉 Nginx 这个文件在哪里
|
v
Nginx 直接返回文件

这么做的好处是:

1
2
PHP 只负责判断逻辑
真正传输文件这种事情交给 Nginx

这也符合 Web Server 和应用层的分工。

Nginx 擅长处理静态文件,PHP 擅长处理动态逻辑。

PHP-FPM 在链路里的位置

再说一下 PHP-FPM。

Nginx 自己不会执行 PHP。

当请求需要执行 server.php 或项目的 public/index.php 时,Nginx 会把请求交给 PHP-FPM。

Valet 里常见的通信方式是 Unix socket,比如:

1
~/.config/valet/valet.sock

可以粗略理解成:

1
2
3
Nginx 是前台
PHP-FPM 是后厨
server.php 和 Laravel index.php 是菜单上的具体菜

Nginx 接到请求后,如果需要执行 PHP,就把请求交给 PHP-FPM。

PHP-FPM 执行完 PHP 脚本,再把响应交回给 Nginx,最后返回给浏览器。

这也解释了为什么有时候 Valet 域名能解析、Nginx 也启动了,但页面仍然打不开,可能是 PHP-FPM 没起来,或者 socket 路径不对。

本地 HTTPS 是怎么来的

现在再看 HTTPS。

问题是:

1
为什么 https://demo.test 能有小锁?

公网 HTTPS 的常见逻辑是:

1
2
3
4
浏览器内置信任一批根 CA
网站证书由这些 CA 或它们的中间 CA 签发
浏览器验证证书链
验证通过后显示可信

本地 HTTPS 没法直接去公网 CA 申请 demo.test 的证书,因为它不是公网真实站点。

Valet 的思路是:

1
2
3
自己创建一个本地 CA
把这个本地 CA 加到 macOS 系统信任
再用这个本地 CA 给 demo.test 签发证书

也就是:

1
2
3
4
5
6
7
Laravel Valet 本地 CA
|
v
demo.test 站点证书
|
v
浏览器验证到系统信任的本地 CA,于是显示可信

这里最关键的一句话是:

1
浏览器信任的不是 demo.test 这个域名本身,而是它的证书链能追溯到系统信任的 CA。

所以 Valet 本地 HTTPS 的本质是:

1
2
3
本地生成证书
本地信任证书颁发者
本地 Nginx 使用这张证书

这个信任只在你的机器上成立。

换一台电脑,如果没有信任你这台机器上的 Valet CA,demo.test 的证书就不会被认为可信。

valet secure 在源码里做了什么

源码里 HTTPS 的主线在 Site.php

valet secure demo 最终会走到类似 Site::secure() 的逻辑。

它的主流程可以整理成:

1
2
3
4
5
6
保留当前站点可能存在的 PHP 版本隔离配置
确保本地 CA 存在
删除旧的站点证书和旧 Nginx 配置
创建 demo.test 的站点证书
生成 HTTPS Nginx server 配置
写入 ~/.config/valet/Nginx/demo.test

里面又可以拆成两个层次。

第一层:创建本地 CA

createCa() 负责创建本地 CA。

它会生成类似这些文件:

1
2
~/.config/valet/CA/LaravelValetCASelfSigned.pem
~/.config/valet/CA/LaravelValetCASelfSigned.key

然后通过 macOS 的 security 命令把这个 CA 加入系统钥匙串。

也就是告诉系统:

1
这个本地 CA 我信任

这是小锁能出现的根本原因。

如果这一步失败,比如钥匙串没有信任成功,证书就算生成了,浏览器也可能不认。

第二层:给具体站点签证书

createCertificate() 负责给具体域名生成证书。

比如:

1
2
3
4
demo.test.key
demo.test.csr
demo.test.crt
demo.test.conf

这些文件放在:

1
~/.config/valet/Certificates

流程大概是:

1
2
3
生成私钥
生成证书签名请求
用本地 CA 签出站点证书

所以证书关系是:

1
2
3
4
CA 私钥 + CA 证书
|
v
签发 demo.test 证书

这和公网证书的逻辑是类似的,只是 CA 从公网机构换成了你本机的本地 CA。

secure 之后 Nginx 多了什么

只生成证书还不够。

Nginx 还得知道:

1
2
3
4
5
demo.test 用哪张证书
demo.test 用哪把私钥
443 端口怎么监听
HTTP 要不要跳 HTTPS
请求最终仍然转给哪个入口

Site::buildSecureNginxServer() 会读取 Valet 的 HTTPS Nginx 模板,然后把占位符替换成真实值。

大概会替换这些东西:

1
2
3
4
5
VALET_SITE  -> demo.test
VALET_CERT -> ~/.config/valet/Certificates/demo.test.crt
VALET_KEY -> ~/.config/valet/Certificates/demo.test.key
VALET_HOME_PATH
VALET_SERVER_PATH

所以 valet secure demo 之后,Nginx 站点配置就会知道:

1
2
3
server_name demo.test
ssl_certificate demo.test.crt
ssl_certificate_key demo.test.key

同时它还会保留 Valet 的基本请求分发逻辑:

1
2
3
4
5
6
7
8
9
10
HTTPS 请求进来
|
v
Nginx 完成 TLS
|
v
请求继续交给 Valet server.php
|
v
server.php 找项目和 driver

也就是说,HTTPS 只是入口层多了一次证书和 TLS 处理,后面的项目分发逻辑并没有变。

SNI 在这里扮演什么角色

这里稍微提一下 SNI,但不展开太远。

本机可能同时有很多 HTTPS 站点:

1
2
3
demo.test
blog.test
api.test

它们都走:

1
127.0.0.1:443

那 Nginx 怎么知道应该拿哪张证书?

客户端在 TLS 握手时会带上要访问的域名,这就是 SNI。

Nginx 根据这个域名匹配对应的 server block,然后选择对应证书。

所以访问:

1
https://demo.test

Nginx 会用 demo.test 的证书。

访问:

1
https://blog.test

Nginx 会用 blog.test 的证书。

这也是为什么 server_name 和证书配置要对应起来。

proxy 又是怎么回事

Valet 还有一个很实用的功能:

1
valet proxy api http://127.0.0.1:3000

这个功能不是 PHP 项目映射,而是 Nginx 反向代理。

它的意思是:

1
2
3
4
5
6
7
api.test
|
v
Nginx
|
v
http://127.0.0.1:3000

源码里也在 Site.php,对应 proxyCreate() 这类逻辑。

它会生成一份 Nginx proxy 配置,把 api.test 的流量转发到指定地址。

如果加上 secure,本质就是:

1
2
3
给 api.test 生成本地证书
Nginx 负责 HTTPS
后端仍然代理到 127.0.0.1:3000

所以 Valet 不只能服务 Laravel 或 PHP 项目。对于 Node、Go、Docker 暴露出来的本地端口,Valet 也可以提供一个统一的本地域名和本地 HTTPS 入口。

从源码角度再串一次

现在把源码文件和职责再汇总一下。

1
Configuration.php

负责 Valet 自己的配置目录和 config.json

1
2
3
tld
loopback
paths
1
DnsMasq.php

负责 .test 这类本地域名后缀:

1
2
/etc/resolver/test
dnsmasq address=/.test/127.0.0.1
1
Nginx.php

负责安装和写入 Valet 的 Nginx 配置:

1
2
3
nginx.conf
valet.conf
~/.config/valet/Nginx
1
Site.php

负责站点层面的事情:

1
2
3
4
5
6
link
secure
unsecure
proxy
证书路径
Nginx 站点配置生成
1
server.php

是每次请求真正进入的 Valet 分发入口:

1
2
3
4
5
解析 Host
找到 site path
选择 driver
判断静态文件
加载 front controller
1
ValetDriver.php

定义项目识别和入口判断的抽象:

1
2
3
serves()
isStaticFile()
frontControllerPath()
1
LaravelValetDriver.php

是 Laravel 项目的具体规则:

1
2
3
4
有 artisan
有 public/index.php
静态文件从 public 或 storage/app/public 找
动态请求交给 public/index.php

这几个文件串起来,就能看到 Valet 的整体设计。

排查问题时可以顺着这条链路看

理解了链路之后,排查也会清楚很多。

1. 域名有没有解析到本机

可以看:

1
cat /etc/resolver/test

以及:

1
cat ~/.config/valet/dnsmasq.d/tld-test.conf

如果这里不对,问题通常还没到 Nginx。

2. Valet 配置里有没有这个路径

可以看:

1
cat ~/.config/valet/config.json

如果是 park,看 paths 里有没有父目录。

如果是 link,看:

1
ls -l ~/.config/valet/Sites

3. Nginx 配置有没有生成

可以看:

1
ls ~/.config/valet/Nginx

如果是 secure 或 proxy 站点,这里一般会有对应配置。

4. 证书是否存在

可以看:

1
ls ~/.config/valet/Certificates

也可以检查本地 CA:

1
ls ~/.config/valet/CA

5. PHP-FPM 是否正常

如果 Nginx 能接请求,但 PHP 执行异常,就要看 PHP-FPM 和 socket。

Valet 的 Nginx 配置会把 PHP 请求交给类似:

1
~/.config/valet/valet.sock

如果这个 socket 不存在,或者 PHP-FPM 没启动,就会出问题。

最后总结一下

Valet 的原理可以压缩成一句话:

Valet 用 dnsmasq 解决本地域名,用 Nginx 接住请求,用 server.php 和 driver 找到真正项目入口,用 PHP-FPM 执行 PHP,再用本地 CA 和 Nginx 配置补上 HTTPS。

它的好用之处不是某个单点技术特别神奇,而是把一堆本来要手工处理的事情自动化了:

1
2
3
4
5
不用每个项目手写 hosts
不用每个项目手写 Nginx 配置
不用自己维护 PHP-FPM socket
不用自己生成和信任本地证书
不用每个项目单独起一套容器或虚拟机

如果只看表面,Valet 好像只是让 demo.test 能访问。

但从源码和链路看,它其实是给本地开发搭了一套很轻量的“入口网关”:

1
2
3
4
5
6
所有本地域名都先进本机
所有请求都先进 Nginx
所有动态判断都进 Valet server.php
所有框架差异都交给 driver
所有 PHP 执行都交给 PHP-FPM
所有 HTTPS 信任都交给本地 CA

理解这一点之后,再看 valet parkvalet linkvalet securevalet proxy,就不太像魔法了。

它们只是分别在这条链路上改了一小段配置。

参考