
1. Mac 本地 React 项目部署到 Nginx从 build 到反向代理的完整链路如果你在 Mac 上写完了一个 React 项目npm start跑得好好的但一想到要把它放到 Nginx 上就有点发怵这篇就是写给你的。核心问题其实就三件事React 打包出来的build目录怎么交给 Nginx 托管、前端请求/api怎么转发到后端、以及后端接口的 Key 怎么不硬编码在前端代码里。把这三件事理顺Mac 本地 React 项目部署到 Nginx 的整条链路就通了。先说清楚适用对象你用的是 macOS项目是 create-react-app 或 Vite 构建的 React 前端后端可能是 Node/Express、Python 或任意 HTTP 服务跑在本机某个端口上。Nginx 在这里扮演两个角色——静态文件服务器和反向代理。静态文件服务器负责把build里的index.html、JS、CSS 吐给浏览器反向代理负责把/api/xxx这类请求转到后端端口顺便解决浏览器同源策略带来的跨域问题。很多人卡住不是因为 Nginx 难而是因为几个细节没对齐root指向的目录不对、try_files没配导致刷新 404、proxy_pass结尾斜杠写错导致路径拼接异常。我试过把这些坑一个个踩过来下面按顺序给你可复制的配置和验证命令。这一篇不会只讲“装个 Nginx 然后复制文件”而是把打包、目录放置、nginx.conf的server/location、反向代理、curl 验证、报错排查串成一条线。你跟着做最后能在浏览器里用http://localhost:8888打开打包后的 React 页面并且页面里的接口请求能正确打到后端。2. TaoToken 前置用统一入口管理后端接口 Key避免前端硬编码在讲 Nginx 配置之前先解决一个容易被忽略但很关键的问题前端代码里不要出现任何后端接口的密钥。React 打包后所有 JS 都是明文你把 Key 写进fetch或axios的 header 里等于把钥匙贴在门上。正确做法是前端只请求自己域名下的/api由 Nginx 反向代理转发到后端后端再去调用真正的大模型或第三方服务Key 存在后端环境变量里。那后端调用大模型时的 Key 从哪来、怎么统一管理这里可以用 TaoToken 作为统一的 API 入口。它的作用是给你一个稳定的 Base URL 和 Key后端代码里只配置一次切换模型或调整调用时不用改一堆散落的地址。对本地开发来说好处是你不用在每台机器、每个项目里重复填不同的地址接口路径和鉴权方式保持一致。TaoToken 的官网入口是 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content API 基础地址是 https://taotoken.net/api 。注意 API 地址不带 UTM 参数直接用它作为后端请求的 base URL 即可。具体到操作你需要在 TaoToken 控制台创建一个 API Key然后把它写进后端项目的.env文件而不是前端。比如后端是 Node/Express可以这样组织# 后端项目根目录 .env TAOTOKEN_API_KEYsk-你的key TAOTOKEN_BASE_URLhttps://taotoken.net/api后端读取环境变量后再拼接具体接口路径去请求。前端只知道自己要调/api/chat完全不知道 Key 的存在。这样即使别人打开浏览器开发者工具也看不到任何密钥。如果你还没有 Key可以去控制台创建https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_contentconsoleutm_campaignrewrite 。创建后建议先在一个最小请求里验证 Key 是否可用再接入正式项目。验证模型连通性可以用模型对话页面快速试一下https://taotoken.net/models?utm_sourcetaotoken_aicg_blog_endutm_contentmodelsutm_campaignrewrite 。这一步的意义在于Nginx 负责把前端和后端连起来TaoToken 负责把后端和外部模型服务连起来两层解耦。前端不碰 KeyNginx 不碰业务逻辑各司其职。3. 可复制配置nginx.conf 的 server/location 与反向代理写法这一节是核心给你可以直接复制修改的配置。先确认 Nginx 在 Mac 上的安装和路径。用 Homebrew 安装brew install nginx安装完成后配置文件通常在/usr/local/etc/nginx/nginx.confIntel Mac或/opt/homebrew/etc/nginx/nginx.confApple Silicon。可以用下面的命令确认nginx -V 21 | grep conf-path启动、停止、检查配置的常用命令nginx # 启动 nginx -s stop # 停止 nginx -t # 检查配置语法 ps -ef | grep nginx # 查看进程接下来是 React 打包。在项目根目录执行npm run build如果是 Vite 项目产物默认在distcreate-react-app 默认在build。假设产物在build把它放到一个固定目录比如/usr/local/etc/nginx/build或者你项目里的绝对路径。我更推荐用项目绝对路径避免复制来复制去# 假设项目在 /Users/you/projects/my-react-app ls /Users/you/projects/my-react-app/build然后编辑nginx.conf重点改http块里的server。下面是一份可复制的片段注意把root换成你自己的 build 绝对路径把后端端口换成你实际的端口server { listen 8888; server_name localhost; # React 静态资源目录 root /Users/you/projects/my-react-app/build; index index.html index.htm; # 前端路由刷新不 404 的关键 location / { try_files $uri $uri/ /index.html; } # 反向代理所有 /api 开头的请求转到后端 location /api/ { proxy_pass http://127.0.0.1:4000/; proxy_http_version 1.1; 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; } # 静态资源缓存可选 location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff2?)$ { expires 7d; add_header Cache-Control public, max-age604800; } }几个关键点必须说清楚。第一try_files $uri $uri/ /index.html;是 React 单页应用刷新不 404 的核心。没有它你在/about页面按 F5Nginx 会去找/about这个文件找不到就报 404。加上它所有找不到的路径都回退到index.html由前端路由接管。第二proxy_pass http://127.0.0.1:4000/;结尾的斜杠很关键。当location /api/和proxy_pass .../都带斜杠时/api/chat会被转发成http://127.0.0.1:4000/chat也就是/api前缀被替换掉了。如果你希望后端收到的路径保留/api就写成proxy_pass http://127.0.0.1:4000;不带结尾斜杠这样/api/chat会原样转发。这个差异是很多人调半天调不通的原因建议先用 curl 验证清楚。第三前端代码里的请求地址要统一加/api前缀。比如原来请求http://localhost:4000/chat改成请求/api/chat。这样浏览器请求的是http://localhost:8888/api/chat同源不跨域Nginx 再转发到后端。如果你用的是 Vite开发环境可以在vite.config.js里配 proxy生产环境靠 Nginx。两边路径规则保持一致避免开发能跑、打包后挂掉。改完配置后一定要检查语法nginx -t看到syntax is ok和test is successful再重载nginx -s reload4. 验证请求与成功结果curl 与浏览器双确认配置写完不能只看浏览器要用 curl 分层验证这样出问题能快速定位是 Nginx 的问题还是后端的问题。第一步验证 Nginx 是否在监听 8888lsof -i :8888应该能看到 nginx 进程。如果端口被占用改listen或杀掉占用进程。第二步验证静态页面能否返回curl -I http://localhost:8888/期望看到HTTP/1.1 200 OK和Content-Type: text/html。如果返回 403多半是root目录权限或路径不对返回 404检查root指向的目录里是否真的有index.html。第三步验证前端路由回退curl -I http://localhost:8888/some/react/route如果配了try_files这里也应该返回 200内容还是index.html。如果返回 404说明try_files没生效或写错了。第四步验证反向代理。先确认后端在 4000 端口跑着curl -i http://127.0.0.1:4000/chat再通过 Nginx 转发访问curl -i http://localhost:8888/api/chat两次返回应该一致假设后端接口是 GET 或你带了正确的请求方法。如果直连后端通、走 Nginx 不通问题就在proxy_pass的路径拼接或斜杠上。可以用curl -v看详细请求路径curl -v http://localhost:8888/api/chat观察请求行里的路径和你后端期望的是否一致。第五步浏览器打开http://localhost:8888按 F12 看 Network。页面能正常渲染接口请求的 URL 是http://localhost:8888/api/xxx状态码 200返回数据正确就说明整条链路通了。如果接口报 CORS 错误说明请求没走 Nginx 代理而是直接打到了后端端口检查前端请求地址是不是漏了/api前缀。后端调用 TaoToken 的部分可以单独用 curl 验证 Key 是否有效curl -X POST https://taotoken.net/api/v1/chat/completions \ -H Authorization: Bearer $TAOTOKEN_API_KEY \ -H Content-Type: application/json \ -d {model:你的模型ID,messages:[{role:user,content:ping}]}返回正常 JSON 就说明 Key 和 Base URL 都对。这一步和后端代码里用的配置保持一致避免环境变量没加载导致 401。5. 本篇常见错排查401、local proxy failed、reading choices、OAuth部署过程中最容易撞上的几类报错这里逐个对照。401 Unauthorized。如果出现在后端调用 TaoToken 的环节先检查Authorizationheader 格式是不是Bearer sk-xxx中间有空格Key 没有多余引号。再检查环境变量是否真的被加载Node 项目里process.env.TAOTOKEN_API_KEY打印出来看看是不是 undefined。如果出现在 Nginx 转发的接口上说明后端自己的鉴权没通过和 Nginx 无关去看后端日志。local proxy failed / 502 Bad Gateway。这是 Nginx 转发时后端没响应。先确认后端进程在跑lsof -i :4000。再确认proxy_pass的地址和端口没写错127.0.0.1和localhost在某些环境下解析不同建议统一用127.0.0.1。如果后端启动慢Nginx 转发时它还没起来也会 502等后端就绪再刷新。reading choices / Cannot read properties of undefined (reading choices)。这是后端拿到模型返回后解析出错通常是返回结构不是预期的 OpenAI 兼容格式或者请求根本没成功、返回了错误对象却被当成正常响应解析。先打印原始响应体确认choices字段存在。检查请求的modelID 是否正确Base URL 是否拼成了https://taotoken.net/api/v1/...。如果用的是流式响应解析方式也不同别把流当普通 JSON 读。OAuth / 鉴权跳转异常。如果你接的是需要 OAuth 的服务注意回调地址要和你实际访问的域名端口一致。本地用localhost:8888访问OAuth 回调也应该是这个地址否则会跳转失败。这类问题和 Nginx 配置关系不大重点是回调 URL 的白名单要加上你的本地地址。刷新 404。前面说过try_files没配或配错。检查location /块里是否有try_files $uri $uri/ /index.html;并且root指向的目录里确实有index.html。静态资源 404 但首页正常。多半是build里的资源路径是绝对路径/static/...而你的root没指对或者 Nginx 的location匹配把静态资源也代理走了。检查location ~* \.(js|css|...)$这类规则有没有误伤。改了配置不生效。Nginx 改完必须nginx -s reload只保存文件不重载是没用的。另外确认你改的是nginx -t实际检查的那个配置文件Mac 上可能有多个 conf 路径。排查顺序建议先nginx -t看语法再lsof看端口再curl分层验证最后看浏览器 Network 和后端日志。这样能快速缩小范围。6. 语义一致 CTA把 Key 管理和部署链路一次理顺整条链路走下来Nginx 负责静态托管和反向代理前端只请求同源/api后端持有 Key 并调用外部服务。这个结构清晰、可维护也避免了密钥泄露。如果你想让后端的模型调用部分也统一管理不用在每个项目里重复填地址和 Key可以从 TaoToken 的 API Key 页面创建一个 Keyhttps://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi-keysutm_campaignrewrite 。创建后把 Base URL 设为https://taotoken.net/api写进后端.env前端完全不感知。接入细节和参数说明可以看接入文档https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 。如果你后面要做长期的编码类或 Agent 类项目需要更稳定的调用额度可以了解 Coding Planhttps://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding-planutm_campaignrewrite 。最后给你一个实用建议把nginx.conf里server块单独拆成一个文件放在servers/目录用include引入这样以后加新项目不用动主配置。改完记得nginx -t nginx -s reload养成先检查再重载的习惯能省掉很多“为什么没生效”的时间。