首页 / 资讯中心 / 文章详情

Nginx反向代理Tomcat配置详解:从80端口到多站点部署

Nginx反向代理Tomcat配置详解:从80端口到多站点部署 ★ FEATURED ARTICLE
最近在实际项目里接到一个很典型的需求一套Java Web应用跑在Tomcat上部署到服务器之后业务方要求所有访问都走80端口、不暴露8080而且后续还要在同一台机器上再挂几个不同的站点。说白了就是要配置Nginx反向代理让用户通过Nginx访问Tomcat网站。这个场景在中小团队里实在太常见了。很多人Java开发出身本地写代码、启动Tomcat、打开8080端口调试一套流程行云流水。但一旦到了服务器环境问题就来了8080端口要跟别的服务冲突怎么办用户总记不住带端口号的地址怎么办Tomcat直接暴露公网天天被扫描器问候怎么办另一个业务也要上线总不能每个应用都开一个端口吧在这些问题面前Nginx反向代理几乎是标准答案。Nginx在前端收口接收用户的HTTP请求再通过内部转发把请求交给后端的Tomcat处理Tomcat的响应再原路返回。用户从头到尾只跟Nginx打交道不知道后端是什么、有几台、跑在哪个端口。这篇文章就把这个配置过程完整拆开讲一遍包括核心参数原理、完整实操步骤、常见坑位和一些安全加固手段适合正在做服务器部署或者准备接手运维工作的同学参考。1. 配置场景与整体思路拆解1.1 为什么要在Tomcat前面加一层Nginx先说个很多新手容易产生的疑问Tomcat本身就是一个HTTP服务器能直接处理浏览器请求为什么非要再套一层Nginx核心原因是职责不同。Tomcat本质上是Servlet容器它的强项是执行Java动态代码JSP、Servlet、Spring MVC这些业务逻辑。但它在静态资源处理和高并发连接管理上并不算高效。Tomcat处理一个请求需要占用一个线程线程的创建、切换、销毁都有成本高并发下线程一多CPU和内存压力很快就上来了。Nginx不一样。它基于事件驱动模型用少量进程就能扛住数万并发连接处理静态文件、转发请求、负载均衡这些活儿非常轻量。让Nginx站在前面等于把两类流量做了分流静态资源图片、CSS、JS由Nginx直接返回动态请求再转发给Tomcat去处理。Tomcat只需要关心应用逻辑整体吞吐量会有明显改善。安全上的好处同样重要。直接把8080暴露到公网等于告诉所有人“这里有一个Java应用服务器”扫描器会盯上你Tomcat的管理接口、已知漏洞都会成为攻击面。用Nginx收口之后你只需要开放80和443后端Tomcat完全可以在内网甚至只监听本机回环地址外网根本摸不到它。再说运维层面。一台服务器上可能要跑好几个应用有的在8080有的在8081还有的在9090。如果没有统一入口用户访问时得记一堆端口号配置HTTPS证书也得每个端口单独弄。有了Nginx你可以让所有域名都指向80/443端口Nginx根据域名或者URL路径分发到不同的后端服务。后续要加负载均衡、灰度发布、限流限速都是在Nginx这一层操作不用去动业务应用。1.2 一次完整请求的流转过程理清楚数据流是配置反代之前最重要的一步。假设用户在浏览器地址栏输入http://www.example.com域名解析完成后实际访问的是服务器上的80端口也就是Nginx。Nginx拿到请求之后做两件事第一根据请求头里的Host字段找到匹配的server块第二根据URL路径在server块里找到匹配的location规则。匹配完成之后Nginx不会自己去生成页面内容而是把整个HTTP请求原样转发给前面设置好的上游地址也就是Tomcat监听的那个端口比如http://127.0.0.1:8080。Tomcat收到Nginx转来的请求后正常处理生成HTML或者JSON响应把结果返回。Nginx收到后原样返回给浏览器。整个过程对用户是透明的浏览器只知道自己在跟Nginx通信既看不到Tomcat的存在也不知道后端具体跑在哪个端口。这个流程用一句大白话总结Nginx是前台接待Tomcat是后厨。客人来了跟接待员点菜接待员通知后厨做菜菜做好了由接待员端上桌。后厨不需要跟客人直接打交道。1.3 反向代理与正向代理的区别很多刚接触这个概念的同学会搞混反向代理和正向代理。打个比方正向代理是“替用户访问”——用户想访问某个外部网站但出于某些原因不能直接访问于是把请求交给代理服务器由代理服务器去访问目标网站再把结果拿回来。这个过程中目标网站只知道代理服务器的存在不知道真实用户是谁。反向代理则是“替服务器接客”——用户想访问某个服务请求先打到代理服务器上代理服务器根据规则把请求分发给内部的后端服务器。这个过程中用户只知道代理服务器的地址不知道后面具体是哪台机器、哪个端口在处理请求。我们这里配置的Nginx反向代理就是第二种替Tomcat接客。理解了这两个方向的区别后面配置里那些参数就好懂了。2. 反向代理核心配置参数详解2.1 一份最小可用的反向代理配置先看一份最简单但能跑通的配置把它放到/etc/nginx/conf.d/tomcat_site.conf里CentOS/RHEL系列或者/etc/nginx/sites-available/下做个软链Debian/Ubuntu系列。server { listen 80; server_name www.example.com; location / { 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; } }这段配置说的是当外部请求的域名是www.example.com且访问的是80端口时Nginx把请求转发给本机的8080端口。proxy_pass就是那个“把请求转出去”的指令它后面的URL决定了请求要去哪里。剩下的四行proxy_set_header是设置请求头。为什么需要它们我在2.3节单独讲这是反代配置里最容易忽略但影响很大的细节。2.2 proxy_pass 的路径替换规则proxy_pass后面要不要加斜杠是整个配置里最经典的坑没有之一。很多人配置完发现页面打不开、样式全丢、接口404最后排查半天问题就出在这里。规则其实不复杂。location用前缀匹配时如果proxy_pass后面不带URI路径没有斜杠或者没有具体路径Nginx会把完整的原始请求URI传递给后端如果proxy_pass后面带了URI路径哪怕只有一个斜杠Nginx会用这个URI去替换掉匹配到的location前缀。直接上表格这样最直观。假设用户请求的路径是/app/loginlocation规则proxy_pass写法后端实际收到的路径location /app/http://127.0.0.1:8080;/app/loginlocation /app/http://127.0.0.1:8080/;/loginlocation /app/http://127.0.0.1:8080/test;/test/login第一种情况用户访问/app/login后端Tomcat也收到/app/login路径原样转发。第二种情况用户访问/app/login匹配到/app/之后被替换成/所以后端收到/login。第三种情况前缀被替换成/test后端收到/test/login。这个规则带来的典型问题就是如果Tomcat上的应用是通过根路径ROOT部署的你把它放在location /app/下面同时proxy_pass后面又带了斜杠那么所有路径都会丢一段页面直接404。反之如果应用本身就有/app这个context path但你的proxy_pass没带斜杠路径倒是能对上后面的静态资源路径如果写的是绝对路径一样会出问题。还有一条硬性规则当 location 使用正则表达式匹配时proxy_pass 后面不允许带URI。原因很好理解正则匹配本身就是为了灵活捕获各种路径如果proxy_pass再强制替换URI两者就会产生逻辑冲突。配置了就会报错nginx -t 直接不通过。2.3 请求头传递与后端获取真实客户端IP配置完代理之后Tomcat 的访问日志里会看到所有请求的来源IP都是127.0.0.1。这不是玄学是因为Nginx转发请求的时候默认的HTTP请求头里并没有告诉后端“原始用户来自哪台机器”Tomcat自然只能看到与自己通信的Nginx的地址。要解决这个问题就必须在转发时把原始信息通过请求头带过去。这就是我们前面配置里那四行proxy_set_header的作用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;逐行解释Host $host把浏览器请求里的Host头原样传给后端。如果没有这一行后端收到的Host会是127.0.0.1:8080应用里如果基于域名做跳转、生成链接全部会出错。X-Real-IP $remote_addr把Nginx直连的那个客户端IP也就是用户的真实IP放到这个自定义头里传给后端。X-Forwarded-For这个头是业界标准Nginx会把原始IP追加在这个头的末尾。因为中间可能有多层代理所以这个头往往是多个IP逗号拼接的。X-Forwarded-Proto $scheme告诉后端用户用的是HTTP还是HTTPS$scheme是Nginx内置变量自动取值。光在Nginx侧把头带过去还不够Tomcat默认并不知道要信任这些头。如果你在Java应用里调用request.getRemoteAddr()获取客户端IP返回的还是127.0.0.1。这时候需要在Tomcat的conf/server.xml的Engine标签里加一个ValveValve classNameorg.apache.catalina.valves.RemoteIpValve internalProxies127.0.0.1 remoteIpHeaderX-Forwarded-For protocolHeaderX-Forwarded-Proto /internalProxies声明哪些IP是可信代理这里写127.0.0.1表示本机Nginx是唯一可信的转发方。配置完之后重启Tomcat应用里拿到的就是真实用户IPrequest.getScheme()也能正确识别HTTP还是HTTPS。这一步如果漏了后面做HTTPS跳转时容易出现死循环我在第4章会遇到。2.4 超时、缓冲区与连接复用生产环境里反代配置不能只写个proxy_pass就完事。默认参数在简单场景下没问题但遇到长接口、大文件下载、高并发时就会暴露短板。常用的几个参数整理如下参数默认值作用proxy_connect_timeout60s与后端建立TCP连接的超时时间proxy_read_timeout60s两次读取后端响应之间的最大间隔时间proxy_send_timeout60s向后端发送请求的最大间隔时间proxy_bufferingon是否缓冲后端响应再转发给客户端proxy_buffers8 4k缓冲区的数量和大小举个实际场景你的应用里有一个导出报表的接口一般要跑两三分钟。Nginx默认的proxy_read_timeout是60秒接口处理超过这个时间没有返回任何数据Nginx就会中断连接报504 Gateway Time-out。这时候就要把超时调大proxy_connect_timeout 5s; proxy_read_timeout 300s; proxy_send_timeout 300s;proxy_connect_timeout别设太大后端连接如果5秒内建不起来大概率是后端挂了或者端口不对没必要干等60秒。再说说缓冲。proxy_buffering on的时候Nginx会先把后端返回的数据收进缓冲区再统一发给客户端好处是后端响应慢时客户端不用一直干等Nginx能以更稳定的速度往客户端推数据。但对于大文件下载缓冲反而会造成延迟因为必须等缓冲区满了才开始转发。如果做文件下载服务可以考虑对特定location关闭缓冲location /download/ { proxy_pass http://127.0.0.1:8080; proxy_buffering off; }还有一个容易忽略的点是upstream连接复用。默认情况下Nginx与后端Tomcat建立的HTTP连接是短连接每次请求都要重新握手。如果Nginx的并发高握手开销不可小觑。要复用长连接需要做两件事。第一在http块里定义一个upstream集群upstream tomcat_cluster { server 127.0.0.1:8080; keepalive 32; }第二在location里告诉Nginx用HTTP/1.1协议通信并清除Connection头location / { proxy_pass http://tomcat_cluster; proxy_http_version 1.1; proxy_set_header Connection ; }Tomcat默认支持HTTP/1.1和keep-alive这样配置之后Nginx到Tomcat的连接得以复用性能在多请求场景下提升明显。如果后端有多个Tomcat节点upstream里多写几行server就实现了负载均衡upstream tomcat_cluster { server 192.168.1.10:8080 weight2; server 192.168.1.11:8080 weight1; keepalive 32; }weight是权重数字越大分配到的请求越多。这是后话了单机场景先把本机的配好。2.5 location匹配优先级与静态资源分离前面一直在用location /做演示但真实站点通常不止一个location。Nginx的location匹配规则是有优先级的理解这个优先级能帮你解释很多“奇怪”的现象。优先级从高到低精确匹配比如location /favicon.ico^~前缀匹配一旦命中不再检查正则~或~*正则匹配~*不区分大小写普通前缀匹配比如location /app/换句话说精确匹配 普通前缀匹配带^~ 正则匹配 普通前缀匹配。这个顺序如果不了解很容易写出“看起来对但永远不生效”的配置。典型的应用是动静分离。Tomcat擅长动态请求静态资源交给Nginx直接读文件返回能大幅减轻后端压力。配置长这样location ~* \.(css|js|png|jpg|jpeg|gif|ico|svg|woff2?)$ { root /data/static; expires 7d; access_log off; }这个正则的意思是所有以css、js、图片、字体文件结尾的请求直接在/data/static目录下找对应文件返回不进Tomcat。expires 7d设置浏览器缓存7天access_log off关闭静态资源访问日志减少磁盘I/O。这里必须提一个高频翻车点root和alias的区别。很多人搞混这两个指令导致静态资源404。root会把完整的URI拼在路径后面。比如location /static/ { root /data; }用户请求/static/app.cssNginx会在/data/static/app.css下找文件。而alias是直接把location前缀替换掉location /static/ { alias /data/; }用户请求/static/app.cssNginx在/data/app.css下找文件。简单说root加URIalias替换前缀。静态资源目录如果和你location里的路径不一致优先考虑alias。3. 从零开始完成NginxTomcat反向代理配置3.1 环境准备与版本选型我这次配置模拟的是生产环境里最常见的一套组合组件版本/参数说明操作系统LinuxCentOS 7/8 或 Ubuntu 20.04命令会按不同发行版标注Nginx1.20低版本对HTTP/2和TLS配置的支持偏弱Tomcat9.0.x8.5也可以配置思路一致JDK8根据应用要求应用部署方式ROOT部署或带context path后面会有两种路径案例建议先确认一下当前系统里有没有装Nginx装了哪个版本nginx -v如果没装CentOS/RHEL系列可以加官方源安装sudo yum install -y nginxUbuntu/Debian系列sudo apt update sudo apt install -y nginx3.2 Tomcat端的基本设定在配置Nginx之前先确保Tomcat自己跑得通。启动Tomcat/opt/tomcat/bin/startup.sh然后确认8080端口在监听ss -lntp | grep 8080看到类似LISTEN 0 100 127.0.0.1:8080或者0.0.0.0:8080的输出就说明Tomcat已经起来了。用curl直接访问一下curl -I http://127.0.0.1:8080/如果返回了HTTP状态码200说明Tomcat服务正常。这里记住你的应用实际访问路径是什么是直接根路径访问还是需要带工程名比如/demo。后面配置Nginx时这个路径直接决定proxy_pass怎么写。3.3 安装Nginx并确认运行安装好之后先启动Nginx确认默认页能访问sudo systemctl start nginx sudo systemctl enable nginx curl -I http://127.0.0.1/能返回200就说明Nginx本体没问题。这时候如果你直接访问服务器IP的80端口会看到Nginx默认欢迎页这是正常的我们接下来要把它替换成自己的站点配置。3.4 编写反向代理配置我习惯把所有反向代理配置写在独立的conf文件里不用去动主配置文件nginx.conf。CentOS里放到/etc/nginx/conf.d/下Ubuntu里放到/etc/nginx/sites-available/后做软链到/etc/nginx/sites-enabled/。假设我们的Tomcat应用部署在根路径下用户在外部通过http://www.example.com直接访问首页。配置如下server { listen 80; server_name www.example.com; access_log /var/log/nginx/example_access.log; error_log /var/log/nginx/example_error.log; # 动态请求全部转发给Tomcat location / { 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 5s; proxy_read_timeout 60s; proxy_send_timeout 60s; proxy_buffering on; proxy_buffer_size 8k; proxy_buffers 8 32k; } # 静态资源由Nginx直接返回 location ~* \.(css|js|png|jpg|jpeg|gif|ico|svg|woff2?)$ { root /data/static; expires 7d; access_log off; } }注意几个细节access_log和error_log指定了独立的日志文件方便后期排障。如果所有站点混用一个日志流量大了之后查问题会非常痛苦。动态请求的location里把超时、缓冲参数都写上了。proxy_buffering on表示如果后端响应慢Nginx先把数据攒着再发给客户端。静态资源的location里用root /data/static意味着请求一张/images/logo.pngNginx去/data/static/images/logo.png找文件。你需要提前把静态文件放到这个目录或者把路径改成你实际存放的位置。如果你的应用不是部署在根路径而是有一个context path/demo那配置要这样改server { listen 80; server_name www.example.com; location /demo { 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_pass后面没有带URI所以用户请求/demo/login时后端Tomcat收到的也是/demo/login和应用自身的context path完美匹配。写完配置之后一定先检查语法nginx -t输出类似下面这样就说明配置没问题nginx: configuration file /etc/nginx/nginx.conf test is successful如果输出报错别急着重启Nginx先根据提示把配置语法改对。我之前因为少写一个分号nginx -t直接挂掉幸好没有直接reload不然服务就中断了。3.5 重载配置并验证配置生效不需要重启Nginx重载就够了sudo nginx -s reload # 或者 sudo systemctl reload nginxreload是平滑重载旧进程继续处理存量连接新进程用新配置处理新连接不会中断服务。生产环境修改配置后一律用reload不要用restart。验证分几步走。第一步本地curl测试curl -I http://127.0.0.1/ -H Host: www.example.com加上-H Host: www.example.com是为了在本地请求时模拟域名访问让Nginx找到匹配的server块。如果配置正确返回的响应头里能看到Server: nginx/1.x.x。第二步直接访问服务器公网IP或域名确认页面能打开。同时观察Nginx的access log看看请求是不是正常转发、状态码是多少tail -f /var/log/nginx/example_access.log正常的一条记录长这样192.168.1.100 - - [10/Oct/2025:10:00:01 0800] GET / HTTP/1.1 200 1234 Mozilla/5.0...状态码是200说明整个链路是通的。这时候再去看Tomcat的日志可以看到来自127.0.0.1的请求记录说明请求确实被Nginx转发到了Tomcat。3.6 更复杂的场景一个Nginx代理多个Tomcat如果服务器上有两个Tomcat应用分别跑在8080和8081域名也不一样用两个server块区分就可以server { listen 80; server_name app1.example.com; location / { 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; } } server { listen 80; server_name app2.example.com; location / { proxy_pass http://127.0.0.1:8081; 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; } }两个server块监听同一个80端口Nginx根据请求里的Host字段区分该走哪条配置互不干扰。只要域名解析到了这台服务器而且Nginx能匹配到对应的server_name流量就能正确到达各自的后端。如果只有一个域名、两个路径分别对应两个应用就用location来分server { listen 80; server_name www.example.com; location /app1/ { 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; } location /app2/ { proxy_pass http://127.0.0.1:8081; 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; } }这种按路径分流的方式要特别注意2.2节讲的路径替换规则前端的路径和后端应用的context path必须对齐否则404是轻的最怕遇到那种“页面能开但CSS全挂”的隐性错误。4. 常见问题与排查技巧实录4.1 502 Bad Gateway的排查流程反代配置完遇到最多的就是502。502的本意是“Nginx作为网关没能从上游服务器拿到有效响应”。拿到502先别慌按下面这套流程走。第一步看Nginx错误日志tail -f /var/log/nginx/example_error.log502对应的日志会给出失败原因。最常见的几种connect() failed (111: Connection refused) while connecting to upstream这个直接告诉你Nginx尝试连接后端时被拒绝了。接下来用ss -lntp | grep 8080看Tomcat到底在不在监听。如果Tomcat没启动或者监听端口和配置对不上那502就解释得通。connect() failed (110: Connection timed out) while connecting to upstream连接超时。可能是后端IP/端口写错了也可能是防火墙把端口拦了。用telnet 127.0.0.1 8080或者curl -v http://127.0.0.1:8080/测一下后端是否通。upstream prematurely closed connection while reading response header from upstream后端连接提前关闭多见于后端应用崩溃、重启或者应用侧超时把连接断掉了。这时候去看Tomcat日志确认有没有报错或者OOM。第二步确认Nginx配置里proxy_pass的地址对不对。一个常见的低级错误是打算代理本机Tomcat结果写成了http://192.168.1.100:8080而当前服务器IP根本不是这个。尤其是从网上抄配置的时候地址忘改的情况非常多。第三步确认后端Tomcat确实能直接访问。直接在服务器上curl后端地址如果curl都不通那问题就不在Nginx先把后端修好。4.2 首页能打开但CSS、图片全部404这个问题的典型特征是页面结构出来了但样式全丢控制台里一堆404。原因通常有两个。第一个原因是静态资源路径问题。页面里引用的CSS路径是/css/style.css浏览器请求的时候会访问http://www.example.com/css/style.css。如果这个请求没有命中静态资源location而是走到了location /转发给TomcatTomcat里又没有对应资源自然404。解决办法是确认正则location覆盖了这些文件类型而且目录里确实有文件。用curl -I http://127.0.0.1/css/style.css -H Host: www.example.com直接测试看是Nginx返回的还是后端返回的。第二个原因是root和alias用错。我前面踩过一个坑某个项目把静态文件放在/opt/app/static配置写成location /static/ { root /opt/app; }用户请求/static/app.cssNginx找的是/opt/app/static/app.css没问题。但另一个项目静态文件放在/opt/app/assets同事图省事把配置改成location /static/ { root /opt/app/assets; }结果用户请求/static/app.cssNginx找的是/opt/app/assets/static/app.css目录根本不存在全部404。正确写法是location /static/ { alias /opt/app/assets/; }这就是root拼接路径和alias替换前缀的区别。遇到404先想清楚你到底想让Nginx去哪个目录找文件再决定用哪个指令。4.3 登录后跳转地址错误或无限重定向配置反代后经常遇到一种“灵异现象”登录成功之后浏览器被重定向到一个内网地址或者http://127.0.0.1:8080/xxx页面直接打不开。严重的时候会导致无限重定向、浏览器报“重定向次数过多”。根源在于后端应用不知道外部的真实协议和域名。应用收到请求后生成一个跳转地址比如用户请求的是http://www.example.com/login应用处理完重定向到/dashboard它可能生成http://127.0.0.1:8080/dashboard。如果应用通过request.getServerName()拿到的Host是127.0.0.1:8080它就会重定向到那里去。解决思路有两条。第一条确保Nginx把原始Host传给后端这是2.3节里proxy_set_header Host $host在做的事。第二条在Tomcat里配置RemoteIpValve让Tomcat信任Nginx传来的头这在2.3节也讲过了。如果跳转的地址是http://www.example.com/dashboard但总是循环重定向通常是协议判断出了问题。比如Nginx层已经终止了HTTPS传给后端的是HTTP应用里又配置了“强制HTTPS访问”于是后端收到HTTP请求后返回301跳转HTTPSNginx又用HTTP转发应用又跳HTTPS……如此循环。解决办法是让后端正确识别外部协议proxy_set_header X-Forwarded-Proto $scheme;然后在Tomcat侧配置RemoteIpValve让request.getScheme()能读到X-Forwarded-Proto的值。这样应用就知道用户实际是用HTTPS访问的不会再强制跳转。还有一招Nginx的proxy_redirect指令可以主动改写后端返回的Location头。比如后端返回http://127.0.0.1:8080/xxx你希望用户跳转到http://www.example.com/xxx可以这样写proxy_redirect http://127.0.0.1:8080/ http://www.example.com/;这个属于兜底方案尽量优先解决后端对外部协议和域名的感知改应用或者改Tomcat配置才是正路靠Nginx硬改Location头终归是打补丁。4.4 长接口超时与大文件下载中断前面2.4节提到过Nginx默认的proxy_read_timeout是60秒。如果一个接口要计算很久逻辑处理后端还没返回数据Nginx就会主动断开连接用户侧表现为504 Gateway Time-out。排查方法也很简单看Nginx错误日志里有没有upstream timed out (110: Operation timed out) while reading response header from upstream。如果有说明确实是后端处理超时不是网络问题。针对这些耗时接口的location单独调大超时时间location /api/report/ { proxy_pass http://127.0.0.1:8080; proxy_read_timeout 300s; proxy_send_timeout 300s; }大文件下载中断的问题则通常是proxy_buffering和缓冲区大小引起的。下载大文件时如果Nginx缓冲开得很大会先把整个文件攒到内存或磁盘再慢慢发给用户下载过程中断一下重连就可能拿到不完整文件。对下载专用location关闭缓冲直接流式转发下载体验更稳定location /download/ { proxy_pass http://127.0.0.1:8080; proxy_buffering off; proxy_read_timeout 300s; }4.5 如何快速确认请求是否真的走了Nginx有时候配置完了页面能打开但你不确定流量到底走没走Nginx直接测试一下最靠谱。在服务器上运行curl -I http://127.0.0.1/ -H Host: www.example.com响应头里如果出现Server: nginx/1.20.1说明请求确实是Nginx处理的。如果出现Server: Apache-Coyote/1.1说明请求直接打到了Tomcat你的Nginx没生效——常见原因包括端口没改对、server_name没匹配上、或者你访问的地址根本不是Nginx监听的地址。再看一眼Tomcat的访问日志。如果Nginx反代生效Tomcat日志里所有请求的客户端IP都是127.0.0.1如果出现别的外网IP说明有人绕过了Nginx直连了Tomcat端口安全上必须处理具体做法在第5章。另外在浏览器开发者工具里看响应头也能看到Server字段。这个方法在本地机器上一样有效不用登录服务器就能快速判断。5. 安全加固与性能优化建议5.1 锁住Tomcat端口不暴露到公网反代配置好以后最重要的一件事确保外网访问不到Tomcat的8080端口。最简单粗暴且有效的方式是让Tomcat只监听本机回环地址。打开Tomcat的conf/server.xml找到Connector配置Connector port8080 protocolHTTP/1.1 connectionTimeout20000 redirectPort8443 address127.0.0.1 /加上address127.0.0.1之后Tomcat只会监听本机Nginx在服务器内部通过127.0.0.1:8080访问完全不受影响但外部任何机器都无法直接连上8080端口。这一招比防火墙配置更彻底因为即使防火墙规则被改乱了端口也不会裸露。如果你需要保留Tomcat远程管理能力就不要用这个方式改成在防火墙层面管控。CentOS/RHEL系列用firewalldsudo firewall-cmd --permanent --remove-port8080/tcp sudo firewall-cmd --permanent --add-servicehttp sudo firewall-cmd --permanent --add-servicehttps sudo firewall-cmd --reloadUbuntu用ufw的话sudo ufw allow 80/tcp sudo ufw allow 443/tcp sudo ufw deny 8080/tcp sudo ufw reload原则很简单对外只留80和443其他端口一律不放行。5.2 Nginx层访问控制与限流就算Tomcat已经锁在内网Nginx这层也可以再加上访问控制。比如有些管理后台路径只允许特定IP访问可以在location里配置location /admin/ { allow 192.168.1.0/24; deny all; proxy_pass http://127.0.0.1:8080; proxy_set_header Host $host; }allow写在deny前面表示只允许192.168.1.0/24网段的访问其他全部拒绝。Nginx会直接返回403连后端都不转发。再配合限流防止恶意请求把后端打垮。Nginx的limit_req模块可以做简单的速率限制。在http块里定义限流区域http { limit_req_zone $binary_remote_addr zoneapi_limit:10m rate10r/s; }然后在需要限流的location里引用location /api/ { limit_req zoneapi_limit burst20 nodelay; proxy_pass http://127.0.0.1:8080; proxy_set_header Host $host; }rate10r/s表示每个IP每秒最多10个请求burst20允许瞬时冲到30个请求1020超出的直接返回503。这个配置对保护后端接口很有效比如登录接口、短信接口这种容易被刷的入口单独加个限流能省不少事。5.3 HTTPS终结放在Nginx层现在的站点基本标配HTTPS。有了Nginx反代HTTPS证书的处理可以全部交给NginxTomcat继续保持纯HTTP模式运行即可。这也是反代架构的一个好处——后端应用不需要关心证书配置证书统一在Nginx层管理。Nginx开启HTTPS的简化配置长这样server { listen 443 ssl; server_name www.example.com; ssl_certificate /etc/nginx/ssl/example.pem; ssl_certificate_key /etc/nginx/ssl/example.key; ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers HIGH:!aNULL:!MD5; location / { 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; } }注意X-Forwarded-Proto这一行Nginx会自动把外部协议这里是https传给后端。后端按照2.3节的方法配置好RemoteIpValve之后应用层面的request.getScheme()、request.isSecure()都会正确返回不会出现HTTPS和HTTP判断错乱的问题。证书获取可以申请免费的证书输入域名后自动颁发也可以手动上传证书文件到服务器再指定上面的路径。生产环境一定要启用HTTPS能用证书管理工具做自动续期就更好省得每年手动换证书。5.4 日志优化与性能参数调优排查问题最依赖的就是日志。默认的Nginx日志格式信息量不够尤其是看不到后端处理耗时。自定义一个日志格式把upstream的响应时间记录下来log_format main $remote_addr - $remote_user [$time_local] $request $status $body_bytes_sent $http_referer $http_user_agent $http_x_forwarded_for upstream_addr$upstream_addr upstream_response_time$upstream_response_time request_time$request_time; access_log /var/log/nginx/example_access.log main;配置完之后重载Nginx日志里每行末尾会出现upstream_response_time0.023这样的字段这个就是后端Tomcat实际处理请求花了多少秒。接口慢的时候看一眼日志就能定位是后端慢还是网络慢不用再靠猜。性能参数方面主配置nginx.conf里值得关注的几项worker_processes auto; worker_connections 10240; keepalive_timeout 65; gzip on; gzip_types text/plain text/css application/json application/javascript;worker_processes auto让Nginx自动匹配CPU核心数不用手动数核数。worker_connections 10240每个worker进程能同时处理的连接数上限提高数值能扛更多并发但内存占用也会增加。gzip on对文本类型响应开启压缩页面体积能小一半以上。部署的时候要留意后端是否已经在响应头里设置了Content-Encoding如果后端已经压缩过Nginx这层就不要再重复压一遍。这些参数按需调整不要一味往大调。连接数开太大而内存不够反而会被系统OOM杀掉。最后几点实际体会折腾Nginx反代这几年踩过不少坑也有几点体会特别想分享。准备任何一条反代配置之前先把我前面表格里的那条proxy_pass规则背下来不带斜杠原样转发、带斜杠替换前缀。这个规则太容易出问题了尤其是从网上复制配置的时候别人写的路径和后端应用的实际路径不一定匹配。我现在写配置都会在每条代理规则后面保留注释标清楚“前端访问路径”和“后端实际收到路径”的对应关系看着不费劲出问题也好排查。另外配完反代之后一定要做两件收尾工作。第一验证外部真的访问不了后端Tomcat的端口方法很简单在本地浏览器直接访问http://服务器IP:8080/如果连不上说明端口封锁生效了。第二看一下Nginx的access log和后端Tomcat的localhost日志确认流量确实经过Nginx。很多环境打开页面正常但实际上有人绕过Nginx直连Tomcat这个问题在安全评审时很容易被点名。Nginx反代Tomcat这套组合放在今天依然是Web服务部署的经典范式。它的路数一点不复杂前面一个高性能入口后面挂业务服务中间靠一套清晰的转发规则串联起来。把这套逻辑吃透了后面再接触负载均衡、网关、服务注册发现这些概念都会顺很多。
阅读完成 · 觉得有帮助?
咨询建站