最近在项目中遇到一个浏览器返回431 Request header fields too large的错误,这个错误很好理解就是传给服务器时header内容过大,header内容包括Cookie,用户的header以及浏览器自带的一些Header内容。官方文档解释如下:
The HTTP 431 Request Header Fields Too Large response status code indicates that the server refuses to process the request because the request’s HTTP headers are too long. The request may be resubmitted after reducing the size of the request headers.
431 can be used when the total size of request headers is too large, or when a single header field is too large. To help those running into this error, indicate which of the two is the problem in the response body — ideally, also include which headers are too large. This lets users attempt to fix the problem, such as by clearing their cookies.
这段英文很容易理解,解决方法也很简单明了就是减少header内容,清除该域下的cookie等。我就顺着这个思路去解决项目中的这个难题,但还是走了不少弯路,现在把思路整理出来,仅供参考。
项目背景是在项目中用到了Keycloak作为用户认证和授权服务器(Identity Server),Keycloak会根据一些用户信息,资源信息和授权信息产生access token,这个token信息会存放在浏览器的Cookie中。Nginx在项目中作为反向代理服务器。
第一步,检测是否是浏览器的问题
首先想到的是Cookie过大,浏览器不支持。如是F12打开浏览器的调试窗口,发现发生431的请求Cookie很大,如下图,如是上网查询浏览器支持的Cookie大小。隆重推荐这个网站http://browsercookielimits.squawky.net/ , 此网站可以查询你当前浏览器支持的cookie大小。下图二是我的Chrome支持的Cookie大小。从分析结果得出,每个域下最多180个Cookie,每个Cookie最大长度是4k,因此180×4k=720k,支持Cookie的大小远远高于我当前的Cookie大小。大Cookie也被拆成了两个Cookie,图中的BPS Cookie被拆成了bps和bps2两个cookie,保证每个Cookie的尺寸小于4k,见图一,因此可以判定不是浏览器支持的问题。第二步,减少Cookie的大小
由于Cookie是Keycloak按照hash算法生成的token信息,于是开始研究如何减少cookie的大小。在Keycloak里的官网中找到如下解释:Keep in mind browser cookie limits if you use access or refresh tokens in the browser cookie. Keycloak-generic-adapter divides the cookie automatically if your cookie is longer than 4093 bytes. Real size of the cookie depends on the content of the issued access token. Also, encryption might add additional bytes to the cookie size. If you have large cookies (>200 KB), you might reach browser cookie limits. All cookies are part of the header request, so you might find a problem with the max headers size limits in your infrastructure (some load balancers have very low this value, such as 8 KB). Be sure that all network devices have sufficient header size limits. Otherwise, your users won’t be able to obtain an access token.
这段文字解释了Keycloak的Cookie有可能会在某些浏览器上因为尺寸的限制而不被支持。至于如何减少access token的大小,在Keycloak的Console工具里就可以找到产生token的内容。在Client的Scopes下面可以配置那些role将会被生成到access token中,在Mapper中也需要配置那些用户信息需要生成到access token中。于是乎把一些项目中不会用到的信息从access token中移除,来减少access token的大小,修改完配置以后确实可以解决问题。但是由于我们的项目中role这个信息是必须生成到access token中,而且这个信息会不断的增加,因此随时会有暴表的可能,因此这种方法不可取。
第三步,检测Nginx
经过上面的两步,可以断定是由于Keycloak生成token的太大导致出现431,但不是因为浏览器支持的问题,因此怀疑是不是Nginx反向代理导致的,于是查找Nginx对header size的配置,如下。按照如下配置,问题依然存在,因此不是Nginx导致的。server {
...
client_body_buffer_size 32k;
client_header_buffer_size 8k;
large_client_header_buffers 416k;
... }
第四步,检测后端服务
不是Nginx导致的,最后只有可能是反向代理的后端服务的问题,于是乎检测这个请求代理到的后端服务如下:其中server-backend是一个Node express部署的Web程序,因此怀疑是不是Nginx将header请求传送给Node express Web程序导致报出431呢。于是,网上查找Node express支持的Header大小.如下图,location /{
limit_req zone=mylimit burst=20 nodelay;
limit_conn myaddr 50;
access_by_lua_file lua/auth.lua;
add_header Allow"GET" always;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Server $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For
proxy_pass http://server-backend/;
if( $request_method !~^(GET)$ ){
return405;
}
}
水落石出
现在问题有点眉目了,Node express只能支持最大8K的header数据。之前是给出400错误,现在统一成了431错误。问题找到了,就好解决。方案一,根据上面的建议,增加max-http-header-size配置,设成80k
方案二,减少传给Node express web的Cookie大小
在我的项目中,由于前端程序不需要用到此Cookie,因此直接在Nginx里把反向代理给后端的Cookie去了,就能彻底解决问题。因此修改了nginx的配置,proxy_set_header Cookie “”;配置如下:
location /{
limit_req zone=mylimit burst=20 nodelay;
limit_conn myaddr 50;
access_by_lua_file lua/auth.lua;
add_header Allow"GET" always;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Server $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For;
proxy_set_header Cookie "";proxy_pass http://server-backend/;
if( $request_method !~^(GET)$ ){
return405;
}
}
网友评论