NGINX 499错误代码的可能原因

223

我遇到了很多 499 NGINX 错误码。我知道这是一个客户端问题,而不是 NGINX 或我的 uWSGI 堆栈的问题。当我收到 499 时,我注意到在 uWSGI 日志中存在相关性。

address space usage: 383692800 bytes/365MB} {rss usage: 167038976
bytes/159MB} [pid: 16614|app: 0|req: 74184/222373] 74.125.191.16 ()
{36 vars in 481 bytes} [Fri Oct 19 10:07:07 2012] POST /bidder/ =>
generated 0 bytes in 8 msecs (HTTP/1.1 200) 1 headers in 59 bytes (1
switches on core 1760)
SIGPIPE: writing to a closed pipe/socket/fd (probably the client
disconnected) on request /bidder/ (ip 74.125.xxx.xxx) !!!
Fri Oct 19 10:07:07 2012 - write(): Broken pipe [proto/uwsgi.c line
143] during POST /bidder/ (74.125.xxx.xxx)
IOError: write error

我正在寻找更深入的解释,并希望我的NGINX配置没有问题,而不是单纯地接受表面上的情况。看起来像是客户端问题。


你是否找到了解决这个问题的方法?我在使用uWSGI和nginx时遇到了完全相同的问题。 - Raj
1
当我中止一个jQuery ajax请求时,我就明白了。 - mpen
16个回答

284

在Nginx中,HTTP 499表示客户端在服务器回应请求前关闭了连接。根据我的经验,通常是由于客户端超时引起的。据我所知,这是一个特定于Nginx的错误代码。


7
作为特殊情况,我注意到有时候最终用户会双击表单提交按钮。这将导致表单发送两次,但客户端只期望收到一次响应。可以通过在JS中第一次单击按钮时禁用它们(至少几秒钟)来解决这个问题。 - Antoine Pinsard
59
需要注意的是,“客户端”实际上可能是代理。例如,如果您正在使用负载均衡器,由于超时,它可能会取消对nginx服务器的请求。 - Brad Koch
2
如果用户关闭选项卡并且我的API请求未完成,则会在我的Angular应用程序上发生这种情况。 - Vivek Saurabh
4
需要注意的是,这也可能是由于 服务器 引起的;如果服务器响应时间过长,客户端会放弃请求。 - ijoseph
在我的情况下,由于CloudFront源在等待来自目标的响应之前超时关闭了连接,因此AWS负载均衡器必须关闭连接。在源中增加响应和连接超时值对我有帮助。 - Yugendran
在我的情况下,VPN 导致了 499 状态码,随后重新提交了请求。 - Ryan DuVal

116

在我的情况下,我很不耐烦,结果误解了日志。

事实上,真正的问题出在nginx和uwsgi之间的通信上,而不是浏览器和nginx之间。如果我在浏览器中加载网站并等足够长的时间,我会得到一个“504 - Bad Gateway”的错误。但是等待这么长时间,我一直试着尝试其他方法,然后刷新浏览器。所以我从来没有等足够长的时间看到504错误。当在浏览器中刷新时,前一个请求被关闭,Nginx将其记录为499。

详细说明

在这里,我假设读者和我开始尝试时一样不懂。

我的设置是反向代理,nginx服务器和应用程序服务器,uWSGI服务器在其后面。所有来自客户端的请求都将发送到nginx服务器,然后转发到uWSGI服务器,然后响应以相同方式返回。我认为这就是每个人使用nginx / uwsgi并应该使用的方式。

我的nginx正常工作,但是uwsgi服务器有问题。uwsgi服务器无法响应nginx服务器有两种方式(可能更多)。

1) uWSGI说:“我正在处理,请等待,您很快会得到响应。” nginx有一定的时间,愿意等待,例如20秒。之后,它将向客户端发送504错误。

2)uWSGI已经死了,或者在nginx等待时uWSGi死亡。 nginx立即看到并在这种情况下返回499错误。

我通过在客户端(浏览器)发送请求测试了我的设置。但是在浏览器中没有任何反应,它只是一直挂着。大约10秒钟后(少于超时时间),我得出结论某些事情不对劲(这是真的),然后从命令行关闭了uWSGI服务器。然后我会去uWSGI设置尝试一些新的东西,然后重新启动uWSGI服务器。我关闭uWSGI服务器的那一刻,nginx服务器就会返回一个499错误。

