搞定 Chrome 远程访问 Laravel+Vite 静态资源加载失败
2025-04-25 03:41:08
搞定!远程访问 Laravel + Vite 项目静态资源加载失败(Chrome 篇)
碰上个有点怪的问题:我用 Laravel + Vite (+ Livewire) 搭了个项目,想在本地开发的时候,也让网络里的其他人能访问看看。本地用 127.0.0.1:8000
访问一切正常,但一用公网 IP 地址(比如 http://92.59.195.93:8000
)去访问,页面是出来了,可样式(CSS)和脚本(JS)全挂了,特别是用 Chrome 浏览器的时候。
问题在哪?
我在本地跑了两个命令:
php artisan serve --host=0.0.0.0 --port=8000
:启动 Laravel 开发服务器,监听所有网络接口的 8000 端口。npm run dev -- --host
:启动 Vite 开发服务器,同样监听所有网络接口,默认端口是 5173,负责处理前端资源的热更新(HMR)和提供服务。
用公网 IP 访问时,打开浏览器的开发者工具,会看到类似这样的报错:
查看页面源代码,发现加载 CSS 和 JS 的 <link>
和 <script>
标签,它们的 href
或 src
地址变成了类似 http://[::]:5173/resources/css/app.css
这样的奇怪格式。
直接在浏览器新标签页尝试访问 http://[::]:5173/resources/css/app.css
,倒是能看到 CSS 内容,说明 Vite 服务本身是启动了,内容也没错。
最关键的信息是 :这个问题只在 Google Chrome 出现,用 Firefox 访问同一个公网 IP 地址就一切正常!这说明问题很可能跟 Chrome 的某些安全策略或者对网络地址的解析方式有关。
为啥会这样?根源分析
问题的核心在于 Vite 开发服务器生成的资源 URL 地址不对。
- Vite 的工作方式 :开发模式下(
npm run dev
),Vite 接管了前端资源的请求。当你在 Blade 模板里使用@vite
指令时,Laravel 会询问 Vite 开发服务器:“喂,我这页面需要的 CSS 和 JS,访问地址是啥?” --host
参数的作用 :给npm run dev
加上--host
参数(或者在vite.config.js
里设置server.host = true
或server.host = '0.0.0.0'
),意思是让 Vite 服务器监听本机的所有网络接口(包括物理网卡的 IP 地址,比如192.168.1.100
,以及回环地址127.0.0.1
和 IPv6 的::1
)。这样局域网或公网(如果端口已映射)才能访问到它。http://[::]:5173
的由来 :当 Laravel 向 Vite 请求资源 URL 时,如果 Vite 没有被明确告知应该使用哪个主机名或 IP 地址对外提供服务,它可能会基于监听的地址或者请求头信息来“猜测”。在这种情况下,它似乎选择了 IPv6 的通配地址[::]
(代表所有 IPv6 地址),并拼接上端口5173
,生成了http://[::]:5173/...
这样的 URL。- Chrome 的“严格” :
[::]
这个地址对于服务器监听是合法的,但作为一个让浏览器去请求的 URL 主机名,在公网环境下就非常奇怪了。远程计算机的浏览器(特别是 Chrome)很可能无法正确解析[::]
指向哪里,或者因为安全策略(比如混合内容、不允许向看起来像本地的地址发出公网请求等)阻止了这些请求。Firefox 对这种地址的处理可能更宽松一些,所以碰巧能用。 .env
文件里的APP_URL
:这个环境变量主要是 Laravel 后端用来生成绝对 URL 的(比如邮件里的链接、队列任务等),它通常不直接影响 Vite 开发服务器生成的 前端资源 URL。虽然设置了APP_URL=http://public_ip
,但@vite
指令优先依赖 Vite 开发服务器自身的配置或推断。
所以,根本原因就是 Vite 开发服务器给出的资源 URL 是一个让远程 Chrome 浏览器“看不懂”或“不信任”的地址。
咋解决呢?试试这几招
既然知道了问题根源,解决起来就思路清晰了。目标是让 Vite 生成正确的、公网可访问的资源 URL。
方案一:明确配置 Vite 开发服务器的主机地址
这是最直接也推荐的方法。告诉 Vite,当别的机器访问你时,应该用哪个地址来找你的资源。
-
原理 :修改
vite.config.js
文件,在server
配置块里明确指定开发服务器的主机名(你的公网 IP 或一个可解析的域名)以及 HMR (热模块替换) 使用的主机。 -
操作步骤 :
-
打开项目根目录下的
vite.config.js
文件。 -
找到
defineConfig
函数里的server
对象(如果没有就创建一个)。 -
添加或修改以下配置项:
import { defineConfig } from 'vite'; import laravel from 'laravel-vite-plugin'; export default defineConfig({ plugins: [ laravel({ input: ['resources/css/app.css', 'resources/js/app.js'], refresh: true, }), ], server: { // 监听所有接口,允许外部访问 host: '0.0.0.0', // port: 5173, // 如果需要自定义端口,取消注释并修改 // ---- 这是关键配置 ---- hmr: { // 告诉 HMR 客户端应该连接哪个主机 // 把 '你的公网IP地址' 替换成实际的公网 IP // 或者一个能从公网解析到你开发机器的域名 host: '你的公网IP地址' }, // ---- 有时候也需要配置 origin ---- // origin: 'http://你的公网IP地址:5173' // 如果 hmr.host 不够,可以试试这个 } });
-
重要 :将上面代码中的
'你的公网IP地址'
替换成你实际的公网 IP 地址(就是那个92.59.195.93
)。 -
保存
vite.config.js
文件。 -
重新启动 Vite 开发服务器:先按
Ctrl+C
停掉原来的npm run dev
进程,然后再次运行npm run dev -- --host
。 -
重新启动 PHP 开发服务器(通常不需要,但保险起见):
php artisan serve --host=0.0.0.0 --port=8000
。 -
清除浏览器缓存(或者用隐私模式),然后再次尝试用公网 IP 访问
http://你的公网IP地址:8000/dashboard
。
-
-
效果 :现在,页面源代码里加载 CSS/JS 的 URL 应该会变成类似
http://你的公网IP地址:5173/resources/css/app.css
,这是一个有效的、公网可达的地址,Chrome 应该就能正常加载了。 -
安全建议 :
- 这样做会把你的 Vite 开发服务器(端口 5173)暴露在公网上,任何人知道你的 IP 都能访问到。这在开发阶段可以接受,但务必不要在生产环境这样做。
- 只在你需要进行远程共享和测试的时候才暴露端口,测试完毕后记得关闭路由器的端口映射或者停止 Vite 服务器。
- 考虑使用防火墙规则,只允许特定 IP 地址访问你的开发端口(8000 和 5173)。
-
进阶使用 :
- 动态公网 IP :如果你的公网 IP 地址是动态变化的,每次变化后都需要修改
vite.config.js
并重启 Vite。可以考虑使用动态 DNS 服务(如 No-IP, Dynu)将一个固定的域名指向你的动态 IP,然后在vite.config.js
里使用这个域名。 - 局域网访问 :如果只是想在局域网内共享,可以将
'你的公网IP地址'
替换成你开发机器在局域网中的 IP 地址(比如192.168.x.x
)。这样配置更安全一些,只在内网可访问。
- 动态公网 IP :如果你的公网 IP 地址是动态变化的,每次变化后都需要修改
方案二:构建静态资源 (适用于临时分享或预览)
如果你只是想临时给别人看一下项目效果,并不需要实时热更新,可以先构建出最终的静态文件。
-
原理 :运行
npm run build
命令。Vite 会将所有的 CSS、JS 和其他资源编译、打包、压缩,并输出到public/build
目录下。然后,Laravel 会配置为直接从这个目录加载这些静态文件,不再依赖 Vite 开发服务器。 -
操作步骤 :
- 停掉 Vite 开发服务器(
npm run dev
那个进程)。 - 在项目根目录运行构建命令:
npm run build
- 这个过程会生成类似
public/build/assets/app-*.css
和public/build/assets/app-*.js
的文件,以及一个public/build/manifest.json
文件。 - 确保你的
php artisan serve --host=0.0.0.0 --port=8000
还在运行(或者重新启动它)。 - 现在用公网 IP 访问
http://你的公网IP地址:8000/dashboard
。
- 停掉 Vite 开发服务器(
-
效果 :页面源代码里,加载 CSS/JS 的
@vite
指令会被 Laravel 解析成指向public/build/assets/
下具体文件的<link>
和<script>
标签。这些资源是通过 Laravel 的 Web 服务器(端口 8000)提供的,不再需要访问 5173 端口。只要端口 8000 映射好了,公网就能正常访问。 -
关于你之前尝试
npm run build
的问题 :"but this only delete my .htaccess, index.php, favicon.ico and created only public general assets"
这听起来不太对劲。
npm run build
正常情况下 绝对不应该 删除项目根目录或public
目录下的index.php
或.htaccess
文件。它只应该在public/build
目录(默认情况下)创建文件。你遇到的问题可能是:vite.config.js
里的build.outDir
配置被错误地指向了public
目录本身或者项目根目录?(默认应该是public/build
)。- 或者
build.emptyOutDir
选项被设置为true
(默认就是true
),但outDir
错误地指向了public
目录,导致清空了public
目录? - 检查你的
vite.config.js
文件中build
相关的配置。默认配置通常是安全的。
// vite.config.js export default defineConfig({ // ... plugins ... build: { // 输出目录,相对于项目根目录。默认是 'dist',但 Laravel 插件会调整为 'public/build' // 确保这里没有指向 'public' 或 '.' // outDir: 'public/build', // 通常由 laravel-vite-plugin 自动处理 // 是否清空输出目录,默认为 true // emptyOutDir: true, // 保持默认即可 // manifest: true, // Laravel 插件会确保开启 // rollupOptions: { ... } // 其他构建选项 } });
确认配置无误后,再试一次
npm run build
。它应该只在public/build
目录下操作。 -
安全建议 :
npm run build
生成的是静态文件,不涉及开发服务器,安全性比方案一高很多。只需要确保 Web 服务器(端口 8000)的访问是受控的。- 缺点是每次代码更改后都需要重新运行
npm run build
才能看到效果,没有了 Vite 开发模式的即时反馈和热更新。
-
进阶使用 :
- 对于需要频繁预览的场景,可以写个简单的脚本来自动化
npm run build
过程。 - 这是项目最终部署到生产环境的标准方式。生产环境会配置 Nginx 或 Apache 等 Web 服务器直接服务
public
目录。
- 对于需要频繁预览的场景,可以写个简单的脚本来自动化
方案三:使用反向代理 (更健壮的方案)
对于更复杂的场景,或者如果你希望用标准端口(80/443)并添加 HTTPS,可以使用反向代理。
-
原理 :在你运行
php artisan serve
和npm run dev
的机器上,再运行一个 Web 服务器软件(如 Nginx 或 Caddy)。配置这个代理服务器监听公网 IP 的某个端口(比如 80),然后根据请求路径,把请求转发给后端的 Laravel 开发服务器(localhost:8000)或 Vite 开发服务器(localhost:5173)。浏览器只需要访问代理服务器就行了。 -
操作步骤 (以 Nginx 为例) :
- 安装 Nginx。
- 配置一个新的 Nginx server 块(通常在
/etc/nginx/sites-available/
目录下创建一个配置文件,然后链接到/etc/nginx/sites-enabled/
)。server { listen 80; # 监听公网的 80 端口 # server_name 你的公网IP地址或域名; # 最好配置 server_name # 匹配 Vite 开发服务器的资源请求 location ~ ^/(resources/|@vite/|node_modules/) { proxy_pass http://127.0.0.1:5173; # 转发给 Vite 开发服务器 proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } # 其他所有请求都转发给 Laravel 开发服务器 location / { proxy_pass http://127.0.0.1:8000; # 转发给 php artisan serve proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } # 可以添加访问日志和错误日志配置 # access_log /var/log/nginx/laravel-dev-access.log; # error_log /var/log/nginx/laravel-dev-error.log; }
- 重要 :确保你的
vite.config.js
不需要 配置hmr.host
或server.origin
为公网 IP。因为所有请求都通过 Nginx (在本地) 代理了,Vite 只需要能和本地的 Nginx 通信即可。Vite 相关的请求会由 Nginx 代理。不过,可能需要配置@vite
指令使其生成的 URL 不包含主机和端口,或者使用相对路径(这个可能需要调整 Laravel Vite 插件或vite.config.js
实现)。一个简化的方法是,确保 Nginx 能正确处理 WebSocket 连接(上面的配置包含了 Upgrade headers),HMR 也许能通过代理正常工作。 - 确保防火墙/路由器只映射了 Nginx 监听的端口(如 80),而 8000 和 5173 端口不需要 对外映射,Nginx 会在本地访问它们。
- 启动或重载 Nginx 配置。
- 运行
php artisan serve --host=127.0.0.1 --port=8000
(监听本地即可)。 - 运行
npm run dev
(不需要--host
,默认监听本地localhost
)。 - 用公网 IP 或域名(不带端口)访问你的项目。
-
效果 :所有外部访问都通过 Nginx 的 80 端口进入。Nginx 根据 URL 判断是应用请求还是静态资源请求(包括 Vite 的 HMR 请求),并分别转发给内部的 8000 和 5173 端口。这种方式对浏览器更友好,也更容易配置 HTTPS。
-
安全建议 :
- 这是相对最安全的方式,因为只有代理服务器端口(如 80/443)暴露在外。
- 可以方便地在 Nginx 层添加访问控制、速率限制、HTTPS(使用 Let's Encrypt 免费证书)等安全措施。
-
进阶使用 :
- 使用 Caddy Server 配置反向代理更简单,它能自动处理 HTTPS。
- 结合 Docker 和 Traefik 或 Nginx Proxy Manager 可以实现更自动化的开发环境代理和管理。
总结一下
远程访问 Laravel + Vite 开发环境时,在 Chrome 中遇到资源加载失败,源头通常是 Vite 开发服务器生成的资源 URL (形如 http://[::]:5173/...
) 对远程 Chrome 来说是无效或不安全的。
最直接的解决办法是修改 vite.config.js
,明确设置 server.hmr.host
为你的公网 IP 或可访问域名 ,并重启 Vite。
如果只是临时分享预览,运行 npm run build
生成静态文件 是个不错的选择,只需暴露 Laravel 的端口(如 8000)。
想要更专业、更安全地长期暴露开发环境(例如用于测试),设置反向代理(如 Nginx) 是个好主意。
根据你的具体需求和场景,选择合适的方案吧!