Skip to content

内网环境手机 HTTPS 访问本地前端项目实践指南 —— 为真机调试摄像头采集

更新: 6/12/2026 字数: 0 字 时长: 0 分钟

💡 一句话目标: 让同一 WiFi 下的手机用 https:// 访问电脑上的本地前端项目,从而在真机上调试摄像头采集。核心方案:mkcert 签发内网受信任证书 + Caddy 反向代理套一层 HTTPS

方案总览:内网环境手机 HTTPS 访问本地前端,调试摄像头采集

一、背景:为什么非要折腾 HTTPS

开发涉及摄像头采集(扫码、人脸、活体检测、AR 等)的前端功能时,本地写完代码必须在手机真机上验证——桌面与手机浏览器的摄像头行为、权限弹窗、分辨率差异很大,很多问题只在真机复现。

而手机访问电脑上的本地项目,绕不开一个硬约束:

⚠️ navigator.mediaDevices.getUserMedia 属于「安全上下文」(Secure Context) API,浏览器仅在 https://localhost 下放行。

手机通过局域网 IP(如 http://192.168.1.100:8080)访问时既不是 https,也不是 localhost,于是 navigator.mediaDevices 直接为 undefined,摄像头根本调不起来。所以核心目标只有一句话:让手机能用 https 访问到电脑上的本地前端项目。

二、整体方案

整个链路分两层:mkcert 负责生成「内网受信任」证书让手机不报红,Caddy 作为反向代理在前端 dev server(HTTP)前面套一层 HTTPS。

text
手机浏览器
  │  https://192.168.1.x:3000   (加密,证书受信任)

Caddy 反向代理(持有 mkcert 证书,负责 TLS)
  │  http://127.0.0.1:8080      (明文,本机回环)

前端 Dev Server(webpack 等,正常 HTTP)

关键设计: TLS 只在 Caddy 这一层做,前端 dev server 保持普通 HTTP。既不用改构建配置,也避免「双层 TLS」错配。

三、实施流程(已验证可用)

mkcert 本地受信任证书签发流程

第 1 步:用 mkcert 生成本地受信任证书

bash
# 1. 安装 mkcert(macOS)
brew install mkcert nss

# 2. 安装本地根 CA(只需执行一次,会把根证书装进系统信任库)
mkcert -install

# 3. 为你的局域网 IP 签发证书(把 192.168.1.xxx 换成你电脑的实际 IP)
mkcert 192.168.1.xxx localhost

执行后会在当前目录生成两个文件:

  • 192.168.1.xxx+1.pem —— 证书
  • 192.168.1.xxx+1-key.pem —— 私钥

💡 怎么查局域网 IP: macOS 用 ipconfig getifaddr en0,或在「系统设置 → 网络」里看。填的是 192.168.x.x 这类内网地址,不是公网 IP。

第 2 步:安装并配置 Caddy

bash
brew install caddy

项目根目录新建一个名为 Caddyfile 的文件(无后缀),写入:

text
https://192.168.1.xxx:3000 {
    tls ./192.168.1.xxx+1.pem ./192.168.1.xxx+1-key.pem
    reverse_proxy 127.0.0.1:8080
}
配置项含义
https://192.168.1.xxx:3000手机访问的入口地址(对外端口 3000)
tls ...pem ...key.pem指定上一步生成的证书文件,确保与 Caddyfile 同目录
reverse_proxy 127.0.0.1:8080前端项目真实运行地址(按实际端口改)

第 3 步:启动 Caddy

bash
caddy run        # 前台运行
# caddy start    # 后台运行
# caddy reload   # 改完 Caddyfile 热加载

第 4 步:手机访问

确保手机和电脑连同一个 WiFi,手机浏览器打开 https://192.168.1.xxx:3000,此时摄像头 API 即可正常调用。

第 5 步(视情况):手机安装 mkcert 根证书

mkcert 的根 CA 默认只信任在生成证书的那台电脑上。手机是独立设备,若访问时仍提示证书不受信任,需把根证书装到手机:

bash
# 根证书文件为该目录下的 rootCA.pem
mkcert -CAROOT

📱 iOS:rootCA.pem 到手机 → 安装描述文件 → 设置 → 通用 → 关于本机 → 证书信任设置 里手动开启完全信任(这一步最易漏!)。

🤖 Android: 设置 → 安全 → 加密与凭据 → 安装证书 → CA 证书。

Caddy 反向代理实现 HTTPS 架构

四、Caddy 反向代理是怎么工作的

  • 手机发来的是 HTTPS 加密请求,由 Caddy 用 mkcert 证书完成 TLS 握手与解密;
  • Caddy 解密后,用明文 HTTP 把请求转发给本机回环 127.0.0.1:8080 上的 dev server;
  • dev server 的响应原路返回,Caddy 再加密后发回手机。

