记一次nginx+tomcat8请求400错误的坑
文章目录
背景
环境信息
- nginx:1.4.2 https
- tomcat:8.5.34 http
以上环境信息为背景,通过nginx访问前台正常,但是工程中某个和外部系统联调的请求中,始终报400 Bad Request
;但是直接通过tomcat访问,该请求正常;
问题现象
- 前台报错
- nginx后台access.log报错
10.189.147.210 - - [09/Oct/2018:22:25:14 +0800] "GET /daxp/xxcheck/data_query/SQL_CUSTOM_ALL_REG/1%2320697%2324%7C29%7C-18%7C-74%7C-96%7C30%7C46%7C-97%7C-70%7C-69%7C-90%7C23%7C126%7C-28%7C-61%7C19%7C27%7C-45%7C-111%7C-18%7C16%7C108%7C-105%7C-33%7C-1 HTTP/1.1" 400 5 "https://192.168.37.11:8080/daxp/xxcheck/getjk?dimCode=SQL_CUSTOM_ALL_REG" "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.87 Safari/537.36"
- tomcat后台报错
无
问题排查及处理过程
- nginx配置如下
upstream daxpnew {
server 192.168.37.100:8080 ;
ip_hash;
}
server {
listen 192.168.37.11:8080 ssl;
server_name _;
access_log off;
client_max_body_size 200M;
error_page 404 502 /404.html;
ssl_certificate /home/ssl/nginx/nginx/ca.ssl/server.crt;
ssl_certificate_key /home/ssl/nginx/nginx/ca.ssl/server.key;
ssl_session_timeout 10m;
ssl_protocols SSLv2 SSLv3 TLSv1;
ssl_ciphers ALL:!kEDH!ADH:RC4+RSA:+HIGH:+EXP;
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;
port_in_redirect on;
location / {
root html;
index index.html index.htm;
}
location /daxp {
proxy_pass http://daxpnew/daxp;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host:$server_port;
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_read_timeout 1800;
proxy_connect_timeout 1800;
root html;
index index.html index.htm;
}
百度及google搜索,有人说400错误原因和解决办法有几种:
- request header过大所引起,request过大,通常是由于cookie中写入了较大的值所引起。 在nginx.conf中,将
client_header_buffer_size
和large_client_header_buffers
都调大,可缓解此问题。 - 客户端的调用方式没有使用host 参数,传递了空的Host头给服务端,一旦Nginx设置了
proxy_set_header Host $host
,空Host头就传给了后端。然而,在http 1.1的规范中,Host只要出现空,就会返回400,所以出现了这个故障。而对于需要在Host字段里带上端口信息的,则仍需要配置proxy_set_header Host $host:$server_port
。 - 在server下加入
server_name _;
- 在tomcat的server.xml中加入一下配置
<Valve className="org.apache.catalina.valves.RemoteIpValve"
portHeader="x-forwarded-port"
remoteIpHeader="x-forwarded-for"
proxiesHeader="x-forwarded-by"
protocolHeader="x-forwarded-proto" />
以上方法都试过一次,改配置的都配置了,但是还是出现400错误;折腾了大半天,无果,哎,心累;
问题解决
在tomcat日志中,一直有个报错
11-Oct-2018 11:44:26.346 INFO [http-nio-8080-exec-2] org.apache.coyote.http11.Http11Processor.service Error parsing HTTP request header
Note: further occurrences of HTTP header parsing errors will be logged at DEBUG level.
java.lang.IllegalArgumentException: Invalid character found in the request target. The valid characters are defined in RFC 7230 and RFC 3986
at org.apache.coyote.http11.Http11InputBuffer.parseRequestLine(Http11InputBuffer.java:484)
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:684)
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:806)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1498)
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:745)
因为这个报错一直都存在,而且在服务请求报400错误的时候并没有报出来这个错误,就忽略掉了,今天我突然仔细查询这个报错和之前有点不一样,之前的报错如下,但是下面的报错并不影响前台功能
10-Oct-2018 14:43:12.531 INFO [http-nio-8080-exec-7] org.apache.coyote.http11.Http11Processor.service Error parsing HTTP request header
Note: further occurrences of HTTP header parsing errors will be logged at DEBUG level.
java.lang.IllegalArgumentException: Invalid character found in method name. HTTP method names must be tokens
at org.apache.coyote.http11.Http11InputBuffer.parseRequestLine(Http11InputBuffer.java:428)
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:684)
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:806)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1498)
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:745)
百度了下现在的报错,Invalid character found in the request target. The valid characters are defined in RFC 7230 and RFC 3986
,也就是说我们的请求中用了无效的字符。查看RFC规范知,url中只允许包含英文字母(a-zA-Z)、数字(0-9)、-_.~四个特殊字符以及保留字符( ! * ’ ( ) ; : @ & = + $ , / ? # [ ] ) (26*2+10+4+18=84)
这84个字符.而我们的请求中出现了|、{}大括号,所以tomcat报错.。
Tomcat从 7.0.73, 8.0.39, 8.5.7 版本后添加了对Url的限制。
- 配置tomcat支持|{}等字符的方法是:在
catalina.properties
中添加tomcat.util.http.parser.HttpParser.requestTargetAllow=|{}
但是只支持7.0.76, 8.0.42, 8.5.12 之后的版本(这些版本之后支持设置上述属性) - Tomcat从 7.0.73, 8.0.39, 8.5.7 版本后添加了对Url的限制。
于是修改了 catalina.properties
的配置文件,重启tomcat,再次测试服务请求正常,不再报400错误;
遗留问题
- 在没有配置
catalina.properties
前,通过tomcat直接请求服务正常,但是通过nginx请求错误,不知道差异在哪儿; org.apache.coyote.http11.Http11Processor.service Error parsing HTTP request heade
这个报错暂时没有发现影响前台功能,所以目前未处理(偷个懒);
总结
造成这次错误的原因如下
- 由于头一天我们的tomcat7.0.72升级到了tomcat8.5.34,之前的tomcat没有对Url进行限制,但是新版本的tomcat配置未改,而且我们并不知道tomcat7和8的差异;
- 在查看tomcat日志的时候没有仔细,并没有发现用tomcat7和tomcat8的报错的差异;