ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

IIS 部署 Vue 项目与 URL 重写解决 History 刷新 404

2026/10/1 12:03:27 拓冰建站 浏览量
IIS 部署 Vue 项目与 URL 重写解决 History 刷新 404 上周帮一个团队把前端从开发机搬到公司内网的一台 Windows Server 上运维给的答复很干脆只能给 IIS机器上不装 Nginx也不额外开端口。这在传统企业里太常见了服务器是统一纳管的资产巡检脚本、备份策略、端口申请流程全围着 IIS 转你临时要装个 Nginx光审批就得走一圈。所以「IIS 部署 vue 项目 IIS 重写 URL」这套组合谈不上技术选型上的最优解但它往往是落地阻力最小的那个解。这篇文章聊的就是这件事把打好的 vue 静态包丢进 IIS靠 IIS 的 URL Rewrite 模块把前端路由接管过来让用户在浏览器里直接敲/order/detail/1024或者按 F5 刷新的时候不出现 404。核心关键词是 IIS、vue、重写 URL我会把环境准备、打包配置、站点搭建、web.config 规则写法、权限报错处理、备份还原这条链路完整走一遍。适合两类人看一类是后端出身、被临时抓来管部署的开发者另一类是刚接手 Windows Server 的运维手上有一堆 vue 项目要往 IIS 上搬。全程只需要 Windows 自带的工具加上一个官方重写模块不需要额外的运行时依赖。1. 先想清楚IIS 托管 Vue 到底在解决什么问题1.1 三种前端部署形态的取舍vue 打包出来的东西本质就是一堆 html、js、css 和图片它对服务器的要求低到几乎没有能按路径把文件吐出来能返回正确的 Content-Type能设缓存头就足够了。能满足这三条的东西太多Nginx、Apache、IIS甚至python -m http.server都能干。选择哪个技术层面差距不大真正决定的是环境约束。我把常见的三种形态摆在一起对比一下你在动手之前先对号入座部署形态典型场景优势主要代价IIS 直接托管静态包内网管理系统、传统企业 IT 环境无需额外软件、运维熟悉、可与现有站点共存重写规则和权限要自己配报错信息偏晦涩Nginx 托管静态包互联网项目、容器化环境配置直观、性能好、文档多需要额外安装和端口审批前端包交给后端一起发小体量项目、演示环境只有一次部署动作前端发版受后端发版节奏牵制如果你的机器上已经有 IIS 在跑别的站点而且运维明确表示不加装软件那就别纠结了第一条路走到底。IIS 做静态托管完全够用性能瓶颈几乎不会出现在这一层真正让人头疼的是路由和权限这两件事后面会重点拆。1.2 Hash 与 History重写 URL 的根因在这里很多人第一次遇到刷新 404第一反应是「IIS 有问题」。其实不是。问题的根因在前端路由的工作模式上。vue-router 有两种模式。Hash 模式下地址栏长这样http://内网地址/#/order/detail/1024。井号后面的内容浏览器根本不会发给服务器服务器永远只收到一个/请求返回 index.html前端拿到井号后面的字符串自己解析、自己渲染。这条链路里服务器是「无感」的你怎么刷新都不会 404。History 模式下去掉井号地址变成http://内网地址/order/detail/1024。这时候用户一按 F5浏览器会老老实实把/order/detail/1024这个路径发给 IIS。IIS 去物理目录里找发现既没有名为order的文件夹也没有detail这个文件于是返回一个 404。这个 404 不是 vue 报的是 IIS 报的前端代码压根没机会执行。注意很多人以为 404 出现在「刷新」这个动作上其实是出现在「浏览器主动向服务器请求非根路径」上。首次进入首页能正常显示是因为请求的是/而首页之后的跳转都是前端路由在改地址栏没有真正发起请求。理解了这一点「IIS 重写 URL」要干的事就非常清楚了凡是物理文件不存在的请求一律不返回 404而是把 index.html 吐回去交给前端路由处理。这就是整个方案的全部核心剩下的都是围绕它的边界处理。1.3 一个容易被忽略的前提谁在管这台机器动手之前先问清楚三件事能省掉你后面至少半天时间。第一你有没有该站点的管理权限。IIS 里新建站点、改应用程序池、改物理路径权限都需要本机管理员权限。如果只是个普通域账号多数操作会在保存那一刻弹「拒绝访问」。第二物理目录放在哪个盘。放在 C 盘的系统目录下UAC 和默认 ACL 会给你制造额外麻烦放在单独的数据盘比如 D 盘建一个干净的目录权限从零开始授反而最省事。第三这个站点会不会和现有站点抢端口。默认的 80 端口大概率已经被占用了IIS 允许多个站点共享 80 但必须靠不同的主机名区分内网环境如果没有 DNS 支撑主机名方案跑不通这时候换个端口比如 8081是最简单的做法。2. 部署前的环境准备IIS、URL Rewrite 与打包配置2.1 IIS 与 URL Rewrite 模块的安装清单Windows Server 上装 IIS 的入口在「服务器管理器」→「添加角色和功能」。一路下一步到「服务器角色」这一步勾选Web 服务器(IIS)然后展开到下面几个必装项Web 服务器 → 常见 HTTP 功能默认文档、目录浏览可选、HTTP 错误、静态内容这几个是基础。Web 服务器 → 性能 → 静态内容压缩强烈建议勾上vue 打包后的 js 和 css 体积不小开压缩收益很明显。Web 服务器 → 安全性 → 请求筛选默认就装别去掉后面重写规则要用到它的判断逻辑。管理工具 → IIS 管理控制台不装这个你连图形界面都打不开。Windows 10 和 Windows 11 上装法不一样走「控制面板 → 程序 → 启用或关闭 Windows 功能」展开Internet Information Services把「Web 管理工具」和「万维网服务」下面的常用项勾上就行。Win11 上打开 IIS 管理器的快捷方式是Win R输入inetmgr。然后是关键的一步URL Rewrite 模块不在 IIS 的默认安装项里它是个独立发布的官方扩展。去微软官方下载页搜「URL Rewrite」选 x64 版本装上去装完重启一次 IIS 管理器你才能在站点功能列表里看到「URL 重写」这个图标。提示如果你在功能列表里找不到「URL 重写」99% 是模块没装或者装了但没重启 IIS 管理器。不用去网上找各种偏方认准官方安装包就行。还有一个可选项是Application Request RoutingARR。什么时候需要它当你的前端需要把/api的请求转发到另一台机器上的后端服务时。ARR 装上之后配合重写规则就能在 IIS 层做反向代理前端和后端对外只有一个地址跨域问题直接消失。如果后端是 Java 系的 Spring Boot 且已经通过别的方式暴露了地址这一步可以跳过。2.2 vue.config.js / vite.config.js 里的 base 参数这一步是最容易翻车的环节而且翻车现场很有迷惑性页面能打开但白屏控制台一堆GET http://内网地址/js/app.xxxx.js 404。原因在于打包时的资源引用路径。vue-cli 项目看vue.config.js里的publicPathVite 项目看vite.config.js里的base默认值是/。如果你把这个站点部署在根路径下也就是访问http://内网地址:8081/就能打开首页那默认值是对的不用改。但如果你部署在虚拟目录下比如站点根目录放的是别的应用vue 项目挂在/admin这个子应用下访问地址是http://内网地址/admin/那默认的/就会让所有资源去站点根目录找必然 404。这时候必须改成// vue-cli 项目vue.config.js module.exports { publicPath: process.env.NODE_ENV production ? /admin/ : /, outputDir: dist, productionSourceMap: false }// Vite 项目vite.config.js import { defineConfig } from vite import vue from vitejs/plugin-vue export default defineConfig({ base: /admin/, build: { outDir: dist, sourcemap: false } })注意base的写法前后都要有斜杠/admin/而不是admin或者/admin。这个细节坑过不少人写成/admin会导致资源路径拼成/adminjs/app.js这种畸形结果。路由这块也要同步。如果你的项目用了createWebHistory(/admin/)参数要和base保持一致用createWebHashHistory()的话就不受这个影响但那样地址栏会带井号。我个人的习惯是只要不是确定部署在根路径一律先改 base 再打包。因为改一个配置重新打包只要几分钟而线上排查资源 404 的心智负担要大得多。2.3 打包产物的自检清单打包完成之后先别急着往服务器上传本地做几项检查能帮你提前发现一半问题。打开dist目录你会看到index.html、js/、css/、img/这些内容。用文本编辑器打开index.html找到 script 和 link 标签看引用的路径是不是你期望的前缀script typemodule crossorigin src/admin/assets/index-a1b2c3.js/script link relstylesheet href/admin/assets/index-d4e5f6.css如果这里是/assets/...而你要部署在/admin下那就是 base 没生效回去检查配置文件名和位置对不对。另外提醒一句dist目录里不应该出现node_modules或者.env文件。有些团队的构建脚本写得随意把整个项目目录同步上去了静态目录里躺着源码和配置文件这在内网环境下也是个不小的隐患。上传的时候只传dist里的内容不要传外层项目目录。3. IIS 站点搭建实操从应用程序池到目录权限3.1 新建站点与应用程序池的参数选择打开 IIS 管理器左侧展开到「网站」右键 → 「添加网站」。弹出的对话框里有几项要填我逐项说一下我的习惯。网站名称用英文别用中文。不是因为中文不行而是后面写命令行备份脚本、查日志文件的时候中文名会给你带来编码上的额外麻烦。比如vue-admin-prod。物理路径指向你放dist内容的目录比如D:\sites\vue-admin。这个目录的创建有个细节——先把dist里的内容复制进去而不是把dist这个文件夹本身复制进去。很多人复制完变成D:\sites\vue-admin\dist\index.html站点根目录下没有 index.html一访问就是目录列表或者 403。端口内网环境如果 80 被占用 8081 这类高位端口。IP 地址留「全部未分配」主机名留空除非你确定内网有可用的 DNS 解析。创建的时候对话框会问你是否为这个站点新建应用程序池选是。应用程序池的配置在「应用程序池」节点里右键你新建的那个池 → 「高级设置」重点关注三项.NET CLR 版本选「无托管代码」。纯静态前端不需要任何 .NET 运行时选了无托管代码应用程序池启动更快也不会因为服务器上没装对应版本的 .NET 而报错。托管管道模式选「集成」。启用 32 位应用程序保持 False。这是给老组件用的静态站点用不上。提示如果你后续发现服务器上「IIS 中没有 .NET 8」这类提示那通常是另一个 ASP.NET Core 后端项目带来的需求和前端静态托管无关需要单独装对应的 Hosting Bundle。别把两件事混在一起排查。3.2 物理路径授权icacls 比鼠标点更靠谱站点建好之后最经典的报错就来了访问页面返回HTTP 错误 500.19或者401.3提示没有权限读取配置文件或目录。原因是 IIS 的工作进程不是用你的登录账号跑代码的它用的是应用程序池标识。默认情况下这个标识叫IIS AppPool\你的应用程序池名它是一个虚拟账号默认对D:\sites\vue-admin这个新建目录没有任何权限。图形界面的做法是右键目录 → 属性 → 安全 → 编辑 → 添加 → 输入IIS AppPool\vue-admin-prod→ 勾选「读取和执行」「列出文件夹内容」「读取」。这套流程没错但有两个问题一是容易漏掉子目录的继承二是批量部署的时候重复劳动太多。我更推荐用命令行一条搞定而且带递归icacls D:\sites\vue-admin /grant IIS AppPool\vue-admin-prod:(OI)(CI)(RX) /T逐个字段解释一下这样你以后遇到别的场景能自己改(OI)是 Object Inherit表示这个权限会被文件继承。(CI)是 Container Inherit表示会被子文件夹继承。(RX)是 Read Execute。静态站点只要读权限不需要写。千万不要图省事给(F)完全控制静态目录给写权限没有任何好处。/T表示递归应用到目录下已有的所有内容。如果站点需要写日志到某个子目录比如某些前端上传组件把文件直接往静态目录塞虽然不推荐单独给那个子目录加写权限就行icacls D:\sites\vue-admin\uploads /grant IIS AppPool\vue-admin-prod:(OI)(CI)(M) /T3.3 0x80005000 与「应用程序池权限设置失败」的处理有些人在给目录授权时会撞上一个很别扭的报错IIS 应用程序池权限设置失败请手动为其设置 LocalSystem 权限 未知错误 (0x80005000)或者反过来在 IIS 管理器里点「编辑权限」弹出执行此操作时出错文件名: C:\Windows\System32\inetsrv\config\administr...。这两个报错的诱因不一样分开说。先说0x80005000。这个错误码的本质是目录服务查询失败最常见的原因是你用来登录的这个账号在输入IIS AppPool\xxx这个名字时系统去做名称解析而当前机器的网络环境比如某个域控制器不可达、DNS 解析异常导致解析超时。绕过它的办法很简单不要通过网络路径去解析这个虚拟账号改用本地方式。具体操作是在「选择用户或组」对话框里点「位置」把它从整个目录切换到本机LOCAL或者机器名那一项然后再输入IIS AppPool\vue-admin-prod通常就能解析出来了。如果图形界面还是不行直接上 3.2 节那条icacls命令命令行不依赖这个解析流程成功率明显更高。再说inetsrv\config\administr...这个报错。它说的是 IIS 的配置文件applicationHost.config读写出问题了典型原因是你当前账号对C:\Windows\System32\inetsrv\config这个目录没有权限或者文件被占用了。正常情况下管理员账号是有权限的如果你遇到按顺序试关闭所有 IIS 管理器窗口重新以管理员身份打开。检查是不是有杀毒软件或者文件监控工具锁住了这个目录临时排除一下。确认applicationHost.config文件本身没被改成只读。注意绝对不要为了绕过这个报错去给inetsrv\config目录开放「所有人完全控制」。这个目录存的是整台机器的 IIS 配置权限一旦放开任何本地进程都能改站点配置这是实打实的安全风险。宁可花时间找到根因。3.4 MIME 类型补全从 woff2 到 m3u8IIS 自带的 MIME 类型表不算最新。vue 项目里常见的字体文件.woff2、.ttf在老版本 Windows Server 上可能没有登记表现是页面上图标字体全部变成方框控制台报 404.3。顺带提一个场景如果你在前端里集成了视频播放能力比如用播放器组件直接播 HLS 切片.m3u8索引文件加.ts分片IIS 默认同样不认识这两种扩展名请求会直接返回 404播放器会一直转圈加载不出来。这不是前端代码的问题是服务器不认扩展名。在站点或者服务器的「MIME 类型」里手工加几条就行扩展名MIME 类型用途.woff2font/woff2现代字体图标.wofffont/woff兼容旧浏览器字体.ttffont/ttf本地字体文件.m3u8application/vnd.apple.mpegurlHLS 播放列表索引.tsvideo/mp2tHLS 视频分片.webpimage/webp现代图片格式批量添加用命令行更省事%windir%\system32\inetsrv\appcmd set config vue-admin-prod -section:staticContent /[fileExtension.woff2,mimeTypefont/woff2] %windir%\system32\inetsrv\appcmd set config vue-admin-prod -section:staticContent /[fileExtension.m3u8,mimeTypeapplication/vnd.apple.mpegurl]加完之后不用重启服务器IIS 会立刻生效。验证方法很直接直接在浏览器地址栏敲一个具体文件的完整路径看能不能下载下来。4. URL 重写规则web.config 怎么写才不出坑4.1 最小可用规则与逐行拆解到这一步站点已经能正常打开首页了但只要点进任何二级页面再刷新还是 404。现在轮到重写 URL 上场。IIS 的重写规则写在站点根目录下的web.config文件里。注意这个文件要放在站点物理路径的根目录也就是和index.html同级的位置。新建一个文本文件改名成web.config写入下面的内容?xml version1.0 encodingUTF-8? configuration system.webServer rewrite rules rule nameVue Router History stopProcessingtrue match url.* / conditions logicalGroupingMatchAll add input{REQUEST_FILENAME} matchTypeIsFile negatetrue / add input{REQUEST_FILENAME} matchTypeIsDirectory negatetrue / /conditions action typeRewrite url/index.html / /rule /rules /rewrite /system.webServer /configuration别看它短每一行都有讲究我逐条拆开讲。match url.* /表示这条规则拦截所有请求。这里的url是相对于站点根目录的路径不含域名和查询字符串所以.*就是「全都要」。两个add条件是这条规则的精髓也是绝大多数错误配置栽跟头的地方{REQUEST_FILENAME}是服务器变量代表请求路径映射到磁盘上的物理绝对路径。matchTypeIsFile判断这个路径是不是一个存在的文件。negatetrue是取反意思是「当它不是文件的时候条件成立」。第二条同理判断「当它不是目录的时候条件成立」。两条条件都满足意味着这个请求既不是真实文件也不是真实目录。这时候才执行动作。如果是真实存在的文件比如/js/app.a1b2c3.js条件不成立规则跳过IIS 正常把文件返回如果是真实存在的目录也跳过避免把目录请求也重写成 index.html。logicalGroupingMatchAll表示两个条件必须同时满足这是默认行为写出来主要是为了可读性。action typeRewrite url/index.html /是关键动作。这里有两个词容易搞混Rewrite重写和Redirect重定向。Redirect 会返回 301 或 302让浏览器重新发起一次请求地址栏会变Rewrite 是在服务器内部把请求转交出去浏览器完全无感地址栏保持原样。前端路由场景必须用 Rewrite用 Redirect 会陷入死循环或者地址栏被改掉的尴尬。提示如果你部署在虚拟目录/admin下url属性要写成/admin/index.html别写成/index.html否则请求会被重写到站点根目录去找 index.html还是 404。4.2 静态资源、API 与其他路径的分流最小规则跑通之后真实项目里通常还有两类路径需要单独处理。第一类是API 请求。前后端分离的项目里前端页面会请求/api/xxx。按上面的规则/api/user/list既不是文件也不是目录会被重写成 index.html前端拿到一坨 HTML 去 JSON.parse报出一堆莫名其妙的解析错误。解决方式是在规则链里加一条排除条件或者干脆在前面插一条专门的反向代理规则。我给常用的写法是加一条前缀排除条件add input{REQUEST_URI} pattern^/(api|ws|upload)/ negatetrue /{REQUEST_URI}包含完整路径和查询字符串pattern用正则匹配以/api/、/ws/、/upload/开头的请求取反后这些请求就不参与 index.html 重写。ws是留给 WebSocket 的upload是留给文件上传接口的。第二类是后端反向代理。如果你装了 ARR可以再写一条规则把/api转到后端服务rule nameAPI Proxy stopProcessingtrue match url^api/(.*) / action typeRewrite urlhttp://127.0.0.1:8080/{R:1} / /rule这里有三个细节。一是顺序。IIS 的重写规则是从上往下匹配的stopProcessingtrue表示这条命中后就不再往下走了。所以反向代理规则必须放在 index.html 那条的前面否则/api/xxx会先被 index.html 那条吃掉。二是{R:1}这个反向引用。match url^api/(.*) /里括号捕获的内容会存到{R:1}写http://127.0.0.1:8080/{R:1}就是把/api/user/list映射成http://127.0.0.1:8080/user/list。如果你希望后端也保留/api前缀就别用捕获组直接写http://127.0.0.1:8080/api/{R:1}。三是 ARR 的开关。装了 ARR 之后还要在服务器节点上打开「Application Request Routing Cache」→ 右侧「Server Proxy Settings」→ 勾选Enable proxy不勾的话规则不生效而且不会给你明显的报错只会一直 502。这个坑我踩过排查了半小时才想起来。第三类是兜底 404 页面。有些团队不希望任何陌生路径都落到首页想让真正的错误路径显示一个自定义 404 页面。这种需求可以在 index.html 重写规则后面再加一条返回自定义页面rule nameCustom 404 stopProcessingtrue match url^[^/]/.* / action typeRewrite url/404.html / /rule不过说实话对大多数后台管理系统统一落到首页体验更好——用户看到的是熟悉的界面而不是一个冷冰冰的错误页。要不要加这条看产品侧的取舍。4.3 缓存头与压缩上线前最后一道优化规则配好、页面正常之后别急着收工还有两个优化项值得花十分钟配上。静态资源缓存。vue 打包后的 js 和 css 文件名里带内容哈希内容一变文件名就变所以这类文件可以放心设长期缓存。在web.config里加staticContent clientCache cacheControlModeUseMaxAge cacheControlMaxAge365.00:00:00 / /staticContent但要注意index.html绝对不能设长期缓存。因为它是入口文件它引用的资源文件名会变如果 index.html 被浏览器缓存了用户拿到的还是旧版本引用的旧 js 早就被删了页面直接白屏。这是个很隐蔽的问题表现是「我明明发版了同事能看到新版我这里还是老版」清一下浏览器缓存就好了然后你就会收到「为什么每次都要清缓存」的投诉。解决办法是在index.html上单独覆盖缓存策略在web.config里针对具体路径设置location pathindex.html system.webServer staticContent clientCache cacheControlModeDisableCache / /staticContent /system.webServer /location静态内容压缩。前面装 IIS 的时候勾了「静态内容压缩」还要确认它真的开着。在站点上双击「压缩」勾选「启用静态内容压缩」。如果服务器上跑的是动态内容再勾上动态压缩。压缩对 js 文件的效果非常明显通常能压掉六到七成体积内网带宽虽然不紧张但页面首屏加载速度还是能感觉到差别。5. 问题排查实录白屏、404、布局异常怎么定位5.1 刷新 404 的三种典型形态同样是 404成因差别很大看报错返回的是什么能快速定位。第一种刷新二级页面 404返回的是 IIS 的默认错误页。页面上有「HTTP 错误 404.0 - Not Found」和详细的模块、处理程序信息。这是重写规则没生效或者没配。按顺序检查web.config是不是放在站点物理根目录、URL Rewrite 模块是不是装了、站点功能列表里能不能看到「URL 重写」图标。如果规则列表里能看到但就是不起作用把规则顺序检查一遍看是不是被前面的规则拦掉了。第二种刷新后页面能加载 html但一片白控制台报资源 404。看具体 404 的路径。如果路径里少了虚拟目录前缀那就是publicPath或者base没配好回到 2.2 节重新打包。如果路径是对的但文件确实不存在检查上传的时候是不是漏了js或css目录或者上传过程中断了。第三种地址栏路径被改了或者浏览器提示重定向次数过多。这是把 Rewrite 写成了 Redirect 的典型症状。检查action的type属性前端路由场景应该是Rewrite。另外如果重写目标指向的页面本身又触发了规则也会形成死循环比如把url写成了/{R:0}这种。5.2 打包后布局异常的排查顺序「本地开发好好的打包上线就乱」这个问题我遇到过好几次规律比较固定按这个顺序查基本能定位。先排除浏览器兼容性。内网环境经常有同事还在用旧版浏览器。如果你在代码里用了display: flex的 gap 属性、aspect-ratio或者 CSS 嵌套语法这类较新的特性旧内核不认布局就会塌。最快的验证方法是用 Chrome 最新版打开同一个地址如果正常那就是浏览器版本问题要么降级语法要么推动浏览器升级。再查 CSS 抽取顺序。有些项目用了多个 UI 组件库打包后样式文件被合并成几个 chunk加载顺序和开发环境不一样后加载的样式覆盖了前面的导致间距或颜色变化。这类问题在开发环境因为模块加载顺序不同往往看不出来只有打包后才会暴露。排查方法是把样式文件的引入顺序调整或者用更具体的选择器提高优先级。最后看资源加载失败。前面提到过图标字体文件因为 MIME 类型缺失返回 404会导致所有图标位置变成空白或方框视觉上很像「布局乱了」。打开控制台看看有没有 404.3 的错误有的话回到 3.4 节加 MIME 类型。5.3 常见报错速查表把上面零散提到的报错整理成一张表方便你在现场对着查现象/报错大概率原因处理动作HTTP 错误 404.0刷新二级页面出现重写规则缺失或未生效检查 web.config 位置与 URL Rewrite 模块HTTP 错误 404.3字体或 m3u8 文件MIME 类型未登记添加对应扩展名的 MIME 映射HTTP 错误 500.19配置文件权限或语法错误检查 web.config 语法确认目录读取权限HTTP 错误 401.3应用程序池标识无目录权限用 icacls 授予 RX 权限0x80005000 / 应用程序池权限设置失败账号名称解析走网络失败切换位置到本机或直接用 icacls执行此操作时出错administr...无权限或文件被占用以管理员重开管理器检查文件占用页面白屏但 html 加载成功资源路径前缀错误检查 publicPath / base 并重新打包一直转圈或 502ARR 未启用代理打开 Server Proxy Settings 勾选代理图标变方框woff2 等字体文件 404补 MIME 类型并强刷缓存发版后部分人看到旧版index.html 被缓存入口文件禁用缓存6. 备份还原与多环境发布别等出事再想办法6.1 appcmd 命令行备份IIS 的配置改动是「改了就生效」的没有撤销按钮。权限改错、规则写错、绑定删错想恢复只能靠记忆。所以在动任何配置之前先做一次备份这个习惯能救你很多次。IIS 管理器右侧有一组「导出配置」「导入配置」的按钮图形化操作够用但我更推荐命令行因为它可以写进发布脚本里自动执行。%windir%\system32\inetsrv\appcmd add backup before-vue-deploy %windir%\system32\inetsrv\appcmd list backups %windir%\system32\inetsrv\appcmd restore backup before-vue-deploy第一条创建备份名字自己取建议带日期比如before-vue-deploy-0321。第二条列出所有已有备份用来确认备份真的生成了。第三条是还原注意它会覆盖整台机器的 IIS 配置而不只是某一个站点所以还原前想清楚是不是真的要整体回滚。备份文件默认放在%windir%\system32\inetsrv\backup目录下。这个目录在系统盘如果你要长期保留多个版本的备份记得定期清理不然 C 盘空间会被慢慢吃掉。数量上留最近五到十个就够了。注意备份的内容是 IIS 配置不包含站点目录里的文件。也就是说网站本身的 html、js、css 不会进备份。你的前端包要有自己的版本管理最简单的方式是按日期命名文件夹改物理路径指过去出问题改回来就行。6.2 回滚与灰度简单但有效的一套做法对于内网管理系统上多实例、做灰度流量的成本往往过高我用的一套简化方案是这样的。站点目录按版本命名比如D:\sites\vue-admin-0321、D:\sites\vue-admin-0322。IIS 站点的物理路径指向其中一个。发版流程是新版本解压到新目录验证本地文件都在然后用appcmd切换物理路径。%windir%\system32\inetsrv\appcmd set vdir vue-admin-prod/ -physicalPath:D:\sites\vue-admin-0322这条命令把站点根虚拟目录指向新目录用户无感不用重启站点。出问题了改回旧目录几秒钟完成回滚。旧版本目录保留两三个确认稳定之后再删同时把对应的 appcmd 备份清掉保持干净。这套做法有两个好处一是回滚速度快到几乎不用思考二是天然形成了「上一次发版是什么样」的现场排查问题的时候可以直接对比新旧目录的文件差异比翻 Git 记录还直观。顺带说一句如果站点数量多、配置项多还有一套更省事的自动化路径把每个站点的web.config和发布脚本一起放进代码仓库服务器上只保留 IIS 的基础角色和 URL Rewrite 模块配置全部通过appcmd脚本注入。这样新机器上线就是跑一遍脚本的事也避免了「这台机器的配置和别人不一样」这种历史遗留问题。我第一次把整套部署脚本化之后最直观的感受是再也不需要靠回忆去复现某台服务器的状态了。最后分享一个我在实际运维中踩出来的小经验。IIS 的日志默认在C:\inetpub\logs\LogFiles\W3SVC*目录下日志格式选 W3C 扩展字段里把cs-uri-stem、sc-status、time-taken都勾上。遇到「用户说打不开但我这边正常」的情况直接按时间点去日志里捞看看那个请求实际返回了什么状态码、花了几毫秒。日志能告诉你的信息比反复问用户「你清了缓存吗」靠谱得多。