Windows Nginx 部署 Vue/React 前端:跨域、404与反向代理
2026/9/18 7:38:12 网站建设 项目流程

把一个 Vue 或 React 项目打包成 dist 之后直接双击 index.html,页面要么白屏,要么路由点不动,接口一个个报跨域——这个场景我见过太多次了。在 Windows 环境下用 Nginx 部署纯前端项目,本质上要解决三件事:把静态产物以正确的方式托管出去、让前端路由在刷新时不会 404、以及处理前后台完全分离之后绕不开的跨域请求问题。前两件事靠 Nginx 的root/aliastry_files就能搞定,第三件事才是真正让很多人卡住的地方——因为大部分人在开发期用的是前端 devServer 的 proxy,一上线发现那套配置根本不会跟着打包产物走,浏览器照样拦你。这篇内容我按自己实际部署的流程从头写一遍,包括 Windows 上 Nginx 的目录怎么放、配置怎么写、跨域到底是谁在拦、反向代理的路径前缀为什么会多出来一层、以及代理之后怎么在后端拿到真实的客户端地址。适合手上有前后台分离项目、准备往服务器或内网机器上放、但对 Nginx 配置还不够熟的人看,也适合已经部署上线但被跨域和 404 反复折腾的人对照排查。

1. 为什么不把纯前端产物继续挂在开发服务器上

1.1 开发服务器解决的是效率问题,不是运行问题

新手最常见的做法是:项目跑完npm run build,然后用npm run dev或者vite preview挂着,把地址发给别人访问。这个做法在临时演示时能用,但一旦进入真实使用就会暴露三个硬伤。

第一是性能。开发服务器为了支持热更新和源码映射,会把大量模块拆成独立请求返回,一个中等规模的页面可能产生几百个 HTTP 请求,浏览器并发连接数一满就排队。生产构建后的产物恰恰相反,代码被合并且带 hash,请求数少、可缓存,用开发服务器去托管这份产物,等于把优化全部作废。第二是路由回退。前端路由如果用的是 history 模式,用户在/order/detail这种地址上按 F5,开发服务器默认会去磁盘找对应的物理文件,找不到就 404——它不会自动回退到 index.html,除非你手动配了 rewrite。第三是反向代理的缺失。开发期的接口地址来自vue.config.jsvite.config.ts里的server.proxy,这份配置只存在于构建工具的运行进程里,打包出来的 dist 完全不含这段逻辑,所以上线之后跨域问题会原封不动地回来。

我一般把开发服务器定位成"只服务于本地编码阶段的热重载工具",它的生命周期在提交代码那一刻就结束了。真正对外的入口,交给 Nginx 这类静态服务器加反向代理的组合。

1.2 前后台完全分离之后,浏览器眼里是两个不同的世界

"前后台完全分离"这个词在工程上意味着前端只产出一个静态包,后端只提供接口,两边独立部署、独立发布。这套架构的收益很明确,但代价是把一个原本由服务端渲染抹平的问题重新抛给了浏览器:同源策略。

同源策略的判断条件是协议、域名、端口三者完全一致。注意这里没有"路径"这一项——http://a.com/xhttp://a.com/y是同源的,而http://a.comhttp://a.com:8080不同源,http://a.comhttps://a.com也不同源。这个细节非常关键,因为它直接决定了我们后面选哪种跨域方案:只要能让浏览器认为前端和接口是同一个来源,跨域问题就不存在了。而 Nginx 恰好具备把两个来源"合成"一个来源的能力。

我在实际项目里通常这么划分:前端静态资源由 Nginx 直接从磁盘返回,路径是/;所有接口请求统一走/api前缀,由 Nginx 反向代理到后端服务。这样浏览器看到的永远是同一个域名同一个端口,前端代码里写fetch('/api/user/list')就够了,不需要任何绝对地址。

1.3 Nginx 在 Windows 上能干什么、不能干什么

