You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

1 lines
86 KiB

<!doctypehtml><html lang=zh-CN><meta charset=UTF-8><meta content=width=device-width name=viewport><meta media="(prefers-color-scheme: light)" content=#222 name=theme-color><meta media="(prefers-color-scheme: dark)" content=#222 name=theme-color><meta content="Hexo 7.3.0" name=generator><link href=https://cdnjs.cloudflare.com rel=preconnect><link href=/images/apple-touch-icon-next.png rel=apple-touch-icon sizes=180x180><link href=/images/favicon-32x32-next.png rel=icon sizes=32x32 type=image/png><link href=/images/favicon-16x16-next.png rel=icon sizes=16x16 type=image/png><link color=#222 href=/images/logo.svg rel=mask-icon><meta content=2X6S9P7CAjXjVvw8YyQR8pCu-B0oEu7O9quLgxXuWyA name=google-site-verification><meta content=dV8JGNzi0c name=baidu-site-verification><script>!function(){var t=!1;function e(){var e;t||(t=!0,(e=document.createElement("script")).setAttribute("data-ad-client","ca-pub-7480618470784058"),e.async=!0,e.src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js",document.head.appendChild(e))}function a(){"requestIdleCallback"in window?window.requestIdleCallback(e,{timeout:5e3}):window.setTimeout(e,5e3)}"complete"===document.readyState?a():window.addEventListener("load",a,{once:!0})}()</script><link href=/css/main.css rel=stylesheet><link crossorigin=anonymous href=https://cdnjs.cloudflare.com/ajax/libs/font-awesome/7.0.0/css/all.min.css integrity=sha256-VHqXKFhhMxcpubYf9xiWdCiojEbY9NexQ4jh8AxbvcM= rel=stylesheet><link crossorigin=anonymous href=https://cdnjs.cloudflare.com/ajax/libs/fancyapps-ui/6.1.14/fancybox/fancybox.css integrity=sha256-mdaMxrItEW6hMIVJJNPKuX0B7KCSJQVEv3mVY4N2we4= rel=stylesheet><script class=next-config data-name=main type=application/json>{"hostname":"nicksxs.me","root":"/","images":"/images","scheme":"Pisces","darkmode":true,"version":"8.29.0","exturl":false,"sidebar":{"position":"left","width_expanded":320,"width_dual_column":240,"display":"post","padding":18,"offset":12},"hljswrap":true,"codeblock":{"theme":{"light":"default","dark":"stackoverflow-dark"},"prism":{"light":"prism","dark":"prism-dark"},"copy_button":{"enable":true,"style":"default"},"fold":{"enable":false,"height":500},"language":false},"bookmark":{"enable":false,"color":"#222","save":"auto"},"mediumzoom":false,"pangu":false,"comments":{"style":"tabs","active":null,"storage":true,"lazyload":true,"nav":null},"stickytabs":false,"motion":{"enable":false,"async":false,"duration":200,"transition":{"menu_item":"fadeInDown","post_block":"fadeIn","post_header":"fadeInDown","post_body":"fadeInDown","coll_header":"fadeInLeft","sidebar":"fadeInUp"}},"prism":false,"i18n":{"placeholder":"搜索...","empty":"没有找到任何搜索结果:${query}","hits_time":"找到 ${hits} 个搜索结果(用时 ${time} 毫秒)","hits":"找到 ${hits} 个搜索结果"},"algolia":{"appID":"663Q99OQQD","apiKey":"e461a1371d12cec1491c1153b288a9ed","indexName":"nicksxs","hits":{"per_page":10}}}</script><script defer src=/js/config.js></script><meta content=之前在用到valet的时候就觉得这个工具很厉害,因为本地部署很多时候都是比较费劲的,也比较简陋,就直接localhost启动下,但是有时候需要验一下回调的,就需要有域名跟https,当然之前也没想明白的是在本地其实用本地的地址也是可以的,意思就是如果回调地址没限制必须要域名,也… name=description><meta content=article property=og:type><meta content="从 app.test 到小锁:valet 本地 HTTPS 的完整链路" property=og:title><meta content=https://nicksxs.me/2026/06/28/%E4%BB%8E-app-test-%E5%88%B0%E5%B0%8F%E9%94%81%EF%BC%9A%E6%9C%AC%E5%9C%B0-HTTPS-%E7%9A%84%E5%AE%8C%E6%95%B4%E9%93%BE%E8%B7%AF/index.html property=og:url><meta content="Nicksxs's Blog" property=og:site_name><meta content=之前在用到valet的时候就觉得这个工具很厉害,因为本地部署很多时候都是比较费劲的,也比较简陋,就直接localhost启动下,但是有时候需要验一下回调的,就需要有域名跟https,当然之前也没想明白的是在本地其实用本地的地址也是可以的,意思就是如果回调地址没限制必须要域名,也… property=og:description><meta content=zh_CN property=og:locale><meta content=2026-06-28T14:18:00.000Z property=article:published_time><meta content=2026-06-28T14:18:00.000Z property=article:modified_time><meta content=Nicksxs property=article:author><meta content=nginx property=article:tag><meta content=php property=article:tag><meta content=valet property=article:tag><meta content=https property=article:tag><meta content=summary name=twitter:card><link href=https://nicksxs.me/2026/06/28/%E4%BB%8E-app-test-%E5%88%B0%E5%B0%8F%E9%94%81%EF%BC%9A%E6%9C%AC%E5%9C%B0-HTTPS-%E7%9A%84%E5%AE%8C%E6%95%B4%E9%93%BE%E8%B7%AF/ rel=canonical><script class=next-config data-name=page type=application/json>{"sidebar":"","isHome":false,"isPost":true,"lang":"zh-CN","comments":true,"permalink":"https://nicksxs.me/2026/06/28/%E4%BB%8E-app-test-%E5%88%B0%E5%B0%8F%E9%94%81%EF%BC%9A%E6%9C%AC%E5%9C%B0-HTTPS-%E7%9A%84%E5%AE%8C%E6%95%B4%E9%93%BE%E8%B7%AF/","path":"2026/06/28/从-app-test-到小锁:本地-HTTPS-的完整链路/","title":"从 app.test 到小锁:valet 本地 HTTPS 的完整链路"}</script><script class=next-config data-name=calendar type=application/json>""</script><title>从 app.test 到小锁:valet 本地 HTTPS 的完整链路 | Nicksxs's Blog</title><script async src=https://www.googletagmanager.com/gtag/js?id=UA-61358619-1></script><script class=next-config data-name=google_analytics type=application/json>{"tracking_id":"UA-61358619-1","only_pageview":false,"measure_protocol_api_secret":null}</script><script defer src=/js/third-party/analytics/google-analytics.js></script><script defer src=/js/third-party/analytics/baidu-analytics.js></script><script async src=https://hm.baidu.com/hm.js?20f33b3c0c0eff9b1522999c0015646d></script><script crossorigin=anonymous defer integrity=sha256-QnJV4HT5r8i89ovZGRD86iynZqHerEbT2xt69vDM36I= src=https://cdnjs.cloudflare.com/ajax/libs/fancyapps-ui/6.1.14/fancybox/fancybox.umd.js></script><script defer src=/js/utils.js></script><script defer src=/js/sidebar.js></script><script defer src=/js/next-boot.js></script><script data-algolia-integrity=sha256-o49zoHLDPBm9WLg6AiliAVi3axCRTqe3ltTxLvhzAVg= data-algolia-src=https://cdnjs.cloudflare.com/ajax/libs/algoliasearch/5.36.0/lite/builds/browser.umd.js data-search-src=/js/third-party/search/algolia-search.js src=/js/third-party/search/algolia-search-loader.js></script><script defer src=/js/third-party/fancybox.js></script><noscript><link href=/css/noscript.css rel=stylesheet></noscript><link title="Nicksxs's Blog" href=/atom.xml rel=alternate type=application/atom+xml><body itemscope itemtype=http://schema.org/WebPage><div class=headband></div><main class=main><div class=column><header class=header itemscope itemtype=http://schema.org/WPHeader><div class=site-brand-container><div class=site-nav-toggle><div aria-label=切换导航栏 class=toggle role=button><span class=toggle-line></span><span class=toggle-line></span><span class=toggle-line></span></div></div><div class=site-meta><a class=brand href=/ rel=start><i class=logo-line></i><p class=site-title>Nicksxs's Blog</p><i class=logo-line></i></a><p class=site-subtitle itemprop=description>Java 运行时、生产诊断与 AI 工程实践</div><div class=site-nav-right><div class="toggle popup-trigger" aria-label=搜索 role=button><i class="fa fa-search fa-fw fa-lg"></i></div></div></div><nav class=site-nav><ul class="main-menu menu"><li class="menu-item menu-item-home"><a href=/ rel=section><i class="fa fa-home fa-fw"></i>首页</a><li class="menu-item menu-item-专题"><a href=/topics/ rel=section><i class="fa fa-layer-group fa-fw"></i>专题</a><li class="menu-item menu-item-工具"><a href=/tools/ rel=section><i class="fa fa-toolbox fa-fw"></i>工具</a><li class="menu-item menu-item-about"><a href=/about/ rel=section><i class="fa fa-user fa-fw"></i>关于我</a><li class="menu-item menu-item-tags"><a href=/tags/ rel=section><i class="fa fa-tags fa-fw"></i>标签</a><li class="menu-item menu-item-categories"><a href=/categories/ rel=section><i class="fa fa-th fa-fw"></i>分类</a><li class="menu-item menu-item-archives"><a href=/archives/ rel=section><i class="fa fa-archive fa-fw"></i>归档</a><li class="menu-item menu-item-search"><a class=popup-trigger role=button><i class="fa fa-search fa-fw"></i>搜索</a></ul></nav><div class=search-pop-overlay><div class="popup search-popup"><div class=search-header><span class=search-icon><i class="fa fa-search"></i></span><div class=search-input-container><input autocapitalize=off autocomplete=off class=search-input maxlength=80 placeholder=搜索... spellcheck=false type=search></div><span class=popup-btn-close role=button><i class="fa fa-times-circle"></i></span></div><div class=search-result-container><div class=search-result-icon><i class="fab fa-algolia fa-5x"></i></div></div></div></div></header><aside class=sidebar><div class="sidebar-inner sidebar-nav-active sidebar-toc-active"><ul class=sidebar-nav><li class=sidebar-nav-toc>文章目录<li class=sidebar-nav-overview>站点概览</ul><div class=sidebar-panel-container><div class="post-toc-wrap sidebar-panel"><div class="post-toc animated"><ol class=nav><li class="nav-item nav-level-2"><a class=nav-link href=#%E5%85%88%E7%9C%8B%E5%AE%8C%E6%95%B4%E9%93%BE%E8%B7%AF><span class=nav-number>1.</span> <span class=nav-text>先看完整链路</span></a><li class="nav-item nav-level-2"><a class=nav-link href=#Valet-%E7%9A%84%E9%85%8D%E7%BD%AE%E4%B8%AD%E5%BF%83><span class=nav-number>2.</span> <span class=nav-text>Valet 的配置中心</span></a><li class="nav-item nav-level-2"><a class=nav-link href=#valet-install-%E5%A4%A7%E6%A6%82%E5%81%9A%E4%BA%86%E4%BB%80%E4%B9%88><span class=nav-number>3.</span> <span class=nav-text>valet install 大概做了什么</span></a><li class="nav-item nav-level-2"><a class=nav-link href=#demo-test-%E4%B8%BA%E4%BB%80%E4%B9%88%E4%BC%9A%E5%88%B0%E6%9C%AC%E6%9C%BA><span class=nav-number>4.</span> <span class=nav-text>demo.test 为什么会到本机</span></a><li class="nav-item nav-level-2"><a class=nav-link href=#%E8%AF%B7%E6%B1%82%E5%88%B0%E6%9C%AC%E6%9C%BA%E5%90%8E%E8%B0%81%E6%9D%A5%E6%8E%A5><span class=nav-number>5.</span> <span class=nav-text>请求到本机后谁来接</span></a><li class="nav-item nav-level-2"><a class=nav-link href=#park-%E5%92%8C-link-%E5%88%B0%E5%BA%95%E6%9C%89%E4%BB%80%E4%B9%88%E5%8C%BA%E5%88%AB><span class=nav-number>6.</span> <span class=nav-text>park 和 link 到底有什么区别</span></a><ol class=nav-child><li class="nav-item nav-level-3"><a class=nav-link href=#park-%E6%98%AF%E7%99%BB%E8%AE%B0%E4%B8%80%E4%B8%AA%E7%88%B6%E7%9B%AE%E5%BD%95><span class=nav-number>6.1.</span> <span class=nav-text>park 是登记一个父目录</span></a><li class="nav-item nav-level-3"><a class=nav-link href=#link-%E6%98%AF%E5%88%9B%E5%BB%BA%E4%B8%80%E4%B8%AA%E6%98%8E%E7%A1%AE%E6%98%A0%E5%B0%84><span class=nav-number>6.2.</span> <span class=nav-text>link 是创建一个明确映射</span></a></ol><li class="nav-item nav-level-2"><a class=nav-link href=#%E6%AF%8F%E6%AC%A1%E8%AF%B7%E6%B1%82%E7%9C%9F%E6%AD%A3%E8%BF%9B%E5%85%A5%E7%9A%84%E6%98%AF-server-php><span class=nav-number>7.</span> <span class=nav-text>每次请求真正进入的是 server.php</span></a><li class="nav-item nav-level-2"><a class=nav-link href=#driver-%E6%98%AF-Valet-%E7%9A%84%E6%A1%86%E6%9E%B6%E9%80%82%E9%85%8D%E5%B1%82><span class=nav-number>8.</span> <span class=nav-text>driver 是 Valet 的框架适配层</span></a><li class="nav-item nav-level-2"><a class=nav-link href=#%E9%9D%99%E6%80%81%E6%96%87%E4%BB%B6%E4%B8%BA%E4%BB%80%E4%B9%88%E4%B8%8D%E6%98%AF-PHP-%E7%9B%B4%E6%8E%A5%E8%AF%BB%E5%87%BA%E6%9D%A5><span class=nav-number>9.</span> <span class=nav-text>静态文件为什么不是 PHP 直接读出来</span></a><li class="nav-item nav-level-2"><a class=nav-link href=#PHP-FPM-%E5%9C%A8%E9%93%BE%E8%B7%AF%E9%87%8C%E7%9A%84%E4%BD%8D%E7%BD%AE><span class=nav-number>10.</span> <span class=nav-text>PHP-FPM 在链路里的位置</span></a><li class="nav-item nav-level-2"><a class=nav-link href=#%E6%9C%AC%E5%9C%B0-HTTPS-%E6%98%AF%E6%80%8E%E4%B9%88%E6%9D%A5%E7%9A%84><span class=nav-number>11.</span> <span class=nav-text>本地 HTTPS 是怎么来的</span></a><li class="nav-item nav-level-2"><a class=nav-link href=#valet-secure-%E5%9C%A8%E6%BA%90%E7%A0%81%E9%87%8C%E5%81%9A%E4%BA%86%E4%BB%80%E4%B9%88><span class=nav-number>12.</span> <span class=nav-text>valet secure 在源码里做了什么</span></a><ol class=nav-child><li class="nav-item nav-level-3"><a class=nav-link href=#%E7%AC%AC%E4%B8%80%E5%B1%82%EF%BC%9A%E5%88%9B%E5%BB%BA%E6%9C%AC%E5%9C%B0-CA><span class=nav-number>12.1.</span> <span class=nav-text>第一层:创建本地 CA</span></a><li class="nav-item nav-level-3"><a class=nav-link href=#%E7%AC%AC%E4%BA%8C%E5%B1%82%EF%BC%9A%E7%BB%99%E5%85%B7%E4%BD%93%E7%AB%99%E7%82%B9%E7%AD%BE%E8%AF%81%E4%B9%A6><span class=nav-number>12.2.</span> <span class=nav-text>第二层:给具体站点签证书</span></a></ol><li class="nav-item nav-level-2"><a class=nav-link href=#secure-%E4%B9%8B%E5%90%8E-Nginx-%E5%A4%9A%E4%BA%86%E4%BB%80%E4%B9%88><span class=nav-number>13.</span> <span class=nav-text>secure 之后 Nginx 多了什么</span></a><li class="nav-item nav-level-2"><a class=nav-link href=#SNI-%E5%9C%A8%E8%BF%99%E9%87%8C%E6%89%AE%E6%BC%94%E4%BB%80%E4%B9%88%E8%A7%92%E8%89%B2><span class=nav-number>14.</span> <span class=nav-text>SNI 在这里扮演什么角色</span></a><li class="nav-item nav-level-2"><a class=nav-link href=#proxy-%E5%8F%88%E6%98%AF%E6%80%8E%E4%B9%88%E5%9B%9E%E4%BA%8B><span class=nav-number>15.</span> <span class=nav-text>proxy 又是怎么回事</span></a><li class="nav-item nav-level-2"><a class=nav-link href=#%E4%BB%8E%E6%BA%90%E7%A0%81%E8%A7%92%E5%BA%A6%E5%86%8D%E4%B8%B2%E4%B8%80%E6%AC%A1><span class=nav-number>16.</span> <span class=nav-text>从源码角度再串一次</span></a><li class="nav-item nav-level-2"><a class=nav-link href=#%E6%8E%92%E6%9F%A5%E9%97%AE%E9%A2%98%E6%97%B6%E5%8F%AF%E4%BB%A5%E9%A1%BA%E7%9D%80%E8%BF%99%E6%9D%A1%E9%93%BE%E8%B7%AF%E7%9C%8B><span class=nav-number>17.</span> <span class=nav-text>排查问题时可以顺着这条链路看</span></a><ol class=nav-child><li class="nav-item nav-level-3"><a class=nav-link href=#1-%E5%9F%9F%E5%90%8D%E6%9C%89%E6%B2%A1%E6%9C%89%E8%A7%A3%E6%9E%90%E5%88%B0%E6%9C%AC%E6%9C%BA><span class=nav-number>17.1.</span> <span class=nav-text>1. 域名有没有解析到本机</span></a><li class="nav-item nav-level-3"><a class=nav-link href=#2-Valet-%E9%85%8D%E7%BD%AE%E9%87%8C%E6%9C%89%E6%B2%A1%E6%9C%89%E8%BF%99%E4%B8%AA%E8%B7%AF%E5%BE%84><span class=nav-number>17.2.</span> <span class=nav-text>2. Valet 配置里有没有这个路径</span></a><li class="nav-item nav-level-3"><a class=nav-link href=#3-Nginx-%E9%85%8D%E7%BD%AE%E6%9C%89%E6%B2%A1%E6%9C%89%E7%94%9F%E6%88%90><span class=nav-number>17.3.</span> <span class=nav-text>3. Nginx 配置有没有生成</span></a><li class="nav-item nav-level-3"><a class=nav-link href=#4-%E8%AF%81%E4%B9%A6%E6%98%AF%E5%90%A6%E5%AD%98%E5%9C%A8><span class=nav-number>17.4.</span> <span class=nav-text>4. 证书是否存在</span></a><li class="nav-item nav-level-3"><a class=nav-link href=#5-PHP-FPM-%E6%98%AF%E5%90%A6%E6%AD%A3%E5%B8%B8><span class=nav-number>17.5.</span> <span class=nav-text>5. PHP-FPM 是否正常</span></a></ol><li class="nav-item nav-level-2"><a class=nav-link href=#%E6%9C%80%E5%90%8E%E6%80%BB%E7%BB%93%E4%B8%80%E4%B8%8B><span class=nav-number>18.</span> <span class=nav-text>最后总结一下</span></a><li class="nav-item nav-level-2"><a class=nav-link href=#%E5%8F%82%E8%80%83><span class=nav-number>19.</span> <span class=nav-text>参考</span></a></ol></div></div><div class="site-overview-wrap sidebar-panel"><div class="site-author animated" itemprop=author itemscope itemtype=http://schema.org/Person><img alt=Nicksxs class=site-author-image itemprop=image src=/uploads/avatar.jpg><p class=site-author-name itemprop=name>Nicksxs<div class=site-description itemprop=description>Nicksxs 的技术博客,深入 Java 运行时与生产诊断、低成本大模型推理和 Agent 工程实践。</div></div><div class="site-state-wrap animated"><nav class=site-state><div class="site-state-item site-state-posts"><a href=/archives/><span class=site-state-item-count>394</span> <span class=site-state-item-name>日志</span></a></div><div class="site-state-item site-state-categories"><a href=/categories/><span class=site-state-item-count>181</span> <span class=site-state-item-name>分类</span></a></div><div class="site-state-item site-state-tags"><a href=/tags/><span class=site-state-item-count>347</span> <span class=site-state-item-name>标签</span></a></div></nav></div><div class="links-of-author animated"><span class=links-of-author-item><a rel="noopener me" title="GitHub → https://github.com/nicksxs" href=https://github.com/nicksxs target=_blank><i class="fab fa-github fa-fw"></i>GitHub</a> </span><span class=links-of-author-item><a rel="noopener me" title="E-Mail → mailto:nicksxs1202@gmail.com" href=mailto:nicksxs1202@gmail.com target=_blank><i class="fa fa-envelope fa-fw"></i>E-Mail</a></span></div><div class="cc-license animated" itemprop=license><a class=cc-opacity href=https://creativecommons.org/licenses/by-nc-sa/4.0/ rel=noopener target=_blank><img alt="Creative Commons" src=https://cdnjs.cloudflare.com/ajax/libs/creativecommons-vocabulary/2020.11.3/assets/license_badges/small/by_nc_sa.svg></a></div><div class="widget-wrap sidebar-tag-cloud"><div class=tag-cloud-tags><a class=tag-cloud-10 href=/tags/Java/ style=font-size:16px>Java</a><a class=tag-cloud-9 href=/tags/leetcode/ style=font-size:15.62px>leetcode</a><a class=tag-cloud-8 href=/tags/LLM/ style=font-size:15.23px>LLM</a><a class=tag-cloud-8 href=/tags/%E7%94%9F%E6%B4%BB/ style=font-size:14.85px>生活</a><a class=tag-cloud-7 href=/tags/%E9%A2%98%E8%A7%A3/ style=font-size:14.46px>题解</a><a class=tag-cloud-6 href=/tags/SpringBoot/ style=font-size:14.08px>SpringBoot</a><a class=tag-cloud-6 href=/tags/MySQL/ style=font-size:14.08px>MySQL</a><a class=tag-cloud-5 href=/tags/C/ style=font-size:13.69px>C++</a><a class=tag-cloud-5 href=/tags/Redis/ style=font-size:13.31px>Redis</a><a class=tag-cloud-4 href=/tags/Tomcat/ style=font-size:12.92px>Tomcat</a><a class=tag-cloud-4 href=/tags/Mybatis/ style=font-size:12.92px>Mybatis</a><a class=tag-cloud-3 href=/tags/%E5%B0%8F%E6%8A%80%E5%B7%A7/ style=font-size:12.54px>小技巧</a><a class=tag-cloud-2 href=/tags/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/ style=font-size:12.15px>数据结构</a><a class=tag-cloud-2 href=/tags/%E6%BA%90%E7%A0%81/ style=font-size:12.15px>源码</a><a class=tag-cloud-2 href=/tags/MQ/ style=font-size:11.77px>MQ</a><a class=tag-cloud-2 href=/tags/%E6%B6%88%E6%81%AF%E9%98%9F%E5%88%97/ style=font-size:11.77px>消息队列</a><a class=tag-cloud-2 href=/tags/RocketMQ/ style=font-size:11.77px>RocketMQ</a><a class=tag-cloud-2 href=/tags/%E8%BF%90%E5%8A%A8/ style=font-size:11.77px>运动</a><a class=tag-cloud-1 href=/tags/php/ style=font-size:11.38px>php</a><a class=tag-cloud-0 href=/tags/Spring/ style=font-size:11px>Spring</a><a class=tag-cloud-0 href=/tags/linux/ style=font-size:11px>linux</a><a class=tag-cloud-0 href=/tags/Docker/ style=font-size:11px>Docker</a><a class=tag-cloud-0 href=/tags/%E5%87%8F%E8%82%A5/ style=font-size:11px>减肥</a><a class=tag-cloud-0 href=/tags/%E8%B7%91%E6%AD%A5/ style=font-size:11px>跑步</a></div></div><div class="affiliate-creative affiliate-creative--sidebar" data-affiliate-id=tencent-cloud-general><a rel="sponsored noopener" aria-label=腾讯云产品优惠 href=https://cloud.tencent.com/act/cps/redirect?redirect=2446&cps_key=2e33a1eedb380997d03618b4a5ba6ca7&from=console target=_blank><img alt=腾讯云服务器、数据库和云产品优惠 decoding=async height=419 loading=lazy src=/uploads/affiliate/tencent-cloud-general.webp width=690> </a><span class=affiliate-disclosure>推广</span></div></div></div></div><div class="sidebar-inner sidebar-blogroll"><div class="links-of-blogroll animated"><div class=links-of-blogroll-title><i class="fa fa-globe fa-fw"></i> 链接</div><ul class=links-of-blogroll-list><li class=links-of-blogroll-item><a href=https://covermusic.cn/ rel=noopener target=_blank title=https://covermusic.cn>69伙伴</a></ul></div></div></aside></div><div class="main-inner post posts-expand"><div class=post-block><article class=post-content itemscope itemtype=http://schema.org/Article lang=zh-CN><link href=https://nicksxs.me/2026/06/28/%E4%BB%8E-app-test-%E5%88%B0%E5%B0%8F%E9%94%81%EF%BC%9A%E6%9C%AC%E5%9C%B0-HTTPS-%E7%9A%84%E5%AE%8C%E6%95%B4%E9%93%BE%E8%B7%AF/ itemprop=mainEntityOfPage><span hidden itemprop=author itemscope itemtype=http://schema.org/Person><meta content=/uploads/avatar.jpg itemprop=image><meta content=Nicksxs itemprop=name></span><span hidden itemprop=publisher itemscope itemtype=http://schema.org/Organization><meta content="Nicksxs's Blog" itemprop=name><meta content="Nicksxs 的技术博客,深入 Java 运行时与生产诊断、低成本大模型推理和 Agent 工程实践。" itemprop=description></span><span hidden itemprop=post itemscope itemtype=http://schema.org/CreativeWork><meta content="从 app.test 到小锁:valet 本地 HTTPS 的完整链路 | Nicksxs's Blog" itemprop=name><meta content=之前在用到valet的时候就觉得这个工具很厉害,因为本地部署很多时候都是比较费劲的,也比较简陋,就直接localhost启动下,但是有时候需要验一下回调的,就需要有域名跟https,当然之前也没想明白的是在本地其实用本地的地址也是可以的,意思就是如果回调地址没限制必须要域名,也… itemprop=description></span><header class=post-header><h1 itemprop="name headline" class=post-title>从 app.test 到小锁:valet 本地 HTTPS 的完整链路</h1><div class=post-meta-container><div class=post-meta><span class=post-meta-item><span class=post-meta-item-icon><i class="far fa-calendar"></i> </span><span class=post-meta-item-text>发表于</span> <time itemprop="dateCreated datePublished" title="创建时间:2026-06-28 22:18:00" datetime=2026-06-28T22:18:00+08:00>2026-06-28</time> </span><span class=post-meta-item><span class=post-meta-item-icon><i class="far fa-folder"></i> </span><span class=post-meta-item-text>分类于</span> <span itemprop=about itemscope itemtype=http://schema.org/Thing><a href=/categories/php/ itemprop=url rel=index><span itemprop=name>php</span></a> </span></span><span class="post-meta-item cloudflare-page-counter" data-title="从 app.test 到小锁:valet 本地 HTTPS 的完整链路" data-increment=true data-path=/2026/06/28/%E4%BB%8E-app-test-%E5%88%B0%E5%B0%8F%E9%94%81%EF%BC%9A%E6%9C%AC%E5%9C%B0-HTTPS-%E7%9A%84%E5%AE%8C%E6%95%B4%E9%93%BE%E8%B7%AF/ title=阅读次数><span class=post-meta-item-icon><i class="far fa-eye"></i> </span><span class=post-meta-item-text>阅读次数:</span> <span class=cloudflare-page-counter-count></span></span></div><div class=post-description>之前在用到valet的时候就觉得这个工具很厉害,因为本地部署很多时候都是比较费劲的,也比较简陋,就直接localhost启动下,但是有时候需要验一下回调的,就需要有域名跟https,当然之前也没想明白的是在本地其实用本地的地址也是可以的,意思就是如果回调地址没限制必须要域名,也…</div></div></header><div class=post-body itemprop=articleBody><p>之前在用到valet的时候就觉得这个工具很厉害,因为本地部署很多时候都是比较费劲的,也比较简陋,就直接localhost启动下,但是有时候需要验一下回调的,就需要有域名跟https,当然之前也没想明白的是在本地其实用本地的地址也是可以的,意思就是如果回调地址没限制必须要域名,也可以用127.0.0.1或者localhost,当然如果需要域名和https那就正好是valet的用武之地,所以想把 Valet 这类工具背后的逻辑捋一下。<p>平时用 Valet 的时候体验是非常顺的:<figure class="highlight shell"><table><tr><td class=gutter><pre><span class=line>1</span><br><span class=line>2</span><br></pre><td class=code><pre><span class=line>valet park</span><br><span class=line>valet secure demo</span><br></pre></table></figure><p>然后浏览器里访问:<figure class="highlight text"><table><tr><td class=gutter><pre><span class=line>1</span><br></pre><td class=code><pre><span class=line>https://demo.test</span><br></pre></table></figure><p>就能看到本地项目,而且还有 HTTPS 的小锁。<p>这个体验乍一看很像魔法。一个本地域名,既不用手动写 hosts,也不用自己配 Nginx,还不用去公网 CA 申请证书,怎么就能跑起来了呢?<p>其实拆开之后,Valet 并没有发明什么新协议,它只是把本地开发里几件比较麻烦的事情串起来了:<figure class="highlight text"><table><tr><td class=gutter><pre><span class=line>1</span><br><span class=line>2</span><br><span class=line>3</span><br><span class=line>4</span><br><span class=line>5</span><br><span class=line>6</span><br></pre><td class=code><pre><span class=line>本地域名解析</span><br><span class=line>Nginx 入口</span><br><span class=line>PHP-FPM 执行 PHP</span><br><span class=line>项目目录映射</span><br><span class=line>本地 CA 和站点证书</span><br><span class=line>框架 driver 判断入口文件</span><br></pre></table></figure><h2 id=先看完整链路><a class=headerlink href=#先看完整链路 title=先看完整链路></a>先看完整链路</h2><p>先放一个整体流程,后面再一段一段拆。<p>假设我们访问:<figure class="highlight text"><table><tr><td class=gutter><pre><span class=line>1</span><br></pre><td class=code><pre><span class=line>https://demo.test/users/1</span><br></pre></table></figure><p>整个过程大概是:<figure class="highlight text"><table><tr><td class=gutter><pre><span class=line>1</span><br><span class=line>2</span><br><span class=line>3</span><br><span class=line>4</span><br><span class=line>5</span><br><span class=line>6</span><br><span class=line>7</span><br><span class=line>8</span><br><span class=line>9</span><br><span class=line>10</span><br><span class=line>11</span><br><span class=line>12</span><br><span class=line>13</span><br><span class=line>14</span><br><span class=line>15</span><br><span class=line>16</span><br><span class=line>17</span><br><span class=line>18</span><br><span class=line>19</span><br><span class=line>20</span><br><span class=line>21</span><br><span class=line>22</span><br><span class=line>23</span><br><span class=line>24</span><br><span class=line>25</span><br><span class=line>26</span><br><span class=line>27</span><br><span class=line>28</span><br></pre><td class=code><pre><span class=line>浏览器访问 https://demo.test/users/1</span><br><span class=line> |</span><br><span class=line> v</span><br><span class=line>macOS 发现 .test 域名要走 /etc/resolver/test</span><br><span class=line> |</span><br><span class=line> v</span><br><span class=line>本地 dnsmasq 把 demo.test 解析到 127.0.0.1</span><br><span class=line> |</span><br><span class=line> v</span><br><span class=line>请求到达本机 443 端口</span><br><span class=line> |</span><br><span class=line> v</span><br><span class=line>Nginx 接收请求,并使用 demo.test 的证书完成 HTTPS</span><br><span class=line> |</span><br><span class=line> v</span><br><span class=line>Nginx 把请求交给 Valet 的 server.php</span><br><span class=line> |</span><br><span class=line> v</span><br><span class=line>server.php 根据 Host 找到 demo 对应的本地目录</span><br><span class=line> |</span><br><span class=line> v</span><br><span class=line>Valet driver 判断这是 Laravel 项目</span><br><span class=line> |</span><br><span class=line> v</span><br><span class=line>静态文件直接返回,非静态请求交给 public/index.php</span><br><span class=line> |</span><br><span class=line> v</span><br><span class=line>Laravel 应用开始处理路由</span><br></pre></table></figure><p>所以可以先得出一个粗略结论:<figure class="highlight text"><table><tr><td class=gutter><pre><span class=line>1</span><br><span class=line>2</span><br><span class=line>3</span><br><span class=line>4</span><br><span class=line>5</span><br><span class=line>6</span><br></pre><td class=code><pre><span class=line>DNS 负责把域名带到本机</span><br><span class=line>Nginx 负责接住 HTTP/HTTPS 请求</span><br><span class=line>server.php 负责把请求分发到具体项目</span><br><span class=line>driver 负责判断不同框架的入口规则</span><br><span class=line>PHP-FPM 负责真正执行 PHP</span><br><span class=line>本地 CA 负责让浏览器信任本地证书</span><br></pre></table></figure><p>这个结论很重要,因为很多时候我们排查 Valet 问题,就是在判断链路断在哪一层。<p>比如:<figure class="highlight text"><table><tr><td class=gutter><pre><span class=line>1</span><br><span class=line>2</span><br><span class=line>3</span><br><span class=line>4</span><br><span class=line>5</span><br></pre><td class=code><pre><span class=line>域名打不开,可能是 DNS/dnsmasq 问题</span><br><span class=line>80/443 端口不通,可能是 Nginx 问题</span><br><span class=line>项目找不到,可能是 park/link 映射问题</span><br><span class=line>PHP 报错,可能是 PHP-FPM 或项目本身问题</span><br><span class=line>HTTPS 不被信任,可能是证书或系统信任问题</span><br></pre></table></figure><h2 id=Valet-的配置中心><a title="Valet 的配置中心" class=headerlink href=#Valet-的配置中心></a>Valet 的配置中心</h2><p>源码里可以先看 <code>Configuration.php</code>。<p>Valet 会在用户目录下维护一个自己的配置目录:<figure class="highlight text"><table><tr><td class=gutter><pre><span class=line>1</span><br></pre><td class=code><pre><span class=line>~/.config/valet</span><br></pre></table></figure><p>这个目录下面会有几类东西:<figure class="highlight text"><table><tr><td class=gutter><pre><span class=line>1</span><br><span class=line>2</span><br><span class=line>3</span><br><span class=line>4</span><br><span class=line>5</span><br><span class=line>6</span><br><span class=line>7</span><br></pre><td class=code><pre><span class=line>~/.config/valet/config.json</span><br><span class=line>~/.config/valet/Sites</span><br><span class=line>~/.config/valet/Drivers</span><br><span class=line>~/.config/valet/Nginx</span><br><span class=line>~/.config/valet/Log</span><br><span class=line>~/.config/valet/Certificates</span><br><span class=line>~/.config/valet/CA</span><br></pre></table></figure><p>从源码看,<code>Configuration::install()</code> 做的事情就是创建这些基础目录,并确保基础配置存在。<p>其中最核心的是:<figure class="highlight text"><table><tr><td class=gutter><pre><span class=line>1</span><br></pre><td class=code><pre><span class=line>config.json</span><br></pre></table></figure><p>它大概长这样:<figure class="highlight json"><table><tr><td class=gutter><pre><span class=line>1</span><br><span class=line>2</span><br><span class=line>3</span><br><span class=line>4</span><br><span class=line>5</span><br></pre><td class=code><pre><span class=line><span class=punctuation>{</span></span><br><span class=line> <span class=attr>"tld"</span><span class=punctuation>:</span> <span class=string>"test"</span><span class=punctuation>,</span></span><br><span class=line> <span class=attr>"loopback"</span><span class=punctuation>:</span> <span class=string>"127.0.0.1"</span><span class=punctuation>,</span></span><br><span class=line> <span class=attr>"paths"</span><span class=punctuation>:</span> <span class=punctuation>[</span><span class=punctuation>]</span></span><br><span class=line><span class=punctuation>}</span></span><br></pre></table></figure><p>这几个字段分别代表:<figure class="highlight text"><table><tr><td class=gutter><pre><span class=line>1</span><br><span class=line>2</span><br><span class=line>3</span><br></pre><td class=code><pre><span class=line>tld 本地域名后缀,默认是 test</span><br><span class=line>loopback 本地回环地址,默认是 127.0.0.1</span><br><span class=line>paths 被 park 的目录列表</span><br></pre></table></figure><p>所以 Valet 的状态不是凭空来的,它有一个非常明确的落盘位置。<p>比如 <code>valet park</code> 之后,当前目录会被写到 <code>paths</code> 里。<p>比如 <code>valet link demo</code> 之后,<code>~/.config/valet/Sites/demo</code> 里会出现一个指向真实项目目录的符号链接。<p>比如 <code>valet secure demo</code> 之后,证书会进入:<figure class="highlight text"><table><tr><td class=gutter><pre><span class=line>1</span><br></pre><td class=code><pre><span class=line>~/.config/valet/Certificates</span><br></pre></table></figure><p>对应的 Nginx 站点配置会进入:<figure class="highlight text"><table><tr><td class=gutter><pre><span class=line>1</span><br></pre><td class=code><pre><span class=line>~/.config/valet/Nginx</span><br></pre></table></figure><p>理解这个目录很有用,因为后面很多问题都可以直接去这里看。<h2 id=valet-install-大概做了什么><a title="valet install 大概做了什么" class=headerlink href=#valet-install-大概做了什么></a>valet install 大概做了什么</h2><p>我们平时执行:<figure class="highlight shell"><table><tr><td class=gutter><pre><span class=line>1</span><br></pre><td class=code><pre><span class=line>valet install</span><br></pre></table></figure><p>它背后不是单纯装一个命令,而是在本机准备一整套本地 Web 开发环境。<p>从源码结构上看,大体可以分成几部分:<figure class="highlight text"><table><tr><td class=gutter><pre><span class=line>1</span><br><span class=line>2</span><br><span class=line>3</span><br><span class=line>4</span><br><span class=line>5</span><br></pre><td class=code><pre><span class=line>Configuration.php 创建 ~/.config/valet 相关目录和 config.json</span><br><span class=line>DnsMasq.php 安装和配置 dnsmasq</span><br><span class=line>Nginx.php 安装和配置 Nginx</span><br><span class=line>PhpFpm.php 配置 PHP-FPM 和 valet.sock</span><br><span class=line>Valet.php 处理 valet 命令本身的链接和 sudoers 等辅助事情</span><br></pre></table></figure><p>这里先不展开 PHP 版本管理和 sudoers 等细节,只看主链路。<p>Valet 安装完成后,本机基本会形成这么一个结构:<figure class="highlight text"><table><tr><td class=gutter><pre><span class=line>1</span><br><span class=line>2</span><br><span class=line>3</span><br><span class=line>4</span><br><span class=line>5</span><br><span class=line>6</span><br><span class=line>7</span><br><span class=line>8</span><br><span class=line>9</span><br><span class=line>10</span><br><span class=line>11</span><br><span class=line>12</span><br><span class=line>13</span><br><span class=line>14</span><br><span class=line>15</span><br><span class=line>16</span><br></pre><td class=code><pre><span class=line>macOS DNS</span><br><span class=line> |</span><br><span class=line> v</span><br><span class=line>dnsmasq</span><br><span class=line> |</span><br><span class=line> v</span><br><span class=line>127.0.0.1:80 / 127.0.0.1:443</span><br><span class=line> |</span><br><span class=line> v</span><br><span class=line>Nginx</span><br><span class=line> |</span><br><span class=line> v</span><br><span class=line>Valet server.php</span><br><span class=line> |</span><br><span class=line> v</span><br><span class=line>具体项目</span><br></pre></table></figure><p>也就是说,Valet 的轻量不是说它什么服务都没有,而是它复用了本机的 Nginx、PHP-FPM、dnsmasq,不需要为每个项目单独开一个虚拟机或容器。<h2 id=demo-test-为什么会到本机><a title="demo.test 为什么会到本机" class=headerlink href=#demo-test-为什么会到本机></a>demo.test 为什么会到本机</h2><p>先看第一个问题:<figure class="highlight text"><table><tr><td class=gutter><pre><span class=line>1</span><br></pre><td class=code><pre><span class=line>demo.test 怎么就指向 127.0.0.1 了?</span><br></pre></table></figure><p>如果不用 Valet,我们最容易想到的方法是改 <code>/etc/hosts</code>:<figure class="highlight text"><table><tr><td class=gutter><pre><span class=line>1</span><br></pre><td class=code><pre><span class=line>127.0.0.1 demo.test</span><br></pre></table></figure><p>但这样有个问题:每加一个项目,就要写一行。<p>Valet 不这么做。它用的是 <code>dnsmasq</code> 加 macOS resolver。<p>源码里对应的是 <code>DnsMasq.php</code>。<p><code>DnsMasq::install()</code> 的流程大概是:<figure class="highlight text"><table><tr><td class=gutter><pre><span class=line>1</span><br><span class=line>2</span><br><span class=line>3</span><br><span class=line>4</span><br><span class=line>5</span><br></pre><td class=code><pre><span class=line>确保 dnsmasq 已安装</span><br><span class=line>让 dnsmasq 加载额外配置目录</span><br><span class=line>创建 .test 后缀对应的 dnsmasq 配置</span><br><span class=line>创建 /etc/resolver/test</span><br><span class=line>重启 dnsmasq</span><br></pre></table></figure><p>这里有两个关键文件。<p>第一个是类似这样的 dnsmasq 配置:<figure class="highlight text"><table><tr><td class=gutter><pre><span class=line>1</span><br><span class=line>2</span><br><span class=line>3</span><br></pre><td class=code><pre><span class=line>address=/.test/127.0.0.1</span><br><span class=line>address=/.test/::1</span><br><span class=line>listen-address=127.0.0.1</span><br></pre></table></figure><p>它的意思是:<figure class="highlight text"><table><tr><td class=gutter><pre><span class=line>1</span><br></pre><td class=code><pre><span class=line>所有 .test 结尾的域名,都解析到本机</span><br></pre></table></figure><p>所以这些域名都可以成立:<figure class="highlight text"><table><tr><td class=gutter><pre><span class=line>1</span><br><span class=line>2</span><br><span class=line>3</span><br><span class=line>4</span><br></pre><td class=code><pre><span class=line>demo.test</span><br><span class=line>blog.test</span><br><span class=line>api.test</span><br><span class=line>anything.test</span><br></pre></table></figure><p>第二个是:<figure class="highlight text"><table><tr><td class=gutter><pre><span class=line>1</span><br></pre><td class=code><pre><span class=line>/etc/resolver/test</span><br></pre></table></figure><p>内容大概是:<figure class="highlight text"><table><tr><td class=gutter><pre><span class=line>1</span><br></pre><td class=code><pre><span class=line>nameserver 127.0.0.1</span><br></pre></table></figure><p>这个文件告诉 macOS:<figure class="highlight text"><table><tr><td class=gutter><pre><span class=line>1</span><br></pre><td class=code><pre><span class=line>遇到 .test 这个后缀,不要走普通 DNS,交给 127.0.0.1 上的 DNS 服务处理</span><br></pre></table></figure><p>然后本地的 dnsmasq 再把它解析回 127.0.0.1。<p>这里要注意一件事:DNS 这一层只解决了“域名到 IP”的问题。<p>也就是说:<figure class="highlight text"><table><tr><td class=gutter><pre><span class=line>1</span><br><span class=line>2</span><br><span class=line>3</span><br></pre><td class=code><pre><span class=line>demo.test -> 127.0.0.1</span><br><span class=line>blog.test -> 127.0.0.1</span><br><span class=line>api.test -> 127.0.0.1</span><br></pre></table></figure><p>但是 DNS 并不知道:<figure class="highlight text"><table><tr><td class=gutter><pre><span class=line>1</span><br><span class=line>2</span><br></pre><td class=code><pre><span class=line>demo.test 对应哪个项目目录</span><br><span class=line>blog.test 对应哪个项目目录</span><br></pre></table></figure><p>这个问题要留给后面的 Nginx 和 Valet 自己处理。<h2 id=请求到本机后谁来接><a class=headerlink href=#请求到本机后谁来接 title=请求到本机后谁来接></a>请求到本机后谁来接</h2><p>域名解析到 127.0.0.1 之后,浏览器会根据协议和端口发请求。<p>如果是 HTTP:<figure class="highlight text"><table><tr><td class=gutter><pre><span class=line>1</span><br></pre><td class=code><pre><span class=line>http://demo.test -> 127.0.0.1:80</span><br></pre></table></figure><p>如果是 HTTPS:<figure class="highlight text"><table><tr><td class=gutter><pre><span class=line>1</span><br></pre><td class=code><pre><span class=line>https://demo.test -> 127.0.0.1:443</span><br></pre></table></figure><p>这时接请求的是 Nginx。<p>源码里对应的是 <code>Nginx.php</code>。<p><code>Nginx::install()</code> 大致做几件事:<figure class="highlight text"><table><tr><td class=gutter><pre><span class=line>1</span><br><span class=line>2</span><br><span class=line>3</span><br><span class=line>4</span><br></pre><td class=code><pre><span class=line>确保 Nginx 已安装</span><br><span class=line>写入 nginx.conf</span><br><span class=line>写入 Valet 的 valet.conf</span><br><span class=line>创建 ~/.config/valet/Nginx 目录</span><br></pre></table></figure><p>这里比较关键的是 Valet 的 Nginx 不是简单地给某一个项目写死 root。<p>它会准备一个统一入口,把请求转给 Valet 自己的 <code>server.php</code>。<p>可以粗略理解为:<figure class="highlight text"><table><tr><td class=gutter><pre><span class=line>1</span><br><span class=line>2</span><br><span class=line>3</span><br><span class=line>4</span><br><span class=line>5</span><br><span class=line>6</span><br><span class=line>7</span><br></pre><td class=code><pre><span class=line>Nginx 接住所有本地 .test 请求</span><br><span class=line> |</span><br><span class=line> v</span><br><span class=line>统一转给 Valet server.php</span><br><span class=line> |</span><br><span class=line> v</span><br><span class=line>server.php 再决定这个请求属于哪个项目</span><br></pre></table></figure><p>这一点是 Valet 设计里比较关键的地方。<p>如果完全靠 Nginx,每个项目都要生成一个完整的 server block:<figure class="highlight text"><table><tr><td class=gutter><pre><span class=line>1</span><br><span class=line>2</span><br></pre><td class=code><pre><span class=line>demo.test -> /Users/me/code/demo/public</span><br><span class=line>blog.test -> /Users/me/code/blog/public</span><br></pre></table></figure><p>Valet 当然也会为 HTTPS、proxy、PHP 隔离这类特殊场景生成站点配置,但普通场景下,它更核心的思路是:<figure class="highlight text"><table><tr><td class=gutter><pre><span class=line>1</span><br><span class=line>2</span><br></pre><td class=code><pre><span class=line>Nginx 做入口</span><br><span class=line>PHP 层的 server.php 做动态分发</span><br></pre></table></figure><p>这样它才能做到 <code>valet park</code> 之后,目录下面新增一个项目,不需要手工改 Nginx 配置也能访问。<h2 id=park-和-link-到底有什么区别><a title="park 和 link 到底有什么区别" class=headerlink href=#park-和-link-到底有什么区别></a>park 和 link 到底有什么区别</h2><p>接着看项目目录映射。<p>Valet 常用两个命令:<figure class="highlight shell"><table><tr><td class=gutter><pre><span class=line>1</span><br><span class=line>2</span><br></pre><td class=code><pre><span class=line>valet park</span><br><span class=line>valet link</span><br></pre></table></figure><p>它们都能让本地域名对应到项目,但思路不一样。<h3 id=park-是登记一个父目录><a title="park 是登记一个父目录" class=headerlink href=#park-是登记一个父目录></a>park 是登记一个父目录</h3><p>假设我们在这个目录执行:<figure class="highlight shell"><table><tr><td class=gutter><pre><span class=line>1</span><br><span class=line>2</span><br></pre><td class=code><pre><span class=line>cd ~/Sites</span><br><span class=line>valet park</span><br></pre></table></figure><p>然后目录结构是:<figure class="highlight text"><table><tr><td class=gutter><pre><span class=line>1</span><br><span class=line>2</span><br><span class=line>3</span><br></pre><td class=code><pre><span class=line>~/Sites/demo</span><br><span class=line>~/Sites/blog</span><br><span class=line>~/Sites/shop</span><br></pre></table></figure><p>Valet 就可以让这些域名工作:<figure class="highlight text"><table><tr><td class=gutter><pre><span class=line>1</span><br><span class=line>2</span><br><span class=line>3</span><br></pre><td class=code><pre><span class=line>demo.test -> ~/Sites/demo</span><br><span class=line>blog.test -> ~/Sites/blog</span><br><span class=line>shop.test -> ~/Sites/shop</span><br></pre></table></figure><p>从配置角度看,<code>park</code> 主要就是把 <code>~/Sites</code> 这个路径写入 <code>config.json</code> 的 <code>paths</code>。<p>之后 Valet 查找站点时,会扫描这些 parked path 下面的子目录。<p>所以 <code>park</code> 的心智模型是:<figure class="highlight text"><table><tr><td class=gutter><pre><span class=line>1</span><br><span class=line>2</span><br></pre><td class=code><pre><span class=line>我把一个工作区交给 Valet</span><br><span class=line>工作区下每个子目录都是一个站点</span><br></pre></table></figure><h3 id=link-是创建一个明确映射><a title="link 是创建一个明确映射" class=headerlink href=#link-是创建一个明确映射></a>link 是创建一个明确映射</h3><p><code>link</code> 更像是给当前目录起一个名字。<p>比如:<figure class="highlight shell"><table><tr><td class=gutter><pre><span class=line>1</span><br><span class=line>2</span><br></pre><td class=code><pre><span class=line>cd ~/code/some-long-project-name</span><br><span class=line>valet link demo</span><br></pre></table></figure><p>它会创建类似这样的符号链接:<figure class="highlight text"><table><tr><td class=gutter><pre><span class=line>1</span><br></pre><td class=code><pre><span class=line>~/.config/valet/Sites/demo -> ~/code/some-long-project-name</span><br></pre></table></figure><p>之后:<figure class="highlight text"><table><tr><td class=gutter><pre><span class=line>1</span><br></pre><td class=code><pre><span class=line>demo.test</span><br></pre></table></figure><p>就对应这个真实项目目录。<p>源码里 <code>Site::link()</code> 做的事情也很直接:<figure class="highlight text"><table><tr><td class=gutter><pre><span class=line>1</span><br><span class=line>2</span><br><span class=line>3</span><br></pre><td class=code><pre><span class=line>确保 ~/.config/valet/Sites 存在</span><br><span class=line>把 Sites 目录加入配置路径</span><br><span class=line>创建一个符号链接</span><br></pre></table></figure><p>所以可以这样理解:<figure class="highlight text"><table><tr><td class=gutter><pre><span class=line>1</span><br><span class=line>2</span><br></pre><td class=code><pre><span class=line>park 是目录扫描</span><br><span class=line>link 是显式别名</span><br></pre></table></figure><p>这也是排查时很好用的判断。<p>如果是 <code>park</code> 出来的站点,要看父目录有没有在 <code>config.json</code> 的 <code>paths</code> 里。<p>如果是 <code>link</code> 出来的站点,要看 <code>~/.config/valet/Sites</code> 下的符号链接是否存在、是否指向正确目录。<h2 id=每次请求真正进入的是-server-php><a title="每次请求真正进入的是 server.php" class=headerlink href=#每次请求真正进入的是-server-php></a>每次请求真正进入的是 server.php</h2><p>现在 DNS 和 Nginx 都说完了,请求已经进入 Valet。<p>真正处理请求分发的是仓库根目录下的:<figure class="highlight text"><table><tr><td class=gutter><pre><span class=line>1</span><br></pre><td class=code><pre><span class=line>server.php</span><br></pre></table></figure><p>这段源码非常值得看,因为它基本把 Valet 的请求生命周期写清楚了。<p>它做的事情可以简化成这样:<figure class="highlight php"><table><tr><td class=gutter><pre><span class=line>1</span><br><span class=line>2</span><br><span class=line>3</span><br><span class=line>4</span><br><span class=line>5</span><br><span class=line>6</span><br><span class=line>7</span><br><span class=line>8</span><br><span class=line>9</span><br><span class=line>10</span><br><span class=line>11</span><br><span class=line>12</span><br><span class=line>13</span><br><span class=line>14</span><br><span class=line>15</span><br></pre><td class=code><pre><span class=line><span class=variable>$config</span> = <span class="title function_ invoke__">read_valet_config</span>();</span><br><span class=line></span><br><span class=line><span class=variable>$uri</span> = <span class="title function_ invoke__">parse_request_uri</span>();</span><br><span class=line><span class=variable>$siteName</span> = <span class="title function_ invoke__">site_name_from_http_host</span>(<span class=variable>$_SERVER</span>[<span class=string>'HTTP_HOST'</span>]);</span><br><span class=line><span class=variable>$sitePath</span> = <span class="title function_ invoke__">find_site_path</span>(<span class=variable>$siteName</span>);</span><br><span class=line></span><br><span class=line><span class=variable>$driver</span> = <span class="title class_">ValetDriver</span>::<span class="title function_ invoke__">assign</span>(<span class=variable>$sitePath</span>, <span class=variable>$siteName</span>, <span class=variable>$uri</span>);</span><br><span class=line></span><br><span class=line><span class=keyword>if</span> (<span class=variable>$driver</span>-><span class="title function_ invoke__">isStaticFile</span>(...)) {</span><br><span class=line> <span class=keyword>return</span> <span class=variable>$driver</span>-><span class="title function_ invoke__">serveStaticFile</span>(...);</span><br><span class=line>}</span><br><span class=line></span><br><span class=line><span class=variable>$frontController</span> = <span class=variable>$driver</span>-><span class="title function_ invoke__">frontControllerPath</span>(...);</span><br><span class=line></span><br><span class=line><span class=keyword>require</span> <span class=variable>$frontController</span>;</span><br></pre></table></figure><p>这里面有几个关键点。<p>第一个,<code>siteName</code> 来自 HTTP Host。<p>访问:<figure class="highlight text"><table><tr><td class=gutter><pre><span class=line>1</span><br></pre><td class=code><pre><span class=line>demo.test</span><br></pre></table></figure><p>Valet 会从 Host 里解析出:<figure class="highlight text"><table><tr><td class=gutter><pre><span class=line>1</span><br></pre><td class=code><pre><span class=line>demo</span><br></pre></table></figure><p>第二个,<code>sitePath</code> 来自 Valet 的站点映射。<p>也就是前面说的:<figure class="highlight text"><table><tr><td class=gutter><pre><span class=line>1</span><br><span class=line>2</span><br></pre><td class=code><pre><span class=line>parked paths</span><br><span class=line>linked sites</span><br></pre></table></figure><p>第三个,Valet 不会一上来就假设这是 Laravel 项目,而是会通过 driver 选择。<p>这一层是 Valet 能支持很多框架的关键。<h2 id=driver-是-Valet-的框架适配层><a title="driver 是 Valet 的框架适配层" class=headerlink href=#driver-是-Valet-的框架适配层></a>driver 是 Valet 的框架适配层</h2><p>源码里 <code>ValetDriver.php</code> 定义了几个关键方法:<figure class="highlight text"><table><tr><td class=gutter><pre><span class=line>1</span><br><span class=line>2</span><br><span class=line>3</span><br></pre><td class=code><pre><span class=line>serves()</span><br><span class=line>isStaticFile()</span><br><span class=line>frontControllerPath()</span><br></pre></table></figure><p>这三个方法基本就回答了三个问题:<figure class="highlight text"><table><tr><td class=gutter><pre><span class=line>1</span><br><span class=line>2</span><br><span class=line>3</span><br></pre><td class=code><pre><span class=line>这个 driver 能不能处理当前项目?</span><br><span class=line>当前请求是不是静态文件?</span><br><span class=line>如果不是静态文件,入口文件在哪里?</span><br></pre></table></figure><p>比如 Laravel 项目的 driver,也就是 <code>LaravelValetDriver.php</code>,判断逻辑可以概括成:<figure class="highlight text"><table><tr><td class=gutter><pre><span class=line>1</span><br><span class=line>2</span><br><span class=line>3</span><br></pre><td class=code><pre><span class=line>项目目录下有 public/index.php</span><br><span class=line>并且项目目录下有 artisan</span><br><span class=line>那就认为这是 Laravel 项目</span><br></pre></table></figure><p>这就很符合 Laravel 项目的基本结构。<p>对于静态文件,它会优先找:<figure class="highlight text"><table><tr><td class=gutter><pre><span class=line>1</span><br><span class=line>2</span><br></pre><td class=code><pre><span class=line>项目目录/public/当前 URI</span><br><span class=line>项目目录/storage/app/public/当前 URI</span><br></pre></table></figure><p>如果请求的是:<figure class="highlight text"><table><tr><td class=gutter><pre><span class=line>1</span><br></pre><td class=code><pre><span class=line>/css/app.css</span><br></pre></table></figure><p>并且真实文件存在:<figure class="highlight text"><table><tr><td class=gutter><pre><span class=line>1</span><br></pre><td class=code><pre><span class=line>public/css/app.css</span><br></pre></table></figure><p>那就直接返回静态文件。<p>如果请求的是:<figure class="highlight text"><table><tr><td class=gutter><pre><span class=line>1</span><br></pre><td class=code><pre><span class=line>/users/1</span><br></pre></table></figure><p>这显然不是一个真实静态文件,那就交给:<figure class="highlight text"><table><tr><td class=gutter><pre><span class=line>1</span><br></pre><td class=code><pre><span class=line>public/index.php</span><br></pre></table></figure><p>然后 Laravel 自己的路由系统再接着处理。<p>这就是 Valet driver 的意义。<p>Nginx 并不知道 Laravel、WordPress、Statamic、普通 PHP 项目的入口规则有什么区别。Valet 通过 driver 把这些规则封装起来。<p>所以 Valet 支持多框架的本质不是 Nginx 有多聪明,而是:<figure class="highlight text"><table><tr><td class=gutter><pre><span class=line>1</span><br><span class=line>2</span><br></pre><td class=code><pre><span class=line>server.php 统一接管请求</span><br><span class=line>driver 负责识别项目类型和入口文件</span><br></pre></table></figure><h2 id=静态文件为什么不是-PHP-直接读出来><a title="静态文件为什么不是 PHP 直接读出来" class=headerlink href=#静态文件为什么不是-PHP-直接读出来></a>静态文件为什么不是 PHP 直接读出来</h2><p>Valet 处理静态文件还有一个有意思的细节。<p>在 driver 判断某个请求是静态文件后,它不是简单地在 PHP 里 <code>readfile()</code>。<p>Valet driver 会通过一个内部跳转机制,让 Nginx 去返回真实文件。<p>也就是类似:<figure class="highlight text"><table><tr><td class=gutter><pre><span class=line>1</span><br><span class=line>2</span><br><span class=line>3</span><br><span class=line>4</span><br><span class=line>5</span><br><span class=line>6</span><br><span class=line>7</span><br></pre><td class=code><pre><span class=line>PHP 判断这是静态文件</span><br><span class=line> |</span><br><span class=line> v</span><br><span class=line>告诉 Nginx 这个文件在哪里</span><br><span class=line> |</span><br><span class=line> v</span><br><span class=line>Nginx 直接返回文件</span><br></pre></table></figure><p>这么做的好处是:<figure class="highlight text"><table><tr><td class=gutter><pre><span class=line>1</span><br><span class=line>2</span><br></pre><td class=code><pre><span class=line>PHP 只负责判断逻辑</span><br><span class=line>真正传输文件这种事情交给 Nginx</span><br></pre></table></figure><p>这也符合 Web Server 和应用层的分工。<p>Nginx 擅长处理静态文件,PHP 擅长处理动态逻辑。<h2 id=PHP-FPM-在链路里的位置><a title="PHP-FPM 在链路里的位置" class=headerlink href=#PHP-FPM-在链路里的位置></a>PHP-FPM 在链路里的位置</h2><p>再说一下 PHP-FPM。<p>Nginx 自己不会执行 PHP。<p>当请求需要执行 <code>server.php</code> 或项目的 <code>public/index.php</code> 时,Nginx 会把请求交给 PHP-FPM。<p>Valet 里常见的通信方式是 Unix socket,比如:<figure class="highlight text"><table><tr><td class=gutter><pre><span class=line>1</span><br></pre><td class=code><pre><span class=line>~/.config/valet/valet.sock</span><br></pre></table></figure><p>可以粗略理解成:<figure class="highlight text"><table><tr><td class=gutter><pre><span class=line>1</span><br><span class=line>2</span><br><span class=line>3</span><br></pre><td class=code><pre><span class=line>Nginx 是前台</span><br><span class=line>PHP-FPM 是后厨</span><br><span class=line>server.php 和 Laravel index.php 是菜单上的具体菜</span><br></pre></table></figure><p>Nginx 接到请求后,如果需要执行 PHP,就把请求交给 PHP-FPM。<p>PHP-FPM 执行完 PHP 脚本,再把响应交回给 Nginx,最后返回给浏览器。<p>这也解释了为什么有时候 Valet 域名能解析、Nginx 也启动了,但页面仍然打不开,可能是 PHP-FPM 没起来,或者 socket 路径不对。<h2 id=本地-HTTPS-是怎么来的><a title="本地 HTTPS 是怎么来的" class=headerlink href=#本地-HTTPS-是怎么来的></a>本地 HTTPS 是怎么来的</h2><p>现在再看 HTTPS。<p>问题是:<figure class="highlight text"><table><tr><td class=gutter><pre><span class=line>1</span><br></pre><td class=code><pre><span class=line>为什么 https://demo.test 能有小锁?</span><br></pre></table></figure><p>公网 HTTPS 的常见逻辑是:<figure class="highlight text"><table><tr><td class=gutter><pre><span class=line>1</span><br><span class=line>2</span><br><span class=line>3</span><br><span class=line>4</span><br></pre><td class=code><pre><span class=line>浏览器内置信任一批根 CA</span><br><span class=line>网站证书由这些 CA 或它们的中间 CA 签发</span><br><span class=line>浏览器验证证书链</span><br><span class=line>验证通过后显示可信</span><br></pre></table></figure><p>本地 HTTPS 没法直接去公网 CA 申请 <code>demo.test</code> 的证书,因为它不是公网真实站点。<p>Valet 的思路是:<figure class="highlight text"><table><tr><td class=gutter><pre><span class=line>1</span><br><span class=line>2</span><br><span class=line>3</span><br></pre><td class=code><pre><span class=line>自己创建一个本地 CA</span><br><span class=line>把这个本地 CA 加到 macOS 系统信任</span><br><span class=line>再用这个本地 CA 给 demo.test 签发证书</span><br></pre></table></figure><p>也就是:<figure class="highlight text"><table><tr><td class=gutter><pre><span class=line>1</span><br><span class=line>2</span><br><span class=line>3</span><br><span class=line>4</span><br><span class=line>5</span><br><span class=line>6</span><br><span class=line>7</span><br></pre><td class=code><pre><span class=line>Laravel Valet 本地 CA</span><br><span class=line> |</span><br><span class=line> v</span><br><span class=line>demo.test 站点证书</span><br><span class=line> |</span><br><span class=line> v</span><br><span class=line>浏览器验证到系统信任的本地 CA,于是显示可信</span><br></pre></table></figure><p>这里最关键的一句话是:<figure class="highlight text"><table><tr><td class=gutter><pre><span class=line>1</span><br></pre><td class=code><pre><span class=line>浏览器信任的不是 demo.test 这个域名本身,而是它的证书链能追溯到系统信任的 CA。</span><br></pre></table></figure><p>所以 Valet 本地 HTTPS 的本质是:<figure class="highlight text"><table><tr><td class=gutter><pre><span class=line>1</span><br><span class=line>2</span><br><span class=line>3</span><br></pre><td class=code><pre><span class=line>本地生成证书</span><br><span class=line>本地信任证书颁发者</span><br><span class=line>本地 Nginx 使用这张证书</span><br></pre></table></figure><p>这个信任只在你的机器上成立。<p>换一台电脑,如果没有信任你这台机器上的 Valet CA,<code>demo.test</code> 的证书就不会被认为可信。<h2 id=valet-secure-在源码里做了什么><a title="valet secure 在源码里做了什么" class=headerlink href=#valet-secure-在源码里做了什么></a>valet secure 在源码里做了什么</h2><p>源码里 HTTPS 的主线在 <code>Site.php</code>。<p><code>valet secure demo</code> 最终会走到类似 <code>Site::secure()</code> 的逻辑。<p>它的主流程可以整理成:<figure class="highlight text"><table><tr><td class=gutter><pre><span class=line>1</span><br><span class=line>2</span><br><span class=line>3</span><br><span class=line>4</span><br><span class=line>5</span><br><span class=line>6</span><br></pre><td class=code><pre><span class=line>保留当前站点可能存在的 PHP 版本隔离配置</span><br><span class=line>确保本地 CA 存在</span><br><span class=line>删除旧的站点证书和旧 Nginx 配置</span><br><span class=line>创建 demo.test 的站点证书</span><br><span class=line>生成 HTTPS Nginx server 配置</span><br><span class=line>写入 ~/.config/valet/Nginx/demo.test</span><br></pre></table></figure><p>里面又可以拆成两个层次。<h3 id=第一层:创建本地-CA><a title="第一层:创建本地 CA" class=headerlink href=#第一层:创建本地-CA></a>第一层:创建本地 CA</h3><p><code>createCa()</code> 负责创建本地 CA。<p>它会生成类似这些文件:<figure class="highlight text"><table><tr><td class=gutter><pre><span class=line>1</span><br><span class=line>2</span><br></pre><td class=code><pre><span class=line>~/.config/valet/CA/LaravelValetCASelfSigned.pem</span><br><span class=line>~/.config/valet/CA/LaravelValetCASelfSigned.key</span><br></pre></table></figure><p>然后通过 macOS 的 <code>security</code> 命令把这个 CA 加入系统钥匙串。<p>也就是告诉系统:<figure class="highlight text"><table><tr><td class=gutter><pre><span class=line>1</span><br></pre><td class=code><pre><span class=line>这个本地 CA 我信任</span><br></pre></table></figure><p>这是小锁能出现的根本原因。<p>如果这一步失败,比如钥匙串没有信任成功,证书就算生成了,浏览器也可能不认。<h3 id=第二层:给具体站点签证书><a class=headerlink href=#第二层:给具体站点签证书 title=第二层:给具体站点签证书></a>第二层:给具体站点签证书</h3><p><code>createCertificate()</code> 负责给具体域名生成证书。<p>比如:<figure class="highlight text"><table><tr><td class=gutter><pre><span class=line>1</span><br><span class=line>2</span><br><span class=line>3</span><br><span class=line>4</span><br></pre><td class=code><pre><span class=line>demo.test.key</span><br><span class=line>demo.test.csr</span><br><span class=line>demo.test.crt</span><br><span class=line>demo.test.conf</span><br></pre></table></figure><p>这些文件放在:<figure class="highlight text"><table><tr><td class=gutter><pre><span class=line>1</span><br></pre><td class=code><pre><span class=line>~/.config/valet/Certificates</span><br></pre></table></figure><p>流程大概是:<figure class="highlight text"><table><tr><td class=gutter><pre><span class=line>1</span><br><span class=line>2</span><br><span class=line>3</span><br></pre><td class=code><pre><span class=line>生成私钥</span><br><span class=line>生成证书签名请求</span><br><span class=line>用本地 CA 签出站点证书</span><br></pre></table></figure><p>所以证书关系是:<figure class="highlight text"><table><tr><td class=gutter><pre><span class=line>1</span><br><span class=line>2</span><br><span class=line>3</span><br><span class=line>4</span><br></pre><td class=code><pre><span class=line>CA 私钥 + CA 证书</span><br><span class=line> |</span><br><span class=line> v</span><br><span class=line>签发 demo.test 证书</span><br></pre></table></figure><p>这和公网证书的逻辑是类似的,只是 CA 从公网机构换成了你本机的本地 CA。<h2 id=secure-之后-Nginx-多了什么><a title="secure 之后 Nginx 多了什么" class=headerlink href=#secure-之后-Nginx-多了什么></a>secure 之后 Nginx 多了什么</h2><p>只生成证书还不够。<p>Nginx 还得知道:<figure class="highlight text"><table><tr><td class=gutter><pre><span class=line>1</span><br><span class=line>2</span><br><span class=line>3</span><br><span class=line>4</span><br><span class=line>5</span><br></pre><td class=code><pre><span class=line>demo.test 用哪张证书</span><br><span class=line>demo.test 用哪把私钥</span><br><span class=line>443 端口怎么监听</span><br><span class=line>HTTP 要不要跳 HTTPS</span><br><span class=line>请求最终仍然转给哪个入口</span><br></pre></table></figure><p><code>Site::buildSecureNginxServer()</code> 会读取 Valet 的 HTTPS Nginx 模板,然后把占位符替换成真实值。<p>大概会替换这些东西:<figure class="highlight text"><table><tr><td class=gutter><pre><span class=line>1</span><br><span class=line>2</span><br><span class=line>3</span><br><span class=line>4</span><br><span class=line>5</span><br></pre><td class=code><pre><span class=line>VALET_SITE -> demo.test</span><br><span class=line>VALET_CERT -> ~/.config/valet/Certificates/demo.test.crt</span><br><span class=line>VALET_KEY -> ~/.config/valet/Certificates/demo.test.key</span><br><span class=line>VALET_HOME_PATH</span><br><span class=line>VALET_SERVER_PATH</span><br></pre></table></figure><p>所以 <code>valet secure demo</code> 之后,Nginx 站点配置就会知道:<figure class="highlight text"><table><tr><td class=gutter><pre><span class=line>1</span><br><span class=line>2</span><br><span class=line>3</span><br></pre><td class=code><pre><span class=line>server_name demo.test</span><br><span class=line>ssl_certificate demo.test.crt</span><br><span class=line>ssl_certificate_key demo.test.key</span><br></pre></table></figure><p>同时它还会保留 Valet 的基本请求分发逻辑:<figure class="highlight text"><table><tr><td class=gutter><pre><span class=line>1</span><br><span class=line>2</span><br><span class=line>3</span><br><span class=line>4</span><br><span class=line>5</span><br><span class=line>6</span><br><span class=line>7</span><br><span class=line>8</span><br><span class=line>9</span><br><span class=line>10</span><br></pre><td class=code><pre><span class=line>HTTPS 请求进来</span><br><span class=line> |</span><br><span class=line> v</span><br><span class=line>Nginx 完成 TLS</span><br><span class=line> |</span><br><span class=line> v</span><br><span class=line>请求继续交给 Valet server.php</span><br><span class=line> |</span><br><span class=line> v</span><br><span class=line>server.php 找项目和 driver</span><br></pre></table></figure><p>也就是说,HTTPS 只是入口层多了一次证书和 TLS 处理,后面的项目分发逻辑并没有变。<h2 id=SNI-在这里扮演什么角色><a title="SNI 在这里扮演什么角色" class=headerlink href=#SNI-在这里扮演什么角色></a>SNI 在这里扮演什么角色</h2><p>这里稍微提一下 SNI,但不展开太远。<p>本机可能同时有很多 HTTPS 站点:<figure class="highlight text"><table><tr><td class=gutter><pre><span class=line>1</span><br><span class=line>2</span><br><span class=line>3</span><br></pre><td class=code><pre><span class=line>demo.test</span><br><span class=line>blog.test</span><br><span class=line>api.test</span><br></pre></table></figure><p>它们都走:<figure class="highlight text"><table><tr><td class=gutter><pre><span class=line>1</span><br></pre><td class=code><pre><span class=line>127.0.0.1:443</span><br></pre></table></figure><p>那 Nginx 怎么知道应该拿哪张证书?<p>客户端在 TLS 握手时会带上要访问的域名,这就是 SNI。<p>Nginx 根据这个域名匹配对应的 server block,然后选择对应证书。<p>所以访问:<figure class="highlight text"><table><tr><td class=gutter><pre><span class=line>1</span><br></pre><td class=code><pre><span class=line>https://demo.test</span><br></pre></table></figure><p>Nginx 会用 <code>demo.test</code> 的证书。<p>访问:<figure class="highlight text"><table><tr><td class=gutter><pre><span class=line>1</span><br></pre><td class=code><pre><span class=line>https://blog.test</span><br></pre></table></figure><p>Nginx 会用 <code>blog.test</code> 的证书。<p>这也是为什么 <code>server_name</code> 和证书配置要对应起来。<h2 id=proxy-又是怎么回事><a title="proxy 又是怎么回事" class=headerlink href=#proxy-又是怎么回事></a>proxy 又是怎么回事</h2><p>Valet 还有一个很实用的功能:<figure class="highlight shell"><table><tr><td class=gutter><pre><span class=line>1</span><br></pre><td class=code><pre><span class=line>valet proxy api http://127.0.0.1:3000</span><br></pre></table></figure><p>这个功能不是 PHP 项目映射,而是 Nginx 反向代理。<p>它的意思是:<figure class="highlight text"><table><tr><td class=gutter><pre><span class=line>1</span><br><span class=line>2</span><br><span class=line>3</span><br><span class=line>4</span><br><span class=line>5</span><br><span class=line>6</span><br><span class=line>7</span><br></pre><td class=code><pre><span class=line>api.test</span><br><span class=line> |</span><br><span class=line> v</span><br><span class=line>Nginx</span><br><span class=line> |</span><br><span class=line> v</span><br><span class=line>http://127.0.0.1:3000</span><br></pre></table></figure><p>源码里也在 <code>Site.php</code>,对应 <code>proxyCreate()</code> 这类逻辑。<p>它会生成一份 Nginx proxy 配置,把 <code>api.test</code> 的流量转发到指定地址。<p>如果加上 secure,本质就是:<figure class="highlight text"><table><tr><td class=gutter><pre><span class=line>1</span><br><span class=line>2</span><br><span class=line>3</span><br></pre><td class=code><pre><span class=line>给 api.test 生成本地证书</span><br><span class=line>Nginx 负责 HTTPS</span><br><span class=line>后端仍然代理到 127.0.0.1:3000</span><br></pre></table></figure><p>所以 Valet 不只能服务 Laravel 或 PHP 项目。对于 Node、Go、Docker 暴露出来的本地端口,Valet 也可以提供一个统一的本地域名和本地 HTTPS 入口。<h2 id=从源码角度再串一次><a class=headerlink href=#从源码角度再串一次 title=从源码角度再串一次></a>从源码角度再串一次</h2><p>现在把源码文件和职责再汇总一下。<figure class="highlight text"><table><tr><td class=gutter><pre><span class=line>1</span><br></pre><td class=code><pre><span class=line>Configuration.php</span><br></pre></table></figure><p>负责 Valet 自己的配置目录和 <code>config.json</code>:<figure class="highlight text"><table><tr><td class=gutter><pre><span class=line>1</span><br><span class=line>2</span><br><span class=line>3</span><br></pre><td class=code><pre><span class=line>tld</span><br><span class=line>loopback</span><br><span class=line>paths</span><br></pre></table></figure><figure class="highlight text"><table><tr><td class=gutter><pre><span class=line>1</span><br></pre><td class=code><pre><span class=line>DnsMasq.php</span><br></pre></table></figure><p>负责 <code>.test</code> 这类本地域名后缀:<figure class="highlight text"><table><tr><td class=gutter><pre><span class=line>1</span><br><span class=line>2</span><br></pre><td class=code><pre><span class=line>/etc/resolver/test</span><br><span class=line>dnsmasq address=/.test/127.0.0.1</span><br></pre></table></figure><figure class="highlight text"><table><tr><td class=gutter><pre><span class=line>1</span><br></pre><td class=code><pre><span class=line>Nginx.php</span><br></pre></table></figure><p>负责安装和写入 Valet 的 Nginx 配置:<figure class="highlight text"><table><tr><td class=gutter><pre><span class=line>1</span><br><span class=line>2</span><br><span class=line>3</span><br></pre><td class=code><pre><span class=line>nginx.conf</span><br><span class=line>valet.conf</span><br><span class=line>~/.config/valet/Nginx</span><br></pre></table></figure><figure class="highlight text"><table><tr><td class=gutter><pre><span class=line>1</span><br></pre><td class=code><pre><span class=line>Site.php</span><br></pre></table></figure><p>负责站点层面的事情:<figure class="highlight text"><table><tr><td class=gutter><pre><span class=line>1</span><br><span class=line>2</span><br><span class=line>3</span><br><span class=line>4</span><br><span class=line>5</span><br><span class=line>6</span><br></pre><td class=code><pre><span class=line>link</span><br><span class=line>secure</span><br><span class=line>unsecure</span><br><span class=line>proxy</span><br><span class=line>证书路径</span><br><span class=line>Nginx 站点配置生成</span><br></pre></table></figure><figure class="highlight text"><table><tr><td class=gutter><pre><span class=line>1</span><br></pre><td class=code><pre><span class=line>server.php</span><br></pre></table></figure><p>是每次请求真正进入的 Valet 分发入口:<figure class="highlight text"><table><tr><td class=gutter><pre><span class=line>1</span><br><span class=line>2</span><br><span class=line>3</span><br><span class=line>4</span><br><span class=line>5</span><br></pre><td class=code><pre><span class=line>解析 Host</span><br><span class=line>找到 site path</span><br><span class=line>选择 driver</span><br><span class=line>判断静态文件</span><br><span class=line>加载 front controller</span><br></pre></table></figure><figure class="highlight text"><table><tr><td class=gutter><pre><span class=line>1</span><br></pre><td class=code><pre><span class=line>ValetDriver.php</span><br></pre></table></figure><p>定义项目识别和入口判断的抽象:<figure class="highlight text"><table><tr><td class=gutter><pre><span class=line>1</span><br><span class=line>2</span><br><span class=line>3</span><br></pre><td class=code><pre><span class=line>serves()</span><br><span class=line>isStaticFile()</span><br><span class=line>frontControllerPath()</span><br></pre></table></figure><figure class="highlight text"><table><tr><td class=gutter><pre><span class=line>1</span><br></pre><td class=code><pre><span class=line>LaravelValetDriver.php</span><br></pre></table></figure><p>是 Laravel 项目的具体规则:<figure class="highlight text"><table><tr><td class=gutter><pre><span class=line>1</span><br><span class=line>2</span><br><span class=line>3</span><br><span class=line>4</span><br></pre><td class=code><pre><span class=line>有 artisan</span><br><span class=line>有 public/index.php</span><br><span class=line>静态文件从 public 或 storage/app/public 找</span><br><span class=line>动态请求交给 public/index.php</span><br></pre></table></figure><p>这几个文件串起来,就能看到 Valet 的整体设计。<h2 id=排查问题时可以顺着这条链路看><a class=headerlink href=#排查问题时可以顺着这条链路看 title=排查问题时可以顺着这条链路看></a>排查问题时可以顺着这条链路看</h2><p>理解了链路之后,排查也会清楚很多。<h3 id=1-域名有没有解析到本机><a title="1. 域名有没有解析到本机" class=headerlink href=#1-域名有没有解析到本机></a>1. 域名有没有解析到本机</h3><p>可以看:<figure class="highlight shell"><table><tr><td class=gutter><pre><span class=line>1</span><br></pre><td class=code><pre><span class=line>cat /etc/resolver/test</span><br></pre></table></figure><p>以及:<figure class="highlight shell"><table><tr><td class=gutter><pre><span class=line>1</span><br></pre><td class=code><pre><span class=line>cat ~/.config/valet/dnsmasq.d/tld-test.conf</span><br></pre></table></figure><p>如果这里不对,问题通常还没到 Nginx。<h3 id=2-Valet-配置里有没有这个路径><a title="2. Valet 配置里有没有这个路径" class=headerlink href=#2-Valet-配置里有没有这个路径></a>2. Valet 配置里有没有这个路径</h3><p>可以看:<figure class="highlight shell"><table><tr><td class=gutter><pre><span class=line>1</span><br></pre><td class=code><pre><span class=line>cat ~/.config/valet/config.json</span><br></pre></table></figure><p>如果是 <code>park</code>,看 <code>paths</code> 里有没有父目录。<p>如果是 <code>link</code>,看:<figure class="highlight shell"><table><tr><td class=gutter><pre><span class=line>1</span><br></pre><td class=code><pre><span class=line>ls -l ~/.config/valet/Sites</span><br></pre></table></figure><h3 id=3-Nginx-配置有没有生成><a title="3. Nginx 配置有没有生成" class=headerlink href=#3-Nginx-配置有没有生成></a>3. Nginx 配置有没有生成</h3><p>可以看:<figure class="highlight shell"><table><tr><td class=gutter><pre><span class=line>1</span><br></pre><td class=code><pre><span class=line>ls ~/.config/valet/Nginx</span><br></pre></table></figure><p>如果是 secure 或 proxy 站点,这里一般会有对应配置。<h3 id=4-证书是否存在><a title="4. 证书是否存在" class=headerlink href=#4-证书是否存在></a>4. 证书是否存在</h3><p>可以看:<figure class="highlight shell"><table><tr><td class=gutter><pre><span class=line>1</span><br></pre><td class=code><pre><span class=line>ls ~/.config/valet/Certificates</span><br></pre></table></figure><p>也可以检查本地 CA:<figure class="highlight shell"><table><tr><td class=gutter><pre><span class=line>1</span><br></pre><td class=code><pre><span class=line>ls ~/.config/valet/CA</span><br></pre></table></figure><h3 id=5-PHP-FPM-是否正常><a title="5. PHP-FPM 是否正常" class=headerlink href=#5-PHP-FPM-是否正常></a>5. PHP-FPM 是否正常</h3><p>如果 Nginx 能接请求,但 PHP 执行异常,就要看 PHP-FPM 和 socket。<p>Valet 的 Nginx 配置会把 PHP 请求交给类似:<figure class="highlight text"><table><tr><td class=gutter><pre><span class=line>1</span><br></pre><td class=code><pre><span class=line>~/.config/valet/valet.sock</span><br></pre></table></figure><p>如果这个 socket 不存在,或者 PHP-FPM 没启动,就会出问题。<h2 id=最后总结一下><a class=headerlink href=#最后总结一下 title=最后总结一下></a>最后总结一下</h2><p>Valet 的原理可以压缩成一句话:<blockquote><p>Valet 用 dnsmasq 解决本地域名,用 Nginx 接住请求,用 server.php 和 driver 找到真正项目入口,用 PHP-FPM 执行 PHP,再用本地 CA 和 Nginx 配置补上 HTTPS。</blockquote><p>它的好用之处不是某个单点技术特别神奇,而是把一堆本来要手工处理的事情自动化了:<figure class="highlight text"><table><tr><td class=gutter><pre><span class=line>1</span><br><span class=line>2</span><br><span class=line>3</span><br><span class=line>4</span><br><span class=line>5</span><br></pre><td class=code><pre><span class=line>不用每个项目手写 hosts</span><br><span class=line>不用每个项目手写 Nginx 配置</span><br><span class=line>不用自己维护 PHP-FPM socket</span><br><span class=line>不用自己生成和信任本地证书</span><br><span class=line>不用每个项目单独起一套容器或虚拟机</span><br></pre></table></figure><p>如果只看表面,Valet 好像只是让 <code>demo.test</code> 能访问。<p>但从源码和链路看,它其实是给本地开发搭了一套很轻量的“入口网关”:<figure class="highlight text"><table><tr><td class=gutter><pre><span class=line>1</span><br><span class=line>2</span><br><span class=line>3</span><br><span class=line>4</span><br><span class=line>5</span><br><span class=line>6</span><br></pre><td class=code><pre><span class=line>所有本地域名都先进本机</span><br><span class=line>所有请求都先进 Nginx</span><br><span class=line>所有动态判断都进 Valet server.php</span><br><span class=line>所有框架差异都交给 driver</span><br><span class=line>所有 PHP 执行都交给 PHP-FPM</span><br><span class=line>所有 HTTPS 信任都交给本地 CA</span><br></pre></table></figure><p>理解这一点之后,再看 <code>valet park</code>、<code>valet link</code>、<code>valet secure</code>、<code>valet proxy</code>,就不太像魔法了。<p>它们只是分别在这条链路上改了一小段配置。<h2 id=参考><a class=headerlink href=#参考 title=参考></a>参考</h2><ul><li><a href=https://laravel.com/docs/12.x/valet rel=noopener target=_blank>Laravel Valet 官方文档</a><li><a href=https://github.com/laravel/valet rel=noopener target=_blank>laravel/valet GitHub 仓库</a><li><a href=https://github.com/laravel/valet/blob/master/server.php rel=noopener target=_blank>server.php</a><li><a href=https://github.com/laravel/valet/blob/master/cli/Valet/Site.php rel=noopener target=_blank>Site.php</a><li><a href=https://github.com/laravel/valet/blob/master/cli/Valet/DnsMasq.php rel=noopener target=_blank>DnsMasq.php</a><li><a href=https://github.com/laravel/valet/blob/master/cli/Valet/Nginx.php rel=noopener target=_blank>Nginx.php</a><li><a href=https://github.com/laravel/valet/blob/master/cli/Valet/Drivers/ValetDriver.php rel=noopener target=_blank>ValetDriver.php</a></ul></div><footer class=post-footer><div class="affiliate-creative affiliate-creative--post-end" data-affiliate-id=tencent-cloud-general><a rel="sponsored noopener" aria-label=腾讯云产品优惠 href=https://cloud.tencent.com/act/cps/redirect?redirect=2446&cps_key=2e33a1eedb380997d03618b4a5ba6ca7&from=console target=_blank><img alt=腾讯云服务器、数据库和云产品优惠 decoding=async height=419 loading=lazy src=/uploads/affiliate/tencent-cloud-general.webp width=690> </a><span class=affiliate-disclosure>推广</span></div><div class=reward-container><div>请我一杯咖啡吧!</div><button>赞赏</button><div class=post-reward><div><img alt="Nicksxs 微信" src=https://i.loli.net/2020/01/12/NUlWanT31E8LQGx.png><span>微信</span></div><div><img alt="Nicksxs 支付宝" src=https://ooo.0o0.ooo/2016/08/17/57b421e773057.jpg><span>支付宝</span></div></div></div><div class=post-copyright><ul><li class=post-copyright-author><strong>本文作者: </strong>Nicksxs<li class=post-copyright-link><strong>本文链接:</strong> <a title="从 app.test 到小锁:valet 本地 HTTPS 的完整链路" href=https://nicksxs.me/2026/06/28/%E4%BB%8E-app-test-%E5%88%B0%E5%B0%8F%E9%94%81%EF%BC%9A%E6%9C%AC%E5%9C%B0-HTTPS-%E7%9A%84%E5%AE%8C%E6%95%B4%E9%93%BE%E8%B7%AF/>https://nicksxs.me/2026/06/28/从-app-test-到小锁:本地-HTTPS-的完整链路/</a><li class=post-copyright-license><strong>版权声明: </strong>本博客所有文章除特别声明外,均采用 <a href=https://creativecommons.org/licenses/by-nc-sa/4.0/ rel=noopener target=_blank><i class="fab fa-fw fa-creative-commons"></i>BY-NC-SA</a> 许可协议。转载请注明出处!</ul></div><div class=post-tags><a href=/tags/nginx/ rel=tag># nginx</a><a href=/tags/php/ rel=tag># php</a><a href=/tags/valet/ rel=tag># valet</a><a href=/tags/https/ rel=tag># https</a></div><div class=post-nav><div class=post-nav-item><a title="JPEG 图片格式详解:压缩原理、DCT、量化与编码流程" href=/2026/06/21/%E6%B5%85%E6%9E%90%E4%B8%80%E4%B8%8B%E5%90%84%E7%B1%BB%E5%9B%BE%E7%89%87%E6%A0%BC%E5%BC%8F%E5%8F%8A%E5%85%B6%E6%9D%A5%E6%BA%90/ rel=prev><i class="fa fa-angle-left"></i> JPEG 图片格式详解:压缩原理、DCT、量化与编码流程</a></div><div class=post-nav-item><a href=/2026/07/05/%E7%9C%8B%E4%B8%8Bchrome%E7%9A%84%E5%86%85%E7%BD%AE%E6%A8%A1%E5%9E%8B/ rel=next title=看下chrome的内置模型>看下chrome的内置模型 <i class="fa fa-angle-right"></i></a></div></div></footer></article></div><div class=comments id=disqus_thread><noscript>Please enable JavaScript to view the comments powered by Disqus.</noscript></div></div></main><footer class=footer><div class=footer-inner><div class=beian><a href=https://beian.miit.gov.cn/ rel=noopener target=_blank>浙ICP备16002580号-2 </a><img alt src=/uploads/beian.png><a href=https://beian.mps.gov.cn/#/query/webSearch?code=33010602011094 rel=noopener target=_blank>浙公网安备 33010602011094号</a></div><div class=copyright>© <span itemprop=copyrightYear>2026</span><span class=with-love><i class="fa fa-heart"></i> </span><span class=author itemprop=copyrightHolder>Nicksxs</span></div></div></footer><div class="toggle sidebar-toggle" role=button><span class=toggle-line></span><span class=toggle-line></span><span class=toggle-line></span></div><div class=sidebar-dimmer></div><div aria-label=返回顶部 class=back-to-top role=button><i class="fa fa-arrow-up fa-lg"></i><span>0%</span></div><a aria-label="在 GitHub 上关注我" title="在 GitHub 上关注我" class=github-corner href=https://github.com/nicksxs rel=noopener target=_blank><svg viewbox="0 0 250 250" aria-hidden=true height=80 width=80><path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z"></path><path d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2" style="transform-origin:130px 106px" class=octo-arm fill=currentColor></path><path d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z" class=octo-body fill=currentColor></path></svg></a><noscript><div class=noscript-warning>Theme NexT works best with JavaScript enabled</div></noscript><script data-cloudflare-page-counter-config type=application/json>{"enable":true,"endpoint":"https://counter.nicksxs.me","site":"nicksxs","local_cache":true,"timeout":5000,"icon":"far fa-eye","item_text":true}</script><script src=/js/cloudflare-page-counter.js></script><script class=next-config data-name=disqus type=application/json>{"enable":true,"shortname":"nicksxs","count":false,"i18n":{"disqus":"disqus"}}</script><script defer src=/js/third-party/comments/disqus.js></script>