返回

搞定 Chrome 远程访问 Laravel+Vite 静态资源加载失败

php

搞定!远程访问 Laravel + Vite 项目静态资源加载失败(Chrome 篇)

碰上个有点怪的问题:我用 Laravel + Vite (+ Livewire) 搭了个项目,想在本地开发的时候,也让网络里的其他人能访问看看。本地用 127.0.0.1:8000 访问一切正常,但一用公网 IP 地址(比如 http://92.59.195.93:8000)去访问,页面是出来了,可样式(CSS)和脚本(JS)全挂了,特别是用 Chrome 浏览器的时候。

问题在哪?

我在本地跑了两个命令:

  1. php artisan serve --host=0.0.0.0 --port=8000:启动 Laravel 开发服务器,监听所有网络接口的 8000 端口。
  2. npm run dev -- --host:启动 Vite 开发服务器,同样监听所有网络接口,默认端口是 5173,负责处理前端资源的热更新(HMR)和提供服务。

用公网 IP 访问时,打开浏览器的开发者工具,会看到类似这样的报错:

Google Chrome 访问 Vite 资源失败截图

查看页面源代码,发现加载 CSS 和 JS 的 <link><script> 标签,它们的 hrefsrc 地址变成了类似 http://[::]:5173/resources/css/app.css 这样的奇怪格式。

直接在浏览器新标签页尝试访问 http://[::]:5173/resources/css/app.css,倒是能看到 CSS 内容,说明 Vite 服务本身是启动了,内容也没错。

最关键的信息是 :这个问题只在 Google Chrome 出现,用 Firefox 访问同一个公网 IP 地址就一切正常!这说明问题很可能跟 Chrome 的某些安全策略或者对网络地址的解析方式有关。

为啥会这样?根源分析

问题的核心在于 Vite 开发服务器生成的资源 URL 地址不对。

  1. Vite 的工作方式 :开发模式下(npm run dev),Vite 接管了前端资源的请求。当你在 Blade 模板里使用 @vite 指令时,Laravel 会询问 Vite 开发服务器:“喂,我这页面需要的 CSS 和 JS,访问地址是啥?”
  2. --host 参数的作用 :给 npm run dev 加上 --host 参数(或者在 vite.config.js 里设置 server.host = trueserver.host = '0.0.0.0'),意思是让 Vite 服务器监听本机的所有网络接口(包括物理网卡的 IP 地址,比如 192.168.1.100,以及回环地址 127.0.0.1 和 IPv6 的 ::1)。这样局域网或公网(如果端口已映射)才能访问到它。
  3. http://[::]:5173 的由来 :当 Laravel 向 Vite 请求资源 URL 时,如果 Vite 没有被明确告知应该使用哪个主机名或 IP 地址对外提供服务,它可能会基于监听的地址或者请求头信息来“猜测”。在这种情况下,它似乎选择了 IPv6 的通配地址 [::] (代表所有 IPv6 地址),并拼接上端口 5173,生成了 http://[::]:5173/... 这样的 URL。
  4. Chrome 的“严格”[::] 这个地址对于服务器监听是合法的,但作为一个让浏览器去请求的 URL 主机名,在公网环境下就非常奇怪了。远程计算机的浏览器(特别是 Chrome)很可能无法正确解析 [::] 指向哪里,或者因为安全策略(比如混合内容、不允许向看起来像本地的地址发出公网请求等)阻止了这些请求。Firefox 对这种地址的处理可能更宽松一些,所以碰巧能用。
  5. .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 (热模块替换) 使用的主机。

  • 操作步骤

    1. 打开项目根目录下的 vite.config.js 文件。

    2. 找到 defineConfig 函数里的 server 对象(如果没有就创建一个)。

    3. 添加或修改以下配置项:

      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 不够,可以试试这个
          }
      });
      
    4. 重要 :将上面代码中的 '你的公网IP地址' 替换成你实际的公网 IP 地址(就是那个 92.59.195.93)。

    5. 保存 vite.config.js 文件。

    6. 重新启动 Vite 开发服务器:先按 Ctrl+C 停掉原来的 npm run dev 进程,然后再次运行 npm run dev -- --host

    7. 重新启动 PHP 开发服务器(通常不需要,但保险起见):php artisan serve --host=0.0.0.0 --port=8000

    8. 清除浏览器缓存(或者用隐私模式),然后再次尝试用公网 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)。这样配置更安全一些,只在内网可访问。

方案二:构建静态资源 (适用于临时分享或预览)

如果你只是想临时给别人看一下项目效果,并不需要实时热更新,可以先构建出最终的静态文件。

  • 原理 :运行 npm run build 命令。Vite 会将所有的 CSS、JS 和其他资源编译、打包、压缩,并输出到 public/build 目录下。然后,Laravel 会配置为直接从这个目录加载这些静态文件,不再依赖 Vite 开发服务器。

  • 操作步骤

    1. 停掉 Vite 开发服务器(npm run dev 那个进程)。
    2. 在项目根目录运行构建命令:
      npm run build
      
    3. 这个过程会生成类似 public/build/assets/app-*.csspublic/build/assets/app-*.js 的文件,以及一个 public/build/manifest.json 文件。
    4. 确保你的 php artisan serve --host=0.0.0.0 --port=8000 还在运行(或者重新启动它)。
    5. 现在用公网 IP 访问 http://你的公网IP地址:8000/dashboard
  • 效果 :页面源代码里,加载 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 servenpm run dev 的机器上,再运行一个 Web 服务器软件(如 Nginx 或 Caddy)。配置这个代理服务器监听公网 IP 的某个端口(比如 80),然后根据请求路径,把请求转发给后端的 Laravel 开发服务器(localhost:8000)或 Vite 开发服务器(localhost:5173)。浏览器只需要访问代理服务器就行了。

  • 操作步骤 (以 Nginx 为例)

    1. 安装 Nginx。
    2. 配置一个新的 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;
      }
      
    3. 重要 :确保你的 vite.config.js 不需要 配置 hmr.hostserver.origin 为公网 IP。因为所有请求都通过 Nginx (在本地) 代理了,Vite 只需要能和本地的 Nginx 通信即可。Vite 相关的请求会由 Nginx 代理。不过,可能需要配置 @vite 指令使其生成的 URL 不包含主机和端口,或者使用相对路径(这个可能需要调整 Laravel Vite 插件或 vite.config.js 实现)。一个简化的方法是,确保 Nginx 能正确处理 WebSocket 连接(上面的配置包含了 Upgrade headers),HMR 也许能通过代理正常工作。
    4. 确保防火墙/路由器只映射了 Nginx 监听的端口(如 80),而 8000 和 5173 端口不需要 对外映射,Nginx 会在本地访问它们。
    5. 启动或重载 Nginx 配置。
    6. 运行 php artisan serve --host=127.0.0.1 --port=8000 (监听本地即可)。
    7. 运行 npm run dev (不需要 --host,默认监听本地 localhost)。
    8. 用公网 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) 是个好主意。

根据你的具体需求和场景,选择合适的方案吧!