整个加密只发生在「手机 ↔ Caddy」之间,内网回环 127.0.0.1 这一段是明文,安全且高效。

常见踩坑与排查清单

五、踩坑实录与排查(重点)

坑 1:connection reset by peer(连上即被重置)

现象: Caddy 日志报 aborting with incomplete response / read: connection reset by peerduration 仅 1 毫秒。

原因: 协议错配。上游 dev server 其实跑的是 HTTPS,而 Caddy 的 reverse_proxy 默认用明文 HTTP 去连,TLS 服务收到明文请求无法解析直接 RST。

解决: 推荐让 dev server 退回普通 HTTP,TLS 全交给 Caddy(即本文标准方案);若 dev server 必须保持 HTTPS,则让 Caddy 用 TLS 连上游:

text
reverse_proxy https://127.0.0.1:8001 {
    transport http {
        tls_insecure_skip_verify
    }
}

坑 2:Connection refused(端口压根没人监听)

现象: curl http://127.0.0.1:8001 直接 Connection refused,但浏览器打开 127.0.0.1:8001 却正常。

原因: dev server 没监听 IPv4 回环。常见两种:只监听了 IPv6 的 ::1(macOS 上 localhost 常解析到 IPv6),而 curl 127.0.0.1 走 IPv4 连不上;或只绑定了某张网卡 IP,没绑回环。

bash
# macOS
lsof -iTCP:8001 -sTCP:LISTEN -n -P
# Linux
ss -tlnp | grep 8001

解决: 把 dev server 监听地址改成 0.0.0.0(IPv4/IPv6 都听),从根上消除回环歧义:

javascript
devServer: {
  host: '0.0.0.0',
  port: 8001,
  allowedHosts: 'all',   // 防止 Invalid Host header
}

或者让 Caddy 显式指向 IPv6:reverse_proxy [::1]:8001

坑 3:端口冲突 —— Caddy 对外端口和上游端口设成同一个

如果 Caddy 监听 192.168.1.x:8001,上游 dev server 也用 8001,一旦 dev server 绑的是 0.0.0.0:8001,两者就会抢同一个端口,出现诡异的自我代理或绑定失败。

最佳实践: 对外端口和上游端口永远分开。比如 Caddy 用 3000/8443 对外,上游保持 8080/8001。本文标准方案(外 3000、内 8080)就是这么设计的。

坑 4:手机证书报红 / 摄像头仍调不起来

  • 证书报红 → 手机没装或没信任 mkcert 根证书;iOS 特别注意漏了「证书信任设置」里的开关。
  • 摄像头 getUserMediaundefined → 确认地址栏是 https:// 而非 http://
  • iOS Safari 调不起摄像头 → 需要用户手势触发,即用户点击按钮后再调 getUserMedia,不能页面一加载就自动调。

坑 5:Invalid Host header

dev server 收到非预期 Host 时拒绝服务。webpack-dev-server v4/v5 设 allowedHosts: 'all',v3 设 disableHostCheck: true

六、排查速查表

现象根因处理
connection reset,1ms 断开上游是 HTTPS,Caddy 用 HTTP 连dev server 改回 HTTP,或 Caddy 用 https + tls_insecure_skip_verify
curl 127.0.0.1 refused 但浏览器能开dev server 只听 ::1 (IPv6)dev server 改 host: '0.0.0.0',或 Caddy 用 [::1]:port
端口绑定失败 / 自我代理对外端口 = 上游端口两个端口分开(外 3000,内 8080)
手机证书报红未装 / 未信任 mkcert 根证书装 rootCA.pem,iOS 开「证书信任设置」
getUserMedia 为 undefined用的是 http 而非 https改用 https 访问
iOS 摄像头调不起缺用户手势点击按钮后再调用
Invalid Host headerdev server Host 校验allowedHosts: 'all'(v3 用 disableHostCheck: true)
手机连不上防火墙 / 不同网段同一 WiFi + 放行对应端口

七、最佳实践小结

  1. TLS 只做一层:交给 Caddy,dev server 保持 HTTP,最省心也最不容易协议错配。
  2. 端口分离:对外端口 ≠ 上游端口,避免端口冲突。
  3. dev server 监听 0.0.0.0:根除 ::1 / 127.0.0.1 回环歧义,并加 allowedHosts: 'all'
  4. 证书私钥不出内网:mkcert 方案全程在局域网内完成,比内网穿透 / 公网隧道安全,适合企业内网调试。
  5. 手机端只需一次性配置:装一次 mkcert 根证书并信任,之后长期复用。
  6. 证书文件加入 .gitignore*.pem 不要提交到仓库。

🏁 按这套流程,手机就能稳定通过 HTTPS 访问本地前端项目,摄像头采集调试再也不用反复打包发测试环境了。