1. 为什么非要在 Tomcat 前面加一层 Nginx很多人第一次接触这套组合都是从端口怎么变成 80 了这个疑问开始的。我手上这套环境最初的形态很朴素三个业务项目各自打包成 war扔进同一个 Tomcat 的 webapps 目录访问地址是http://内网IP:8080/项目名/xxx。用户嫌端口难看运维嫌每次加项目都要改防火墙我自己则被静态资源把 Tomcat 线程占满这件事折磨了很久。后来把 Nginx 放到最前面做反向代理再按路径把请求分发给后面的一个或多个 Tomcat 实例这些问题才一次性收口。Nginx 结合 Tomcat 做反向代理、再按路径转发多个项目说白了就是让 Nginx 站在用户和 Java 应用之间当前台接待用户只看到 80 或 443 端口Nginx 根据请求的路径前缀判断这个请求该交给哪个 Tomcat、哪个 context然后把请求转过去、把响应收回来。它解决的是端口暴露、路径不统一、多项目共用域名、静态资源效率低、SSL 证书集中管理这几件事。适合阅读的人群也很明确手里有一到多个 Java Web 项目、正在做部署架构收敛的开发或运维以及被项目加一个就要改一次配置困扰的团队。哪怕你只会改server.xml和复制粘贴配置下面的内容也能直接照着抄。1.1 裸跑 Tomcat 会撞上的几个现实问题先把这个话题的前提讲透不然后面很多配置选择会显得没来由。Tomcat 本质是 Servlet 容器它的强项是跑 Java 代码、管理连接池、处理会话而不是坐在公网入口上分发流量。让它直接面向用户最先暴露的问题通常是端口和路径默认 8080 要开放出去项目名会原样出现在 URL 里http://x.x.x.x:8080/crm/order/list这种地址既不美观也让后续换端口、换实例变得很麻烦——一旦实例迁移所有前端写死的地址全得改。第二个问题是静态资源。一个页面的 js、css、图片请求数量往往是接口请求的好几倍这些请求全部由 Tomcat 的线程去读磁盘再返回线程池被大量无意义的 IO 占住真正需要计算的后端接口反而排队。把静态文件交给 Nginx 直接返回Tomcat 的线程就能专心处理业务逻辑这个收益在并发上来之后非常明显。第三个是多项目共存时的隔离问题。多个 war 塞进一个 Tomcat共享同一个 JVM 堆、同一个连接池上限任何一个项目出内存问题或者长事务其他项目跟着遭殃而且 Tomcat 的context path一旦定死前端路由和接口地址就得跟着它走。最后一个容易被忽略的是证书和入口治理Tomcat 自己配 HTTPS 不是不行但多实例时每台都要配一遍证书、每台都要处理跳转运维成本直接翻倍。这四件事叠加起来就是我把 Nginx 放到前面的直接动机。1.2 反向代理究竟替我们挡掉了哪些事反向代理这个词听着抽象用前台接待来类比就很清楚客户浏览器只跟接待Nginx说话接待知道哪位同事Tomcat 实例负责哪块业务把话带过去再把答复带回来客户全程不需要知道同事坐在哪个工位。这个模型带来的是入口收敛——对外只有一个域名、一个端口内部实例的 IP、端口、数量怎么变用户完全无感。具体到实际能拿到的好处我按重要性排一下。端口与路径收敛是刚需80/443 对外8080/8081/8082 全部退回内网路径重写让/crm/这种前缀可以映射到任意后端甚至映射到 context 为/的应用静态资源卸载让 Nginx 直接吐文件顺手还能加缓存头SSL 卸载把加解密集中到一个地方后端走明文内网通信证书更新只改一处多实例负载靠 upstream 分发某台挂了自动摘除再往上一层还能做限流、访问日志统一采集、灰度分流。这些能力不需要一次性全用上但架构上留着这个位置后面加什么都很自然。1.3 多项目共存的三种拓扑选错了后面全是返工路径转发的落地方式其实不止一种选之前先想清楚项目的演进方向。我把常见的三种整理成表方便对照自己的场景拓扑形态部署方式优点明显短板适用场景单 Tomcat 多 context一个实例多个 war配置最简单省内存故障域重合一个项目拖垮全部内部系统、项目数量少、流量低多 Tomcat 单项目每个项目独立实例和端口隔离彻底可单独重启扩容内存开销翻倍端口要规划对外业务、有独立发布节奏多 Tomcat 多实例每项目 1-N 个实例 upstream可水平扩容、可灰度配置和运维复杂度最高有并发压力、需要高可用选择逻辑并不复杂项目之间是否共用同一批用户、是否需要独立发布、单项目的峰值流量有多大。我见过最常见的一种错误是一步到位两三个小项目上来就铺六七个 Tomcat 实例结果内存被 JVM 的固定开销吃掉大半机器负载反而不高——JVM 本身是有基础内存成本的实例数量要和实际并发匹配。反过来把所有项目长期塞在一个实例里遇到一次 Full GC 就全线超时这种代价通常比多占几 GB 内存更难接受。我的建议是先按单实例多 context起步把 Nginx 的路径映射写好等某个项目的发布节奏或流量明显独立出来再把它拆成独立实例——因为 Nginx 侧的配置几乎不用改只换 upstream 指向。2. 把proxy_pass那条斜杠彻底搞明白这套架构里 90% 的诡异问题都出在两处location匹配到了不该匹配的规则或者proxy_pass的斜杠让路径被吃掉或多出了一段。这两件事必须单独拎出来讲清楚否则后面照着配置抄也会抄错。2.1 location 的匹配优先级记住这四条就够了Nginx 收到请求后按固定顺序筛选 location顺序错了就会出现我明明写了更具体的规则却没生效。优先级从高到低是精确匹配最优先命中即停其次是^~前缀匹配命中后不再尝试正则然后是~和~*正则匹配按配置文件里出现的先后顺序先匹配到的赢最后才是普通前缀匹配并且取最长前缀而不是最先出现。这里有个反直觉的点普通前缀匹配虽然最后才用但 Nginx 会先记住最长的那条前缀只有在正则全部没命中时才用它。举几个能直接对照的例子。location /health只匹配/health这一个地址/health/都不算location ^~ /static/命中后立刻返回哪怕后面有~ \.jpg$的正则也不会再试location /app1/和location /app1/report/同时存在时访问/app1/report/x走后者因为前缀更长。还有一个坑要提前说如果某个前缀 location 后面跟了proxy_pass同时又存在能匹配到它的正则 location正则优先这在多项目配置里很容易造成静态资源被转到了后端应用上。我的习惯是把静态资源单独用^~前缀锁死避免被正则抢走。注意location末尾有没有斜杠是两回事。location /app1会匹配/app1、/app1/、/app1abclocation /app1/则不会匹配/app1abc。做路径转发时一律加尾部斜杠能省掉很多跨项目的误匹配。2.2proxy_pass带斜杠和不带斜杠结果完全不同这是最容易踩的一脚。规则可以浓缩成一句话proxy_pass后面如果带了 URI也就是域名端口之后还有路径Nginx 会把location匹配到的那部分前缀替换成这个 URI如果只写到域名端口、不带任何路径则原样透传完整请求路径。更细一点location 写法proxy_pass 写法请求/app1/user/list实际转发到location /app1/http://127.0.0.1:8081http://127.0.0.1:8081/app1/user/listlocation /app1/http://127.0.0.1:8081/http://127.0.0.1:8081/user/listlocation /app1/http://127.0.0.1:8081/app1/http://127.0.0.1:8081/app1/user/listlocation /app1/http://127.0.0.1:8081/v2/http://127.0.0.1:8081/v2/user/list看第二行和第三行的区别同样是/app1/前缀proxy_pass写单个斜杠就把前缀吃掉了写/app1/就保留下来。所以后端应用的 context path 到底是什么决定了你该写哪一种。如果 Tomcat 里部署的 war 包叫app1.warcontext 是/app1那就该写http://127.0.0.1:8081/app1/或干脆不带路径如果 war 包改名为ROOT.warcontext 是/就必须用带单斜杠的写法把前缀剥掉。还有一个硬性限制必须记住当location使用正则~或~*时proxy_pass不允许带 URI 部分否则启动直接报 proxy_pass cannot have URI part in location given by regular expression。遇到这种需求要么改用前缀匹配要么用rewrite改写路径后再透传。2.3 请求头、Cookie 与 context path 的连带影响请求转发过去之后后端应用眼里看到的自己是谁如果不做处理Tomcat 看到的是 Nginx 的地址request.getScheme()拿到的是httpgetServerPort()拿到的是 8081生成的重定向地址和静态资源绝对路径就会指向内网端口用户浏览器根本访问不到。这就是很多人在配置完代理后发现登录后跳回 8080或者页面样式全丢的根因。标准做法是补三个头Host透传原始域名、X-Real-IP传真实客户端 IP、X-Forwarded-For追加代理链、X-Forwarded-Proto标记原始协议。Java 侧如果用的是较新的 Tomcat可以在server.xml的Host节点里加RemoteIpValve它会读取这些头并修正getRemoteAddr()、getScheme()的返回值。老项目里也可以通过过滤器手动处理。Cookie 的问题更隐蔽。Tomcat 的JSESSIONID默认 path 就是 context path多个项目共用同一个域名时理论上 path 不同不会互相覆盖但只要有一个项目的 context 是/它的JSESSIONID就会顶着Path/覆盖整个域名导致其他项目登录态反复失效。解决办法有两个一是给不同项目配置不同的会话 Cookie 名称二是把 context 统一成带前缀的形式别让任何项目占用根路径。前者改起来更快后面实操部分会给出具体配置。3. 从零搭一套能跑起来的环境讲完原理落到具体操作。下面这套规划是我目前还在用的结构三个项目分别跑在两个 Tomcat 实例上Nginx 按路径分发静态资源由 Nginx 直接返回。你可以按自己的项目数量等比调整。3.1 端口与实例规划先把表列出来再动手能省掉后面反复改配置的时间。端口规划的原则是对外只留 80/443管理端口不对外实例端口段固定且留出余量。角色地址与端口说明Nginx0.0.0.0:80 / 443唯一对外入口Tomcat A127.0.0.1:8081承载/crm/、/report/两个 contextTomcat B127.0.0.1:8082承载/portal/ROOT 应用Tomcat 管理端口127.0.0.1:8005 / 8009只监听本地关闭 AJP静态资源目录/data/www/static/Nginx 直接读取不经过后端实例监听127.0.0.1而不是0.0.0.0这点很关键——后端端口不应该被同网段其他机器直接访问全部流量必须经过 Nginx否则限流、日志、鉴权这些入口治理手段形同虚设。AJP 连接器如果不用直接在server.xml里注释掉少一个暴露面。3.2 Tomcat 侧要改的三处配置第一处是端口conf/server.xml里把Server port改成 8005 之外的端口多实例共存时必须改否则第二个实例起不来Connector port改成规划好的 8081/8082redirectPort保持一致。第二处是连接器参数把默认线程数按实际并发调一下maxThreads给到 200acceptCount给 100connectionTimeout20000 毫秒URIEncodingUTF-8一定要显式写上避免中文参数在某些环境下乱码。Connector port8081 protocolorg.apache.coyote.http11.Http11NioProtocol maxThreads200 minSpareThreads20 acceptCount100 connectionTimeout20000 maxConnections10000 URIEncodingUTF-8 redirectPort8443 / Host namelocalhost appBasewebapps unpackWARstrue autoDeployfalse Valve classNameorg.apache.catalina.valves.RemoteIpValve remoteIpHeaderX-Forwarded-For protocolHeaderX-Forwarded-Proto protocolHeaderHttpsValuehttps / Context path/crm docBase/data/apps/crm sessionCookieNameCRMSESSION / Context path/report docBase/data/apps/report sessionCookieNameREPORTSESSION / /Host第三处就是上面这段里的Context与RemoteIpValve。autoDeployfalse是刻意关掉的生产环境不希望 Tomcat 自己扫描目录决定部署什么部署动作应该由明确的脚本控制。sessionCookieName给每个项目起独立名字就是为了解决上一节说的 Cookie 串号问题——这比纠结 context 前缀省事得多。RemoteIpValve则是让后端能拿到真实 IP 和原始协议日志里记录的访问来源才有意义。3.3 Nginx 的完整配置配置我按先全局、再 upstream、最后 server的顺序组织完整贴出来可以直接改路径使用。worker_processes auto; events { worker_connections 10240; } http { include mime.types; default_type application/octet-stream; sendfile on; keepalive_timeout 65; client_max_body_size 50m; map $http_upgrade $connection_upgrade { default upgrade; close; } upstream tomcat_a { server 127.0.0.1:8081 max_fails2 fail_timeout10s; keepalive 32; } upstream tomcat_b { server 127.0.0.1:8082 max_fails2 fail_timeout10s; keepalive 32; } server { listen 80; server_name app.internal; # 静态资源直出前缀锁死避免被后面的正则抢走 location ^~ /static/ { root /data/www; expires 7d; access_log off; } # 前端 history 路由兜底 location /portal/ { proxy_pass http://tomcat_b/; proxy_http_version 1.1; proxy_set_header Connection ; include /etc/nginx/proxy_common.conf; } location /crm/ { proxy_pass http://tomcat_a/crm/; proxy_http_version 1.1; proxy_set_header Connection ; include /etc/nginx/proxy_common.conf; } location /report/ { proxy_pass http://tomcat_a/report/; proxy_http_version 1.1; proxy_set_header Connection ; include /etc/nginx/proxy_common.conf; } location /health { default_type text/plain; return 200 ok\n; } location / { return 404; } } }被include引入的公共片段单独放一个文件所有 location 复用改一处生效全部# /etc/nginx/proxy_common.conf 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_set_header X-Forwarded-Host $host; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection $connection_upgrade; proxy_connect_timeout 5s; proxy_send_timeout 60s; proxy_read_timeout 60s; proxy_next_upstream error timeout http_502 http_503;注意proxy_pass后面带不带 context 是从后端实际部署形态反推的判据/crm/和/report/这两个项目 war 包名没改context 就是/crm、/report所以proxy_pass里把 context 带上了/portal/部署成了ROOT.warcontext 是/所以只写单斜杠把前缀剥掉。这一段如果搞混表现就是访问/portal/返回 404而 Tomcat 的localhost_access_log里能看到请求确实到了后端只是路径不对——这类问题对照上一节的换算表一看就明白。location / { return 404; }这条兜底是我特意加的。多项目环境下如果没有兜底规则未匹配的请求可能落到某个默认 location 上被转给后端白跑一趟还污染日志。直接 404 反而让问题暴露得更快。3.4 联调验证按四步走配置写完nginx -t过一遍语法然后nginx -s reload。验证不要一上来就点页面按下面顺序看出问题能立刻定位是哪一层curl -I http://app.internal/health确认 Nginx 本身活着返回 200curl -I http://127.0.0.1:8081/crm/绕过 Nginx 直连后端确认应用自身正常curl -I http://app.internal/crm/走代理看状态码和响应头里的Set-Cookie路径打开浏览器实际点一遍重点看登录、跳转、图片加载这三类动作。第三步如果直连正常、走代理 404问题基本锁定在proxy_pass的路径换算或 location 匹配如果是 502就看 Nginx 的error.log里connect() failed的地址是不是端口写错了如果是 504说明后端在超时时间内没响应先把proxy_read_timeout临时调大确认是慢查询还是真的挂了。这套顺序看着啰嗦但能避免页面白屏然后到处乱猜的无效排查。4. 上线之后踩过的坑以及怎么绕开配置跑通只是开始真正花时间的是上线后各种本地好好的、线上不对的问题。下面这几类是我这几年反复遇到的按出现频率排。4.1 静态资源 404 与路径丢失最常见的表现页面 HTML 出来了但样式全裸、js 报 404。原因通常是页面里写的是相对路径或绝对路径/static/js/app.js而实际请求被location /或某个正则匹配走了转发到后端后逻辑路径还多了层 context。排查方法很直接——F12 看 404 请求的完整 URL再对照 Nginx 的access_log如果请求根本没进 Nginx是浏览器地址错了如果进了 Nginx 但被转给后端是 location 匹配问题。我现在的固定做法是给静态资源单独一条^~前缀物理目录和 URL 前缀一一对应页面上统一用绝对路径/static/...。配合expires 7d让浏览器缓存但文件名里必须带内容哈希否则改了代码用户还在用旧缓存。另外要提醒一句root和alias别写混location /static/ { root /data/www; }对应的是/data/www/static/而alias是直接把 location 路径替换成指定目录两者差一层目录配错了就是全 404。4.2 登录态丢失、Cookie 串号这个问题的表现非常有迷惑性登录成功刷新一下又回登录页或者 A 项目登录后 B 项目的会话被踢掉。根源基本都在 Cookie 的Path和Domain上。Nginx 默认会把后端的Set-Cookie原样透传如果后端 context 是/crmJSESSIONID的 Path 就是/crm这本身没问题但如果某个项目的 context 是/它的 Cookie 会顶着Path/覆盖整个域名下所有同名 Cookie。我用的解决办法就是第 3.2 节里的sessionCookieName每个项目一个独立名字从根上杜绝同名覆盖。另一个坑是 Cookie 里的Secure属性——如果 Nginx 做 SSL 卸载、后端走 httpTomcat 可能会认为当前不是安全连接而不下发Secure标记浏览器就会拒绝在 https 页面保存这个 Cookie。加上X-Forwarded-Proto: https配合RemoteIpValve就能解决配置里那两个头不是写着好看的。提示排查 Cookie 问题时浏览器开发者工具的 Application 面板里能直接看到每个 Cookie 的 Name、Domain、Path、Secure对照域名一看就知道是重复覆盖还是压根没下发比翻日志快得多。4.3 上传失败、接口超时、WebSocket 连不上这三类问题本质上都是代理层的限制比应用层更严格。上传失败通常是client_max_body_size默认 1MB 卡住了返回 413日志里能看到client intended to send too large body改成 50m 或按业务实际上限设置即可。接口超时则是proxy_read_timeout默认 60 秒导出报表、批量处理这类接口很容易超过需要单独给那个 location 放宽而不是全局调大——全局调大会让真正挂掉的请求占着连接不放。WebSocket 或者 SSE 长连接连不上问题在于 HTTP/1.1 的Upgrade头没有透传。必须显式写proxy_http_version 1.1加上Upgrade和Connection两个头配置里那个map就是为了动态判断。这里有个细节如果你在 upstream 里开了keepalive同时又写死Connection: upgrade普通请求的复用会被打断所以要用map按实际请求头来决定而不是硬编码。4.4 问题速查表把上面这些整理成一张表出问题时先对号入座能省掉大半瞎猜的时间现象最可能的根因快速验证处理方式访问路径 404后端日志无请求location 未匹配或 proxy_pass 路径错误curl -I直连后端对比对照斜杠换算表修正502 Bad Gateway后端未启动或端口写错ss -lntp看端口改 upstream 地址或起服务504 Gateway Timeout后端响应慢或连接池耗尽看后端线程栈与慢查询单独调高该 location 的 read_timeout重定向到内网端口未透传 Host / Proto看响应头 Location补 proxy_set_header登录态反复失效Cookie 同名覆盖或 Path 冲突浏览器 Application 面板改 sessionCookieName上传 413client_max_body_size 过小看 error.log按业务调整并单独 limit样式全丢静态路径被转发到后端对比请求 URL 与 Nginx 日志加^~静态 locationWebSocket 握手失败未透传 Upgrade看 101 响应是否存在加 http_version 与两个头5. 把入口做扎实日志、限流与平滑发布配置能跑之后剩下的工作是让这套入口在出问题时看得见、兜得住、改得动。这部分不是必须做但只要项目还在迭代早晚都要补上。5.1 统一日志格式把耗时变成可查的数字默认的combined格式记不下关键信息至少要加三个字段上游地址、上游耗时、总耗时。改了之后慢请求能直接从日志里筛出来不用靠用户反馈页面很卡再去猜是哪个项目。log_format main_ext $remote_addr - $remote_user [$time_local] $request $status $body_bytes_sent $http_referer $http_user_agent upstream$upstream_addr ustatus$upstream_status urt$upstream_response_time rt$request_time; access_log /var/log/nginx/access.log main_ext;三个时间字段的含义要分清$request_time是从收到客户端第一个字节到响应发完的总时长包含网络耗时$upstream_response_time只是后端处理耗时。两者差距大说明瓶颈在网络或客户端慢差距小但都很大说明后端本身慢。我一般按urt排序看最慢的二十条请求基本能定位到具体接口。5.2 限流不是可选项是保护后端的最低成本手段反向代理做限流比在应用里做限流省事得多因为它在请求进入 JVM 之前就拦掉了。按 IP 限速用limit_req_zone按连接数保护用limit_conn_zone两个配合基本够用limit_req_zone $binary_remote_addr zoneapi_limit:10m rate20r/s; limit_conn_zone $binary_remote_addr zoneconn_limit:10m; location /crm/ { limit_req zoneapi_limit burst40 nodelay; limit_conn conn_limit 20; proxy_pass http://tomcat_a/crm/; }rate20r/s是平均速率burst40允许瞬时突发的排队容量nodelay表示超出速率的请求不排队直接处理但要计入 burst 额度。参数怎么定看后端maxThreads和单请求平均耗时如果单请求平均 100 毫秒、线程池 200理论吞吐上限约 2000 QPS那单 IP 限到 20r/s 对正常用户完全够用对爬虫和异常重试则足够拦下。限流阈值宁可先宽后紧一上来就卡太死正常用户点快点就被拒绝投诉比攻击还多。5.3 HTTPS 与多域名的 server 块组织证书集中到 Nginx 是这套架构最实际的收益之一。配好ssl_certificate之后后端全部走内网明文证书续期只改一处多个 Tomcat 实例不用各自折腾。80 端口留一个只做跳转的 server 块其余业务规则统一放到 443 的 server 里避免两套规则各写一遍导致不一致。server { listen 80; server_name app.internal; return 301 https://$host$request_uri; } server { listen 443 ssl; http2 on; server_name app.internal; ssl_certificate /etc/nginx/cert/app.crt; ssl_certificate_key /etc/nginx/cert/app.key; ssl_protocols TLSv1.2 TLSv1.3; ssl_session_cache shared:SSL:10m; ssl_session_timeout 10m; # 业务 location 与 3.3 节一致 }多域名的情况我倾向于一个域名一个server块把业务 location 抽到一个include文件里复用。这样新增域名时只需要复制十几行的 server 骨架路径规则不用重写也不会出现改了 A 域名忘了改 B 域名的情况。server_name匹配不到时 Nginx 会用第一个 server 块作为默认如果这个默认块恰好是业务站点未备案域名解析过来也能访问所以我会单独放一个返回 444 的默认 server 兜住。5.4 平滑重启与灰度让发布不再挑时间配置文件改完不要在业务高峰直接reload了事先nginx -t校验语法再nginx -s reload。reload 是平滑的老 worker 处理完存量连接后退出不会掐断正在进行的请求。但有一个前提配置文件里不能有语法错误否则 reload 失败、Nginx 继续用旧配置跑你以为生效了其实没有这种静默失败最坑。我的习惯是校验通过后立刻curl -I打一次确认响应头里是新配置的特征比如新增的 header再撤。灰度靠 upstream 就能做不需要额外组件。下面这种写法把流量按权重分给新老两组实例观察一段时间没问题再逐步调权重出问题把新组整组down掉即可回滚upstream tomcat_a { server 127.0.0.1:8081 weight9; server 127.0.0.1:8091 weight1; # 新版本先接 10% 流量 server 127.0.0.1:8082 backup; # 兜底实例 keepalive 32; }backup标记的实例平时不接流量主组全部不可用时才启用这比配一堆健康检查脚本简单有效。max_fails2 fail_timeout10s的意思是 10 秒内失败两次就暂时摘除10 秒后再试探。这套机制对进程假死但仍然监听端口的情况无能为力——那种场景需要主动健康检查开源版没有通常靠外部探活脚本定期curl一下/health来兜。5.5 几个我踩过之后才记住的细节最后补几条零散但很实用的经验。proxy_pass里如果用了 upstream 名字http://tomcat_a/Nginx 只在启动时解析一次域名后端实例 IP 变了必须 reload——用固定 IP 就绕开这个问题。keepalive在 upstream 里配了之后location 里必须同时写proxy_http_version 1.1和proxy_set_header Connection 否则长连接不生效等于白配。文件描述符上限记得调worker_connections写得再大系统ulimit -n卡在 1024 也是白搭。还有个小技巧临时排查问题时在 location 里加一行add_header X-Debug-Upstream $upstream_addr always;浏览器响应头里直接能看到这次请求落到了哪台实例比翻日志快排查完删掉即可。这套东西我前后折腾了大概两周才稳定下来中间最浪费时间的一段是一直用改完直接刷新页面的方式验证出了问题不知道是 Nginx 没生效、路径算错了、还是后端本来就 500。后来养成习惯任何一次改动都按直连后端 → 走代理 → 浏览器实测三步走每一步都确认过再往下看起来慢实际比盲目试错快得多。多项目路径转发这件事本身不复杂真正花心思的是边界——哪个路径归谁、前缀怎么剥、Cookie 怎么隔把这些想清楚了配置反而是最省事的一环。
企业数字化 ERP 产品动态
相关推荐
C#基础学习路线与视频课程设计:运行时、异步、LINQ与项目实战 我最早带人学 C# 的时候,犯过一个很典型的错误:把语法清单从头到尾念了一遍,变量、循环、类、继承、接口,一个不落,学员笔记记得满满当当,结果让他写一个「读一个文本文件、按某列排序、输出到另一个文件」… · 2026/11/3 23:48:10
OIF-CEI-05.2详解:高速互连的电气合规性标尺 简介:本资源为光学互联论坛(OIF)于2024年1月正式发布的最新版CEI高速电气接口标准——OIF-CEI-05.2实施协议官方PDF文档,面向芯片设计、高速互连系统开发、光模块与交换机硬件工程师及通信标准研究人员。该标准定义了6Gbps至112Gb… · 2026/11/3 23:48:10
OpenAI 700个智能体潜伏Hugging Face:智能体安全与检测盲区 最近AI圈有个挺有意思的事件——OpenAI被曝出在Hugging Face平台上投放了约700个智能体,这些智能体在平台上悄无声息地"做一道考题",持续了整整两个多月才被外界察觉。乍一看这像是一条攻击事件的新闻,但把细节拆开会发现ÿ… · 2026/11/3 23:47:09
uniapp海外游戏分发工程:PG电子集成与Switch卡带区域适配 简介:这是一套面向海外游戏业务开发者的全栈前端解决方案,基于UniApp框架实现跨平台兼容,专为构建海外游戏交易平台、手游下载站及Switch游戏卡带销售系统设计。资源包含完整可二次开发的前端源码(非编译后产物)&#… · 2026/11/4 0:00:17
Qoder NEXT:智能代码补全工具的技术解析与应用 1. 项目概述Qoder NEXT作为新一代智能编程辅助工具,其核心突破在于通过深度优化代码补全算法,将AI生成代码的开发者采纳率提升了65%。这个数字背后代表着编程效率的实质性飞跃——开发者不再需要花费大量时间在重复性代码编写和机械性调试上,… · 2026/11/4 0:00:17
32G统一内存跑大模型:内存账、TPS实测与端云分流 想在一台 32G 统一内存的小主机上把大模型跑成"日常可用",这件事我前后折腾了小半年。标题里说的这台 Mac mini M6,是我们这次讨论的目标平台,具体到手规格以实物为准;但下面这套推算方法和实测思路,对任何统… · 2026/11/4 0:00:17
从生成Token到直接决策:Jev如何重塑AI Agent工程实践 1. 从"生成文字"到"拍板决策":Jev 到底在解决什么问题大多数人第一次接触大模型,脑子里建立的模型是"输入问题、输出文字"。你问它今天天气怎么样,它给你一段描述;你让它写个周报,它给你… · 2026/11/4 0:00:17
AI辅助技术栈评估:提升研发决策效率60% 1. 项目背景与核心价值技术决策一直是研发团队面临的重大挑战。随着技术生态的快速演进,技术栈的选择、维护和迭代变得愈发复杂。我们团队在过去两年里,先后经历了三次重大技术栈迁移,每次都需要投入大量人力进行技术评估和风险分析。直到去年… · 2026/11/4 0:00:17
双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/2 13:10:28
STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 6:04:24
DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 5:52:46
AI辅助技术栈评估:提升研发决策效率60% 1. 项目背景与核心价值技术决策一直是研发团队面临的重大挑战。随着技术生态的快速演进,技术栈的选择、维护和迭代变得愈发复杂。我们团队在过去两年里,先后经历了三次重大技术栈迁移,每次都需要投入大量人力进行技术评估和风险分析。直到去年… · 2026/11/4 0:00:17
从生成Token到直接决策:Jev如何重塑AI Agent工程实践 1. 从"生成文字"到"拍板决策":Jev 到底在解决什么问题大多数人第一次接触大模型,脑子里建立的模型是"输入问题、输出文字"。你问它今天天气怎么样,它给你一段描述;你让它写个周报,它给你… · 2026/11/4 0:00:17
32G统一内存跑大模型:内存账、TPS实测与端云分流 想在一台 32G 统一内存的小主机上把大模型跑成"日常可用",这件事我前后折腾了小半年。标题里说的这台 Mac mini M6,是我们这次讨论的目标平台,具体到手规格以实物为准;但下面这套推算方法和实测思路,对任何统… · 2026/11/4 0:00:17
双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/2 13:10:28
STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 6:04:24
DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 5:52:46