You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

IIS部署Node.js+Express+Vite-React应用时静态资源加载404问题

IIS部署Node.js+Express+Vite-React应用时静态资源加载404问题

我最近碰到了和你一模一样的情况:用Vite+React写前端,Node.js+Express搭后端,本地跑完全正常,但部署到IIS作为默认网站的子站点(路径localhost/chat)时,index.html能正常加载,可所有.js、.css静态资源全报404。折腾了好一阵终于搞定,把问题根源和解决办法分享给你:

我的问题场景和你完全一致

  • 前端用Vite+React,已经执行npm run build生成frontend/dist生产包
  • 后端用Express,通过express.static()托管frontend/dist静态文件
  • 部署到IIS子站点chat,用了iisnode和URL Rewrite插件
  • 现象:localhost/chat能返回index.html,但静态资源(比如http://localhost/assets/index-qBCe9vKe.js)全是404

问题到底出在哪?

  1. Vite默认绝对路径踩坑:Vite打包后默认生成的静态资源引用是根路径绝对地址(比如/assets/xxx.js),但部署在子站点chat下时,浏览器会去localhost/assets/xxx.js找资源,可实际资源明明在localhost/chat/assets/xxx.js,这是最核心的问题。
  2. IIS重写规则顺序乱了:原来的规则没有优先匹配静态文件,导致静态资源被错误转发到server.js处理,自然找不到。
  3. 子站点上下文没对齐:Express和IIS的路径映射没有适配子站点的上下文路径,导致资源寻址错位。

分步解决办法

1. 先改Vite的基础路径配置

在Vite项目的vite.config.js里设置base为你的子站点路径(结尾必须带斜杠),这样打包后的静态资源引用会自动加上子站点前缀:

// vite.config.js
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'

export default defineConfig({
  plugins: [react()],
  base: '/chat/' // 替换成你的IIS子站点路径,比如我的是/chat/
})

改完后重新执行npm run build,生成新的frontend/dist包。

2. 优化Express的静态资源托管(可选但更稳妥)

你的server.js这段代码本身没问题,但可以加上子站点上下文兼容,后续改子站点路径也更灵活:

// server.js 中静态资源和兜底路由部分
const subPath = '/chat'; // 子站点路径
app.use(subPath, express.static(path.join(__dirname, "/frontend/dist")));

// 兜底路由也要适配子站点
app.get(`${subPath}/*`, (req, res) => {
  res.sendFile(path.join(__dirname, "/frontend", "dist", "index.html"));
})

如果IIS重写规则配置正确,这一步可以跳过,但加上会更鲁棒。

3. 修正IIS的web.config重写规则

原来的规则顺序和匹配逻辑完全反了,应该优先让静态文件直接返回,再处理API请求,最后处理React路由兜底。修改后的web.config如下:

<configuration> 
    <system.webServer>
  
     <handlers>
       <add name="iisnode" path="server.js" verb="*" modules="iisnode" />
     </handlers>
  
     <rewrite>
       <rules>
         <!-- 1. 优先匹配静态资源,直接返回,不转发到server.js -->
         <rule name="Serve Static Assets" stopProcessing="true">
           <match url="^assets/(.*)$" />
           <action type="None" />
         </rule>

         <!-- 2. 处理所有API请求,转发到server.js -->
         <rule name="Node.js API Routes">
           <match url="^api/(.*)$" />
           <action type="Rewrite" url="server.js" />
         </rule>

         <!-- 3. React路由兜底:非文件非API的请求,返回index.html -->
         <rule name="React Router Fallback" stopProcessing="true">
           <match url=".*" />
           <conditions logicalGrouping="MatchAll">
             <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
             <add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
           </conditions>
           <action type="Rewrite" url="/chat/frontend/dist/index.html" />
         </rule>
       </rules>
     </rewrite>
  
     <security>
       <requestFiltering>
         <hiddenSegments>
           <add segment="node_modules" />
           <add segment="iisnode" />
         </hiddenSegments>
       </requestFiltering>
     </security>
     </system.webServer> 
 </configuration>

⚠️ 重点注意:React Router Fallback规则里的action url要填子站点下index.html的完整路径,比如我的子站点chat下index.html在frontend/dist,所以路径是/chat/frontend/dist/index.html。

4. 最后检查IIS子站点配置

  • 确保子站点的物理路径直接指向你的应用根目录(就是包含server.js和frontend文件夹的那个目录)
  • 给IIS_IUSRS和IUSR用户分配该目录的读取权限
  • 重启IIS子站点或者整个IIS服务

验证效果

重新部署修改后的代码和静态包,访问localhost/chat,打开浏览器控制台你会发现,静态资源的请求路径变成了localhost/chat/assets/xxx.js,不再是根路径,404问题直接解决!

备注:内容来源于stack exchange,提问作者Yash Gawli

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.14 14:39:33