因此,我继续调试499错误,这意味着要搜索499错误。但如果我等待足够长的时间,我将得到504错误。如果我遇到了504错误,我将能够更好地理解问题,然后进行调试。

因此,结论是,问题出在uWGSI上,它一直挂起(“再等一会儿,再等一会儿,然后我就能给你答案了......”)。

我如何解决那个问题,我不记得了。我猜可能是由许多事情引起的。


1
你最终是如何解决这个问题的?我也遇到了同样的问题,但一直没有找到原因。 - Colin Nichols
1
我添加了一些详细说明,不幸的是,我认为它并不能解决你的问题。 - Mads Skjern
1
只是想说谢谢!我遇到了完全相同的情况,这让我走上了正确的轨道。 - Aaron
3
@Shafiul:我的阐述并没有解释是什么导致了uWSGI的问题,它只是说明了uWSGI是问题的原因(而不是nginx)。阐述描述了症状以及我如何误解这些症状。我理解你的失望,但你误解了我的回答的实质。谢谢。 - Mads Skjern
6
非常有用的答案,永远不要删除!这些概念应该在文档中详细说明,你通过阐述其行为方式与文档所表述的有所不同,提供了极大的帮助! - jerclarke
显示剩余4条评论

47

"client closed the connection"中的“client”并不一定是Web浏览器!

如果你在用户和Nginx之间使用AWS或haproxy的负载均衡服务,你可能会在Nginx日志文件中发现499错误。在这种配置下,负载均衡服务将作为Nginx服务器的客户端,同时又作为Web浏览器的服务器,代理数据来回传输。

对于haproxy,某些适用超时的默认值为连接到上游以及从上游(Nginx)或下游(Web浏览器)读取的大约60秒。

这意味着,如果在大约60秒后,代理还没有连接到上游进行写入,或者如果它没有收到任何来自下游(Web浏览器)或上游(Nginx)的HTTP请求或响应数据,那么它将关闭相应的连接,这将被Nginx视为错误,至少在后者正在处理请求时(耗时过长)。

超时可能发生在繁忙的网站或需要更多执行时间的脚本上。您可能需要找到适合您的超时值,例如将其延长到较大的数字,如180秒。这可能会为您解决问题。
根据您的设置,您可能会在浏览器中看到504 Gateway Timeout HTTP错误,这可能表明php-fpm出现了问题。但是,在日志文件中出现499错误不是这种情况。

2
非常感谢,你救了我的一天 :'(。我花了很多天来解决这个问题。我的问题与 haproxy 中的超时有关。我从未意识到。 - Spring

27

当您看到499连接中止的错误日志时,这通常是由于后端服务器响应过慢导致产生的。可能是其他代理超时或用户软件中止了连接。因此,请检查uWSGI是否响应迅速,以及uWSGI/数据库服务器上是否存在负载。

在许多情况下,用户和nginx之间有其他代理。其中一些可能在您的基础架构中,例如CDN、负载均衡器、Varnish缓存等。其他可能在用户端,例如缓存代理等。

如果您的基础架构中有负载均衡器/CDN等代理,则应将超时时间设置为先超时后端,然后逐渐超时其他代理,直至用户。

如果您有:

user >>> CDN >>> Load Balancer >>> Nginx >>> uWSGI

我建议您设置:

  • n秒作为uWSGI超时时间
  • n+1秒作为nginx超时时间
  • n+2秒作为负载均衡器的超时时间
  • n+3秒作为CDN的超时时间。

如果您无法设置某些超时时间(例如CDN),请查找其超时时间并相应地调整其他超时时间(如nn-1等)。

这提供了正确的超时链,并且您将发现谁给出了超时,并向用户返回正确的响应代码。