先把这个边界说清楚,免得后面白折腾。Windows 版本的 Nginx 是官方维护的,功能上覆盖了绝大多数日常场景:静态文件托管、反向代理、负载均衡、gzip 压缩、缓存控制、多站点配置、HTTPS 终结,这些都没问题。

但有几点要有心理预期。一是 Windows 版使用的是不同的 I/O 模型(select / IOCP),在高并发下的表现不如 Linux 版本,日请求量在几十万以内、接口响应正常的话基本感觉不出来差异。二是它本身不提供 Windows 服务封装,重启机器之后不会自动起来,需要额外借助服务包装工具或者任务计划程序。三是路径和权限模型跟 Linux 差别很大,比如反斜杠和正斜杠混用、盘符、中文路径这些问题,都会在这里踩坑。把这些搞清楚再动手,能省掉很多"明明配置一样为什么就是不行"的时间。

2. Windows 上把 Nginx 装成"能用"的状态

2.1 下载解压之后,先决定它住在哪个目录

去官网下载页选 Stable 版本(稳定分支),拿到的是一个 zip 压缩包,不是安装程序,这是 Windows 版的正常形态。解压位置我建议遵循两条规则:不要放在 C:\Program Files 下,不要放在带空格或中文的路径里

原因不玄学。Program Files这个目录名里带空格,配置里写绝对路径时虽然可以加引号,但很多脚本、批处理、第三方工具在处理参数时不加引号,路径一断就报"系统找不到指定的路径"。而中文路径在处理静态文件编码、日志文件名时偶发乱码。我自己的习惯是D:\webserver\nginx,干净、短、无特殊字符。

解压后的目录结构建议花两分钟熟悉一下,后面排查问题全靠它:

目录/文件作用排查时的用途
conf/nginx.conf主配置文件所有改动的入口
html/默认站点目录可以直接把 dist 放进来,也可以指向别处
logs/access.log访问日志看请求有没有打到 Nginx、返回码是多少
logs/error.log错误日志配置语法错、上游连接失败都在这
temp/临时目录存放代理缓冲的临时数据,别手动删
nginx.exe主程序所有命令都由它派生

有一点必须强调:Nginx 在 Windows 上是"主进程 + 工作进程"的模式,主进程负责读取配置和调度,工作进程负责实际处理请求。这意味着你在任务管理器里会看到两个 nginx.exe,一个都别乱杀。

2.2 启动、重载、停止:三条命令背后的进程逻辑

在 Nginx 根目录打开命令行(建议用 PowerShell 或 CMD,右键"在终端中打开"),然后按需要执行:

# 检查配置文件语法,任何改动之后都应该先跑这条 nginx.exe -t # 启动(首次启动也可以用 start nginx,避免占用当前终端窗口) start nginx # 不停止服务,重新加载配置(最常用) nginx.exe -s reload # 快速停止:直接终止所有工作进程,可能丢弃正在处理的请求 nginx.exe -s stop # 优雅停止:处理完当前请求再退出 nginx.exe -s quit

reload这条命令值得单独说一下。它的行为是:主进程重新读取配置文件,启动新的工作进程,然后通知旧的工作进程在处理完手上的请求之后退出。所以它是热更新,不会造成明显的服务中断。这跟stop之后重新start是完全不同的两件事,改配置的时候养成用reload的习惯。

还有两个 Windows 上特有的坑。第一个是直接双击nginx.exe启动,会有一个黑色窗口闪一下然后消失,看起来像是没启动,其实是正常启动了——它把日志写进了 logs 目录而不是屏幕。要确认是否启动成功,用tasklist | findstr nginx看进程,或者浏览器直接访问http://localhost看有没有默认欢迎页。第二个是修改了配置文件但忘了reload,然后在那儿怀疑配置写错了,这个错误我自己也犯过,现在改配置的第一反应就是先-treload

如果配置改坏了导致 Nginx 起不来或者行为异常,用taskkill /F /IM nginx.exe强制清干净所有进程,再重新启动,比一个个找 PID 快得多。

