You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SpringBoot/Tomcat/Apache反向代理POST请求返回403排查与修复

问题概述
  • 部署环境:未集成Spring Security的SpringBoot应用,运行于Tomcat容器,前端使用Apache做反向代理
  • 故障现象:发起POST请求时返回403 Forbidden状态码
  • 核心诉求:
    • 明确HTTP请求/响应中哪些特征会触发403返回码
    • 定位403的具体返回组件(SpringBoot/Tomcat/Apache反向代理)
    • 给出对应修复方案
      请求响应截图

现有配置参考

pom.xml 依赖

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-math3</artifactId>
    <version>3.6.1</version>
</dependency>

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-collections4</artifactId>
    <version>4.4</version>
</dependency>

<dependency>
    <groupId>org.springdoc</groupId>
    <artifactId>springdoc-openapi-ui</artifactId>
    <version>${open-api}</version>
</dependency>

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <scope>provided</scope>
</dependency>

<dependency>
    <groupId>org.postgresql</groupId>
    <artifactId>postgresql</artifactId>
    <scope>runtime</scope>
</dependency>

<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
</dependency>

<dependency>
    <groupId>org.jfree</groupId>
    <artifactId>jfreechart</artifactId>
    <version>1.0.19</version>
</dependency>

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-mail</artifactId>
</dependency>

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
    <exclusions>
        <exclusion>
            <groupId>org.junit.vintage</groupId>
            <artifactId>junit-vintage-engine</artifactId>
        </exclusion>
    </exclusions>
</dependency>

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-webflux</artifactId>
</dependency>

Apache httpd.conf 配置

SSLCipherSuite 'kEECDH+ECDSA kEECDH kEDH HIGH +SHA !aNULL !eNULL !LOW !MEDIUM !MD5 !EXP !DSS !PSK !SRP !kECDH SSLHonorCipherOrder On
SSLRandomSeed startup file:/dev/urandom 2048
SSLRandomSeed connect builtin

DocumentRoot /apache/htdocs

AddDefaultCharset utf-8

<VirtualHost *:80>
  RewriteEngine   On
  RewriteRule     ^/(.*)$   https://%{HTTP_HOST}/$1    [redirect,last]
  <Directory /apache/htdocs>
    Require all granted
    Options None
    AllowOverride None
  </Directory>
</VirtualHost>

<VirtualHost *:443>
  SSLEngine On
  <Directory /apache/htdocs>
    Require all granted
    Options None
    AllowOverride None
  </Directory>
  SSLEngine on
  SSLCertificateFile "/etc/letsencrypt/live/www.guli.com/fullchain.pem"
  SSLCertificateKeyFile "/etc/letsencrypt/live/www.guli.com/privkey.pem"
</VirtualHost>


<IfModule mod_proxy.c>
  <Location "/">
    ProxyPass "http://localhost:50080/main/" connectiontimeout=5 timeout=600
    ProxyPassReverse "http://localhost:50080/main/"
    ProxyPassReverseCookiePath "/main/" "/"
    ProxyPreserveHost On
    Header always set Access-Control-Allow-Origin "*"
    Header always set Access-Control-Allow-Headers "x-requested-with, Content-Type, origin, authorization, accept, client-security-token"
    Header always set Access-Control-Allow-Methods "POST, GET, OPTIONS, DELETE, PUT"
    Header always set Access-Control-Expose-Headers "Content-Security-Policy, Location"
    Header always set Access-Control-Max-Age "600"
  </Location>
# so läuft mal: http://www.guli.com
  <Location "/email">
    ProxyPass "http://localhost:50099/email"
    ProxyPassReverse "http://localhost:50099/email"
    ProxyPassReverseCookiePath "/email" "/"
    RequestHeader set X-Forwarded-Prefix "/email"
    ProxyPreserveHost On
  </Location>
# /ta/swagger-ui.html 
  <Location "/ta">
    ProxyPass "http://localhost:50086/ta"
    ProxyPassReverse "http://localhost:50086/ta"
    ProxyPassReverseCookiePath "/ta" "/"
    RequestHeader set X-Forwarded-Prefix "/ta"
    ProxyPreserveHost On
  </Location>