24
原来499确实意味着“客户端中断连接”。
我有一个客户端的“读取超时”设置为60秒(nginx也有一个默认的proxy_read_timeout为60秒)。在我的情况下,nginx会出现错误日志upstream timed out (110: Connection timed out) while reading upstream,然后nginx会重试“您配置的后端服务器组中的下一个代理服务器”。如果您有多个,则是这样的。
然后它尝试下一个和下一个,直到(按default)耗尽所有内容。随着每个超时,它也将它们从“活动”的后端服务器列表中删除。在全部用尽后,它返回504网关超时。 因此,在我的情况下,nginx将服务器标记为“不可用”,在下一个服务器上重新尝试,然后我的客户端的60s超时(立即)发生,所以我会看到一个upstream timed out (110: Connection timed out) while reading upstream日志,紧接着是一个499日志。但这只是时间巧合。
相关:
如果组中的所有服务器都标记为当前不可用,则还会返回502 Bad Gateway。 10秒钟。请参见here max_fails和fail_timeout。在日志中,它将说no live upstreams while connecting to upstream. 如果您的服务器组中只有一个代理后端,则它只会尝试一个服务器,并在超过proxy_read_timeout时返回504 Gateway Time-out,并且不会将单个服务器从“活动”服务器列表中删除。请参见此处,“如果组中只有一个服务器,则忽略max_fails、fail_timeout和slow_start参数,这样的服务器永远不会被视为不可用。”
真正棘手的部分是,如果您将proxy_pass指定为“localhost”,并且您的计算机同时具有ipv6和ipv4的“localhost”版本(大多数计算机默认情况下都有),则它将被视为在服务器组中列出了多个服务器的“列表”,这意味着您可能会遇到上述情况,即使您只列出一个服务器,它也会返回“502 for 10s”。请参见此处,“如果域名解析为多个地址,则所有地址都将以轮询方式使用。”
一种解决方法是将其声明为proxy_pass http://127.0.0.1:5001;(其ipv4地址),以避免它同时成为ipv6和ipv4。然后它就会被视为“只有一个服务器”的行为。

有几个不同的设置可以调整,使这个问题“减轻”一些。比如增加超时时间或使它在服务器超时时不标记为“禁用”...或修复列表,使其只有1个大小,参见上文 :)

另请参阅: https://serverfault.com/a/783624/27813


1
在将localhost作为proxy_pass添加时,遇到了“502 for 10s”的问题。你的回答对于理解潜在问题非常有帮助,谢谢! - Antoine

12

在我的情况下,当客户端的API在收到响应之前关闭连接时,我得到了499错误。字面上发送了一个POST并立即关闭连接。 这可以通过以下选项解决:

proxy_ignore_client_abort on

Nginx文档


4
我不理解这如何有所帮助。 - Vladimir Starkov
1
也许这不是你的情况?客户端发送数据,对它们的后续处理和响应并不感兴趣。但我的应用程序需要处理这些数据。如果没有这个选项,数据就无法及时到达我的应用程序。 - DerSkythe
哇!那几乎就是我需要的。唯一要添加的是,在 Webhook 源关闭连接之前,发送 200 响应。否则,他们往往会禁用 Webhook 并且不再发送它们... 我可以为选定的 URL 这样做吗? - pilat
这解决了我的问题。我在GKE中使用nginx ingress控制器。 - john
9
这并不能解决你的客户未收到响应的问题,它只是消除了日志中的499错误,并用状态码200替换了它们。这样做是个坏主意。真正的解决办法是告诉你的客户增加他们的超时设置... - marcinx
显示剩余6条评论

6

使用标准的nginx配置和php-fpm,很容易重现此错误。

在页面上按住F5按钮会向服务器创建数十个刷新请求。每个前一个请求都会被浏览器取消,在新的刷新中。在我的情况下,我在客户的在线商店日志文件中发现了数十个499。从nginx的角度来看:如果响应在下一个刷新请求之前没有传递给客户端,nginx将记录499错误。