2.3 别把所有配置都堆在 nginx.conf 里

新手常见的问题是上来就改conf/nginx.conf里的server块,改着改着发现要加第二个项目、第三个项目,文件越来越长,几个月后自己都不敢动了。

我的做法是把主配置当"框架",只保留全局设置,业务站点拆成独立文件:

# conf/nginx.conf 末尾 http { # ...全局配置保持原样... include mime.types; default_type application/octet-stream; # 每个站点一个文件,按域名或项目名命名 include vhost/*.conf; }

然后在conf/下新建vhost目录,里面放front-admin.conffront-portal.conf这样的文件,一个项目一份。这样做的好处很直接:改 A 项目的配置不会误伤 B 项目,出问题的时候直接把对应的 conf 注释掉就能定位,迁移的时候把这个文件连同 dist 一起拷走即可。需要注意include的路径是相对于 Nginx 的安装目录(也就是prefix)来解析的,写vhost/*.conf就够了,不用写成完整绝对路径。

主配置里另外两个我一般会顺手调整的地方:一是worker_processes在 Windows 上设为1更稳妥,因为 Windows 版的 I/O 模型对多工作进程的支持有限,设多了反而可能引发意外;二是把gzip打开,对 js、css 这类文本资源的体积压缩效果通常在 60% 以上,纯前端项目的首屏时间能明显改善。

3. dist 放哪儿、怎么指:静态托管的三个关键决定

3.1 root 与 alias 的行为差异,一张表说清

这两个指令是静态托管的根基,也是最容易混的一对。它们的区别在于:root是把请求路径拼接到指定目录后面,alias是把匹配到的那段路径替换成指定目录。

假设前端产物放在D:/www/admin/dist,请求是/assets/index-3f2b1c.js,两种写法如下:

# 写法一:root,结果是 D:/www/admin/dist/assets/index-3f2b1c.js location / { root D:/www/admin/dist; index index.html; } # 写法二:alias,结果是 D:/www/admin/dist/index-3f2b1c.js location /assets/ { alias D:/www/admin/dist/assets/; }

对比一下就清楚了:root后面接的是"父目录",服务器会把 location 匹配的部分原样接上去;alias后面接的是"目标目录本身",location 匹配的部分会被丢掉。所以用alias结尾必须带斜杠,不带斜杠会把最后一段路径拼错,这是非常高频的失误。

指令匹配逻辑典型场景易错点
root目录 + 完整请求URI整站托管,location 为/误把 dist 目录本身写进去,导致多一层
alias用目录替换匹配前缀子路径部署、单目录映射结尾漏斜杠,路径拼接错位

我个人的默认选择是:整个站点托管用root,只映射某个子目录或某类资源用alias。不要在同一段配置里混着用,容易记混。

3.2 history 路由的刷新 404,靠 try_files 兜住

前端路由有两种模式:hash 模式地址里带#,后面的内容不会被发送到服务器,所以不会 404;history 模式地址干净,但对服务器有要求——任何找不到物理文件的路径都必须返回 index.html,由前端路由自己解析。

try_files就是干这个的,它的执行顺序是"逐个检查,命中即返回,全都不命中就走最后那个兜底值":

location / { root D:/www/admin/dist; index index.html; # 先找同名文件,再找同名目录,都找不到就交给 index.html try_files $uri $uri/ /index.html; }

这里的/index.html是内部重定向,不是 302 跳转,浏览器地址栏不会变,前端路由能正确拿到原始路径。有一点要留意:这个兜底会让所有不存在的路径都返回 200 和首页内容,包括真的写错的接口地址。如果希望不存在的资源老老实实返回 404,可以把兜底限定在非静态资源上,比如给/assets/单独配一个不做兜底的 location,让图片、js 缺失时真实报错,方便定位问题。

另外,如果你的前端项目部署在子路径下(例如https://site.com/admin/),构建工具的basepublicPath必须同步改成/admin/,同时兜底要写成/admin/index.html。这个配置项漏改的典型症状是:首页能打开,但所有 js、css 请求都打到了根路径上,一水的 404,页面白屏。

3.3 一个域名下挂多个纯前端项目

前后台分离的项目往往不止一个前端,比如管理后台、运营后台、用户端。三种组织方式,各有取舍。

第一种是子路径区分,共用 80 端口:

server { listen 80; server_name local.site; location /admin/ { alias D:/www/admin/dist/; index index.html; try_files $uri $uri/ /admin/index.html; } location /portal/ { alias D:/www/portal/dist/; index index.html; try_files $uri $uri/ /portal/index.html; } }

这种方式的优点是省端口、省证书,缺点是每个项目的构建 base 都要对应改,而且容易互相影响——改一个 location 可能把另一个的匹配顺序挤掉。Nginx 的 location 匹配是有优先级的:精确匹配=最高,其次是前缀匹配^~,然后是正则匹配,最后是普通前缀匹配。所以location /admin/要和location /并存时,必须确保更长的前缀能先被匹配到,否则会被/吃掉。

第二种是不同端口区分,互相完全隔离:

server { listen 8081; server_name localhost; root D:/www/admin/dist; index index.html; try_files $uri $uri/ /index.html; }

这种方式我不需要动构建配置,dist 拷过去就能跑,独立排查也方便,适合内网里的小规模部署。缺点是端口要记,防火墙也要逐个放行。

第三种是不同域名,通过server_name区分。最干净,但需要 DNS 或本地 hosts 配合。我一般在内网测试阶段用 hosts 把admin.localportal.local都指向同一台机器,配置里各自一个 server 块,互不干扰。

4. 跨域到底是谁在拦:从同源策略到反向代理

4.1 报错的是浏览器,不是你的后端挂了

先把一个常见误解纠正掉:控制台里那句has been blocked by CORS policy浏览器打出来的,服务器可能正常返回了 200,数据也完整,只是浏览器在把响应交给你的 js 代码之前做了检查,发现缺少必要的响应头,于是拦下了。用 Postman 或 curl 请求同一个地址往往一切正常,原因就在这里。

浏览器检查的是响应头里的Access-Control-Allow-Origin,它必须精确匹配当前页面的源,或者写成*。简单请求(GET、POST 配普通表单类型)浏览器直接发,服务端不带这个头就拦;带自定义请求头、用 PUT/DELETE、或者 Content-Type 是application/json的请求会先发一个 OPTIONS 预检请求,服务端必须正确回应这个 OPTIONS,包括Access-Control-Allow-MethodsAccess-Control-Allow-Headers,否则正式请求根本不会发出。预检请求在开发者工具里看起来就是一条 OPTIONS,状态码 200 或 204 都可能,看不到它的话很容易误判成"请求没发出去"。

还有一条铁律:Access-Control-Allow-Origin设为*时,Access-Control-Allow-Credentials不能是true。也就是说,需要携带 Cookie 的请求,服务端必须把 Origin 原样回显,而不能图省事写星号。这一条不知道坑了多少需要在请求里带登录态的项目。

4.2 三种跨域解决方案的适用边界

真正要做选择的时候,手上其实只有三条路:

方案做法生效范围主要问题
后端加 CORS 头后端代码或框架层设置 Allow-Origin生产、开发都生效需改后端、多环境头值维护麻烦
前端 devServer proxy配置server.proxy仅开发环境打包后完全失效,上线必跨域
Nginx 反向代理把接口路径代理到后端生产环境,开发也可用需要理解路径重写规则

第二条是新手最大的认知陷阱。在vue.config.js里配了proxy: { '/api': { target: 'http://localhost:8080', changeOrigin: true } },本地跑得好好的,一打包部署就跨域,然后开始到处搜"为什么打包后跨域"。答案很朴素:那段 proxy 配置是给 webpack devServer 用的,它是个运行时的中间件,代码根本不进 dist。开发代理是开发便利工具,不是跨域解决方案。

第三条是我实际项目里的首选,原因是它把跨域问题从"协议问题"降维成了"路由问题"。前端请求/api/user/list,浏览器认为这是同源地址,不做任何跨域检查;Nginx 收到之后把这个请求转给后端的 8080 端口,属于服务端之间的通信,压根不受同源策略约束。

4.3 反向代理的完整配置,以及那个多出来的斜杠

先看一份能直接用的配置:

server { listen 80; server_name local.site; # 前端静态资源 location / { root D:/www/admin/dist; index index.html; try_files $uri $uri/ /index.html; } # 接口反向代理 location /api/ { proxy_pass http://127.0.0.1:8080/; 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; proxy_connect_timeout 10s; proxy_read_timeout 60s; proxy_send_timeout 60s; } }

重点在proxy_pass那一行的结尾斜杠上,这是整个配置里最容易出错的地方,我用一个对照表说明:

配置写法请求/api/user/list实际转发到
proxy_pass http://127.0.0.1:8080/;http://127.0.0.1:8080/user/list
proxy_pass http://127.0.0.1:8080;http://127.0.0.1:8080/api/user/list

规律是:proxy_pass的地址带路径部分(哪怕只是一个/)时,location 匹配到的那段前缀会被替换掉;不带路径时,原始 URI 原样透传。前者适合后端接口本身没有/api前缀的情况,后者适合后端接口就挂在/api下的情况。判断标准很简单——去问后端同事或者翻一下接口文档,看真实接口路径是什么。

选错了的症状很好辨认:如果后端实际路由是/user/list而你写成了不带斜杠的版本,后端会返回 404,但 Nginx 的 access.log 里记录的是 404 而不是 502,说明请求确实到达了后端,只是路径不对。如果写成带斜杠但后端路由确实带/api,症状正好相反。

4.4 代理之后,怎么在后端拿到真实的客户端地址

这是很多人上线后才发现的问题。加上反向代理这一层之后,后端代码里request.getRemoteAddr()拿到的是Nginx 所在机器的地址(通常是 127.0.0.1),因为对后端来说,请求就是 Nginx 发过来的。真实用户地址、真实协议、真实域名全都藏在代理头里。

所以上面配置里那几行proxy_set_header不是可选项,而是必需品。它们的作用如下:

  • X-Real-IP:单个真实客户端 IP,值取$remote_addr,也就是直连 Nginx 的那个地址。
  • X-Forwarded-For:一条链,格式是"客户端IP, 代理1IP, 代理2IP"。用$proxy_add_x_forwarded_for会自动把当前客户端地址追加到已有的头后面,多层代理时能还原完整链路。
  • X-Forwarded-Proto:原始请求的协议。这个非常关键,如果后端要根据协议生成回调地址、拼接绝对链接,没有这个头它会以为自己是 http,生成出来的链接在 https 环境下会出问题。
  • Host:原始请求的域名。不设置的话,后端收到的 Host 是127.0.0.1:8080,凡是依赖 Host 做多租户判断、生成绝对地址、做签名校验的逻辑都会出错。

后端取值的方式,以 Java 为例,是从请求头里读:

String ip = request.getHeader("X-Forwarded-For"); if (ip != null && ip.contains(",")) { // 多层代理时取第一个,也就是最初的客户端 ip = ip.split(",")[0].trim(); } if (ip == null || ip.isEmpty()) { ip = request.getRemoteAddr(); }

这里有个必须提醒的安全细节:X-Forwarded-For可以被客户端伪造的,任何人手动加一个这个头就能骗过你的取 IP 逻辑。如果你的场景涉及基于 IP 的限流、风控、白名单,不能无条件相信这个头里的第一个值。稳妥的做法是只信任最靠近自己的那一层代理写入的值,或者干脆在网关层做一次规范化,把不可信的入站头覆盖掉。用proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for的好处是它会追加而不是替换,但前提是你必须清楚链路上有几层代理、哪一层是可信边界。

另外还有一个开发期和上线后的差异要注意:在本地开发时,前端 devServer 代理和后端都在本机,X-Forwarded-For拿到的可能是127.0.0.1或者压根没有这个头,后端测出来的 IP 和上线后不一致。我的建议是接口里关于 IP 的逻辑,一定要在真实代理链路下验证一遍,别只在本地测。

4.5 如果确实需要在 Nginx 层补 CORS 头

有些情况下反向代理走不通,比如后端接口是第三方的、域名不在你手上,或者前后端确实要分域名部署。这时候只能在 Nginx 里加响应头,但有几个坑必须绕开。

第一,简单请求加add_header就够了:

location /api/ { proxy_pass http://127.0.0.1:8080/; add_header Access-Control-Allow-Origin $http_origin always; add_header Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS" always; add_header Access-Control-Allow-Headers "Content-Type, Authorization, X-Requested-With" always; add_header Access-Control-Allow-Credentials "true" always; # 预检请求直接返回,不打到后端 if ($request_method = OPTIONS) { return 204; } }

这里有两个细节。一是Access-Control-Allow-Origin用了$http_origin变量回显请求的 Origin,而不是写死*,原因就是前面说的——需要携带 Cookie 时*会被浏览器拒绝。回显 Origin 等于信任所有来源,如果接口涉及敏感操作,应该在白名单里做校验,只放行自己已知的域名。

二是所有add_header后面都跟了always。这不是可选项。Nginx 的add_header默认只在 200、201、204、206、301、302、303、304、307、308 这些状态码下生效,当后端返回 400、401、403、500 时,这些 CORS 头会消失,浏览器看到的就是"没有 CORS 头"的报错,真正的错误信息被掩盖了。加上always之后,任何状态码下头都会带上。这个坑我在排查一个登录失败问题时浪费了大半天,最后发现只是一个没加always

还有一个坑是add_header的继承规则:如果子级 location 里定义了任何add_header,父级的全部失效,它是覆盖而不是合并。所以同一组 CORS 头要保证在真正处理请求的那个 location 里完整存在,别指望从上层继承下来。

5. 上线前跑一遍这份自检,能省掉大部分返工

5.1 按症状对照的排错表

部署完之后遇到问题,先看 Nginx 的logs/access.loglogs/error.log,再看浏览器开发者工具的 Network 面板,两条线索一对,问题基本就定位了。

症状最可能的原因处理方式
页面白屏,js/css 全 404构建 base/publicPath 与实际部署路径不一致改成对应子路径后重新构建
首页正常,刷新子路由 404缺少 history 回退try_files $uri $uri/ /index.html
接口 404,access.log 里也是 404proxy_pass结尾斜杠导致路径前缀被替换或未替换对照后端真实路由调整斜杠
接口 502后端没起、端口写错、防火墙拦截本机 curl 后端端口验证连通性
接口 504后端处理超时查后端慢查询,适当调proxy_read_timeout
配置改了没反应没执行reload,或进程有残留nginx -treload,必要时强杀重启
整站访问不了80 端口被占用或防火墙未放行`netstat -ano
浏览器报 CORS前端用了绝对地址请求后端改为相对路径 + Nginx 反向代理

关于 80 端口被占用,Windows 上这是高频问题。IIS、某些下载工具、云盘客户端、甚至一些开发工具都会悄悄监听 80。用netstat -ano | findstr :80拿到 PID,再用tasklist | findstr <PID>看是哪个进程,确认后再决定是改 Nginx 端口还是处理占用方。如果开着 Windows Defender 防火墙,还要给 80 和 443 加入站规则,否则本机访问正常、局域网其他机器访问不了,会让人误以为是配置问题。

5.2 缓存策略:index.html 不缓存,带 hash 的资源长缓存

这一段是我认为纯前端部署里最有价值的优化点,也是很多人漏掉的地方。

构建工具产出的 js、css 文件名里带内容 hash(比如index-3f2b1c.js),文件内容一变,hash 就变,文件名就变。这意味着这些文件可以放心地设置超长缓存,一年都行,因为每次发布都是新文件名,浏览器不会拿到旧内容。

index.html恰恰相反,它不能缓存。因为它里面引用的是带 hash 的资源文件名,如果 index.html 被缓存住了,用户下次进来加载的还是旧的 index.html,里面引用的还是旧的文件名,而旧文件可能已经被删掉了,结果就是白屏。这类"发布之后只有部分用户白屏、清一下缓存就好"的问题,根源几乎都在这里。

配置上我一般这么写:

# 带 hash 的静态资源,长时间强缓存 location ~* \.(js|css|png|jpg|jpeg|gif|svg|woff2?|ttf|ico)$ { root D:/www/admin/dist; expires 365d; add_header Cache-Control "public, immutable"; } # 入口文件,禁止缓存,每次都回源校验 location = /index.html { root D:/www/admin/dist; add_header Cache-Control "no-cache, no-store, must-revalidate"; add_header Pragma "no-cache"; expires -1; }

注意location = /index.html用的是精确匹配,优先级最高,不会被前面那条正则 location 抢走。如果你不确定自己的 location 匹配顺序对不对,可以在 index.html 里随便改一个字重新发布,然后用浏览器刷新看看内容有没有变——变了说明没被缓存,没变就说明缓存策略没生效。

5.3 把 Nginx 做成开机自启的两种做法

Windows 版 Nginx 不自带服务注册,服务器重启之后它不会自动起来,这个在测试环境里无所谓,放到正式环境上就是事故隐患。

第一种做法是用 Windows 服务包装工具(比如 nssm 或 WinSW),把nginx.exe包装成一个系统服务,设置成自动启动。这种方式的优势是能进"服务"管理界面,能被监控系统识别,日志也能重定向到文件。配置的时候注意"启动目录"要设成 Nginx 根目录,"关闭方式"建议设成先发nginx -s quit再强制结束,保证优雅退出。

第二种是任务计划程序,创建一个"计算机启动时"触发的任务,操作填nginx.exe的完整路径,勾选"不管用户是否登录都要运行"。这种方式更轻量,不需要引入额外工具,但服务的可见性和可控性差一些,停止和重载还得靠命令行。

我自己的选择是内网环境用任务计划,正式环境用服务包装工具。两种方式都要记得做一次重启验证,别配完了不看效果。

6. 这套配置在我实际项目里的几个体会

跨域问题绕来绕去,最后落到实处的结论其实很朴素:能用 Nginx 反向代理,就不要动 CORS 头。反向代理是从根上让浏览器认为前后端同源,接口路径、Cookie、鉴权头全都原样透传,不需要考虑预检、不需要考虑*和 credentials 的冲突、不需要考虑always的继承规则,配置量还更少。只有当前端和后端确实必须部署在不同域名下、或者接口来自第三方时,才回到 CORS 这条路。

另一个让我印象很深的是proxy_pass结尾斜杠的问题。这个问题我在不同项目里重复遇到,每次症状都是接口 404,每次排查方式都一样:打开 access.log 看 Nginx 记录的实际请求路径,再对照后端的路由定义,两边一比就知道问题出在前缀上。现在我养成了一个习惯,写proxy_pass之前一定先确认一句话——后端接口的真实路径到底带不带/api这个前缀,确认完再动手,能省掉一轮重启。

最后提一个容易被忽视的经验:改完配置别只刷新首页确认。至少走一遍完整链路——打开首页看静态资源有没有 404、点几个带路由跳转的菜单、刷新子路由页面、触发一个需要登录态的接口、看后端日志里记录的客户端 IP 是不是真实 IP。这五步都过了,才算真的部署完成。我踩过的坑里,有一半以上都是首页能打开就以为搞定了,结果用户从分享链接直接进到深层页面时才发现路由回退没配。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询