# pres/swagger-ui.html
  <Location "/pres">
    ProxyPass "http://localhost:50083/pres"
    ProxyPassReverse "http://localhost:50083/pres"
    ProxyPassReverseCookiePath "/pres" "/"
    RequestHeader set X-Forwarded-Prefix "/pres"
    ProxyPreserveHost On
  </Location>
  <Location "/guli-web">
    ProxyPass "http://localhost:50096/guliadmin-web" connectiontimeout=5 timeout=600
    ProxyPassReverse "http://localhost:50096/guliadmin-web"
    ProxyPassReverseCookiePath "/guliadmin-web" "/guliadmin-web"
    ProxyPreserveHost On
  </Location>
  <Location "/.well-known">
    ProxyPass "!"
  </Location>
  ProxyRequests Off
  ProxyVia Off
  ProxyStatus On
</IfModule>

问题解答

一、常见触发403状态码的请求特征

403本质是服务器识别到请求但拒绝放行,不同组件触发的常见场景如下:

  • Apache层触发:
    • 配置了IP黑白名单,来源IP被拦截
    • 目录访问权限配置错误,请求路径无访问授权
    • 跨域配置不匹配,预检OPTIONS请求被拦截
    • WAF/安全模块识别请求包含恶意特征(比如SQL注入、XSS特征参数)
    • 请求方法被显式禁止
  • Tomcat层触发:
    • 启用了CSRF防护,POST请求未携带合法CSRF令牌
    • 请求头/请求体大小超过配置阈值
    • 配置了虚拟路径访问权限限制
    • 内置CORS过滤器拦截非法跨域请求
    • 特殊请求方法被禁止
  • SpringBoot层触发(无Spring Security场景):
    • 自定义拦截器/过滤器返回403(比如权限校验、签名校验不通过)
    • CORS全局配置不匹配拦截请求
    • 静态资源路径权限配置错误
    • 请求方法与接口定义不匹配(多数场景返回405,部分自定义配置会返回403)

二、403返回组件定位方法

按从外到内的顺序逐层排查,效率最高:

  1. 绕过Apache直接访问Tomcat端口测试
    在服务器本地执行curl命令直接请求Tomcat对应的服务端口,比如curl -X POST http://localhost:50080/main/你的接口路径 -d '请求参数' -i,如果直接访问Tomcat返回403,说明问题出在Tomcat或SpringBoot层;如果直接访问正常,说明403是Apache层返回。
  2. 查看Apache日志
    Apache默认日志路径在/var/log/httpd/或/apache/logs/下,查看对应时间的access_log和error_log,如果日志中记录了该请求的403响应,且有对应模块拦截的报错信息(比如mod_security、mod_access_compat的拦截记录),即可确认是Apache返回。
  3. 区分Tomcat和SpringBoot返回
    如果绕过Apache访问仍然返回403,查看Tomcat的localhost_access_log和catalina.out日志:如果Tomcat日志记录该请求已经转发到应用,且应用日志有对应请求的打印,说明是SpringBoot层自定义逻辑返回;如果Tomcat日志没有请求转发到应用的记录,且有对应拦截报错,说明是Tomcat层返回。也可以通过响应头特征判断:Tomcat返回的403响应头通常带有Server: Apache-Coyote/1.1标识,SpringBoot自定义返回的响应头通常带有应用自定义Header。

三、结合当前配置的高频问题点与修复

从现有配置看,最高发的原因有三个:

  1. 跨域预检请求未放行
    你在Apache根路径配置了CORS规则,但没有对OPTIONS请求做放行处理。浏览器发送跨域POST请求时会先发OPTIONS预检请求,如果Apache没有给OPTIONS请求返回200,就会直接触发403。
    修复方法:在<Location "/">配置块中添加OPTIONS请求放行规则:
    RewriteEngine On
    RewriteCond %{REQUEST_METHOD} OPTIONS
    RewriteRule ^(.*)$ $1 [R=200,L]
    
    配置完成后重启Apache即可。
  2. Tomcat CSRF防护拦截
    如果是外部Tomcat部署,且web.xml中配置了CsrfPreventionFilter,会拦截未携带合法CSRF令牌的POST请求。
    修复方法:不需要CSRF防护就直接在Tomcat的web.xml中移除CsrfPreventionFilter配置;需要保留的话,前端请求时携带从Cookie中获取的CSRF令牌即可。
  3. 代理路径斜杠匹配问题
    根路径代理配置转发到http://localhost:50080/main/带末尾斜杠,如果POST请求路径拼接后不符合Tomcat端的上下文路径,部分Tomcat版本的路径校验规则也会返回403,可以尝试去掉ProxyPass和ProxyPassReverse的末尾斜杠测试:
    ProxyPass "http://localhost:50080/main" connectiontimeout=5 timeout=600
    ProxyPassReverse "http://localhost:50080/main"
    

内容的提问来源于stack exchange,提问作者user2023141

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.03 09:33:41