mydomain.com.log:84.240.77.112 - - [19/Jun/2018:09:07:32 +0200] "GET /(path) HTTP/2.0" 499 0 "-" (user-agent-string)
mydomain.com.log:84.240.77.112 - - [19/Jun/2018:09:07:33 +0200] "GET /(path) HTTP/2.0" 499 0 "-" (user-agent-string)
mydomain.com.log:84.240.77.112 - - [19/Jun/2018:09:07:33 +0200] "GET /(path) HTTP/2.0" 499 0 "-" (user-agent-string)
mydomain.com.log:84.240.77.112 - - [19/Jun/2018:09:07:33 +0200] "GET /(path) HTTP/2.0" 499 0 "-" (user-agent-string)
mydomain.com.log:84.240.77.112 - - [19/Jun/2018:09:07:33 +0200] "GET /(path) HTTP/2.0" 499 0 "-" (user-agent-string)
mydomain.com.log:84.240.77.112 - - [19/Jun/2018:09:07:34 +0200] "GET /(path) HTTP/2.0" 499 0 "-" (user-agent-string)
mydomain.com.log:84.240.77.112 - - [19/Jun/2018:09:07:34 +0200] "GET /(path) HTTP/2.0" 499 0 "-" (user-agent-string)
mydomain.com.log:84.240.77.112 - - [19/Jun/2018:09:07:34 +0200] "GET /(path) HTTP/2.0" 499 0 "-" (user-agent-string)
mydomain.com.log:84.240.77.112 - - [19/Jun/2018:09:07:34 +0200] "GET /(path) HTTP/2.0" 499 0 "-" (user-agent-string)
mydomain.com.log:84.240.77.112 - - [19/Jun/2018:09:07:35 +0200] "GET /(path) HTTP/2.0" 499 0 "-" (user-agent-string)
mydomain.com.log:84.240.77.112 - - [19/Jun/2018:09:07:35 +0200] "GET /(path) HTTP/2.0" 499 0 "-" (user-agent-string)

如果php-fpm处理时间很长(例如一个较重的WP页面),当然可能会出现问题。我听说过php-fpm崩溃的情况,但我认为可以通过正确配置服务来防止这种情况,例如处理对xmlrpc.php的调用。


6

我知道这是一个老的线程,但它确切地符合最近发生在我的身上的事情,所以我想在这里记录一下。Docker中的设置如下:

  • nginx_proxy
  • nginx
  • 运行实际应用程序的php_fpm。

症状是应用登录提示出现了“502网关超时”。检查日志发现:

  • 该按钮通过HTTP的POST/login工作...因此...
  • nginx-proxy收到了/login请求,并最终报告了超时。
  • nginx返回了499响应,这当然意味着“主机已死亡”。
  • /login请求在FPM服务器日志中根本没有出现!
  • FPM中没有追溯或错误消息......没有,零,无。

事实证明问题是连接数据库验证登录失败。但如何找出这个问题纯粹就是瞎猜。

完全缺少应用追溯日志……甚至没有记录请求被FPM接收的记录…对我来说是一个完全(而且,毁灭性的)惊喜。是的,应用程序应该记录故障,但在这种情况下,看起来FPM工作进程因运行时错误而死亡,导致nginx返回499响应。现在,这显然是我们应用程序中的问题...出了什么问题。但我想记录发生的情况,以便下一个遇到类似问题的人受益。


3
nginx 返回了一个 499 响应,当然意味着“主机已死”,似乎不正确。应该是“nginx 记录了一个 499 响应,这意味着‘客户端不再等待 nginx’”。 - Michael Freidgeim

5

这并没有回答OP的问题,但由于我在搜索答案时最终到达了这里,我想分享一下我们发现的情况。

在我们的情况下,原来这些499是预期的。例如,当用户使用某些搜索框的“自动填充”功能时,我们会在日志中看到类似于这样的信息。

GET /api/search?q=h [Status 499] 
GET /api/search?q=he [Status 499]
GET /api/search?q=hel [Status 499]
GET /api/search?q=hell [Status 499]
GET /api/search?q=hello [Status 200]

所以在我们的情况下,我认为安全起见可以使用proxy_ignore_client_abort on,这是之前的答案中建议的。谢谢!

4

我从Google搜索来到这里。

我在其他地方找到了答案--> https://dev59.com/c2Uo5IYBdhLWcg3w_DlK#15621223

答案是增加AWS弹性负载均衡器的连接空闲超时时间!

(我设置了一个Django网站,使用nginx / apache反向代理和一个非常非常长的后端作业/视图已经超时)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接