Possível razão para códigos de erro NGINX 499

116

Estou recebendo muitos códigos de erro 499 NGINX. Vejo que esse é um problema do lado do cliente. Não é um problema com o NGINX ou minha pilha uWSGI. Observo a correlação nos logs do uWSGI quando recebo um 499.

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

Estou procurando uma explicação mais detalhada e espero que não haja nada de errado com minha configuração NGINX para uwsgi. Estou assumindo o valor de face. Parece um problema do cliente.

Tampa
fonte
Você já encontrou uma solução para isso? Vejo exatamente o mesmo problema com uWSGI e nginx.
Raj
1
Eu entendo quando aborto uma solicitação jQuery ajax.
maio,
1
Eu sei que esta é uma pergunta muito antiga, mas a quantidade de perguntas mal colocadas no SO é impressionante. Isso claramente pertence ao SF.
Sosukodo de

Respostas:

163

HTTP 499 em Nginx significa que o cliente fechou a conexão antes que o servidor respondesse à solicitação. Na minha experiência, geralmente é causado pelo tempo limite do cliente . Como eu sei, é um código de erro específico do Nginx.

mrbo
fonte
1
Como um caso especial, percebi que às vezes acontece quando o usuário final clica duas vezes em um botão de envio de formulário. O formulário é enviado duas vezes, mas apenas uma resposta é esperada pelo cliente. Isso pode ser contornado desativando (pelo menos por alguns segundos) os botões em JS na primeira vez que eles forem clicados.
Antoine Pinsard
14
É importante observar que o "cliente" pode na verdade ser um proxy. Por exemplo, se você estiver usando um balanceador de carga, ele pode cancelar a solicitação para o servidor nginx devido a um tempo limite.
Brad Koch de
Isso acontece no meu APP Angular se o usuário fechar a guia e minhas solicitações de API não forem concluídas.
Vivek Saurabh
É importante observar que isso também pode ser causado pelo servidor ; se o servidor demorar muito para responder, o cliente desiste.
ijoseph
78

No meu caso, fiquei impaciente e acabei interpretando mal o log.

Na verdade, o verdadeiro problema era a comunicação entre o nginx e o uwsgi, e não entre o navegador e o nginx. Se eu tivesse carregado o site no meu navegador e tivesse esperado o suficiente, teria obtido um "504 - Bad Gateway". Mas demorou tanto, que continuei tentando algumas coisas, e depois atualizei no navegador. Portanto, nunca esperei o suficiente para ver o erro 504. Ao atualizar no navegador, é quando a solicitação anterior é fechada e o Nginx grava isso no log como 499.

Elaboração

Aqui, assumirei que o leitor sabe tão pouco quanto eu sabia quando comecei a brincar.

Minha configuração foi um proxy reverso, o servidor nginx, e um servidor de aplicativos, o servidor uWSGI por trás dele. Todas as solicitações do cliente iriam para o servidor nginx, depois seriam encaminhadas para o servidor uWSGI e a resposta seria enviada da mesma forma. Eu acho que é assim que todo mundo usa nginx / uwsgi e deve usá-lo.

Meu nginx funcionou como deveria, mas algo estava errado com o servidor uwsgi. Existem duas maneiras (talvez mais) em que o servidor uwsgi pode falhar ao responder ao servidor nginx.

1) uWSGI diz: "Estou processando, espere e você receberá uma resposta em breve". O nginx tem um certo período de tempo, que está disposto a esperar, fx 20 segundos. Depois disso, ele responderá ao cliente, com um erro 504.

2) uWSGI está morto, ou uWSGi morre enquanto o nginx está esperando por ele. O nginx vê isso imediatamente e, nesse caso, retorna um erro 499.

Estava testando minha configuração fazendo solicitações no cliente (navegador). No navegador não aconteceu nada, apenas continuou travando. Depois de talvez 10 segundos (menos que o tempo limite), concluí que algo não estava certo (o que era verdade) e fechei o servidor uWSGI da linha de comando. Em seguida, iria para as configurações do uWSGI, tentaria algo novo e, em seguida, reiniciaria o servidor uWSGI. No momento em que fechei o servidor uWSGI, o servidor nginx retornaria um erro 499.

Continuei depurando com o erro 499, o que significa pesquisar no Google pelo erro 499. Mas se eu tivesse esperado o suficiente, teria obtido o erro 504. Se eu tivesse obtido o erro 504, teria sido capaz de entender o problema melhor e, então, ser capaz de depurar.

Portanto, a conclusão é que o problema era com o uWGSI, que ficava pendurado ("Espere um pouco mais, só mais um pouco, então terei uma resposta para você ...").

Não me lembro como resolvi esse problema. Acho que pode ser causado por muitas coisas.

Mads Skjern
fonte
1
Como você acabou resolvendo isso? Estou tendo o mesmo problema e não consigo identificar a causa.
Colin Nichols
1
Acrescentei uma elaboração, infelizmente, não acho que vai resolver o seu problema.
Mads Skjern
1
Só queria dizer obrigado! Eu tive exatamente a mesma situação e isso me colocou no caminho certo.
Aaron
3
@Shafiul: Minha elaboração não explica o que causou o problema com uWSGI, apenas explica que uWSGI foi a causa (e não nginx). A elaboração descreve os sintomas e como os interpretei mal. Eu entendo sua decepção, mas você entendeu mal a essência de minha resposta. Atenciosamente.
Mads Skjern
2
Resposta extremamente útil, nunca exclua! Esses conceitos devem ser detalhados na documentação em algum lugar, você faz um grande serviço ao elaborar como ele se comporta de maneira diferente do que os documentos implicariam!
jerclarke
21

O cliente fechou a conexão não significa que é um problema do navegador !? De modo nenhum!

Você pode encontrar 499 erros em um arquivo de log se tiver um LB (balanceador de carga) na frente do seu servidor da web (nginx) AWS ou haproxy (personalizado). Dito isso, o LB atuará como cliente do nginx.

Se você executar os valores padrão do haproxy para:

    timeout client  60000
    timeout server  60000

Isso significa que o LB atingirá o tempo limite após 60000ms se não houver resposta do nginx. Os tempos limite podem ocorrer para sites ou scripts ocupados que precisam de mais tempo para execução. Você precisará encontrar um tempo limite que funcione para você. Por exemplo, estenda-o para:

    timeout client  180s
    timeout server  180s

E você provavelmente estará pronto.

Dependendo da sua configuração, você poderá ver um erro de tempo limite do gateway 504 em seu navegador, o que indica que algo está errado com o php-fpm, mas esse não será o caso com erros 499 em seus arquivos de log.

Mrki
fonte
12

Conforme você aponta 499um aborto de conexão registrado pelo nginx. Mas geralmente isso é produzido quando seu servidor de back-end está muito lento e outro proxy atinge o tempo limite primeiro ou o software do usuário aborta a conexão. Portanto, verifique se o uWSGI está respondendo rápido ou não ou se há carga no servidor do uWSGI / Banco de dados.

Em muitos casos, existem alguns outros proxies entre o usuário e o nginx. Alguns podem estar em sua infraestrutura, como talvez um CDN, Load Balacer, um cache de Varnish etc. Outros podem estar no lado do usuário, como um proxy de cache etc.

Se houver proxies do seu lado, como um LoadBalancer / CDN ... você deve definir os tempos limite para expirar primeiro seu back-end e progressivamente os outros proxies para o usuário.

Se você tem:

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

Vou recomendar que você defina:

  • n segundos para o tempo limite do uWSGI
  • n+1 segundos para o tempo limite do nginx
  • n+2 senconds para expirar o balanceador de carga
  • n+3 segundos de tempo limite para o CDN.

Se você não pode definir alguns dos tempos de espera (como CDN), encontre qual é o seu tempo de espera e ajuste os outros de acordo com ele ( n, n-1...).

Isso fornece uma cadeia correta de tempos limite. e você descobrirá realmente quem está dando o tempo limite e retorna o código de resposta correto para o usuário.

Bartomeu
fonte
8

No meu caso, recebi 499 quando a API do cliente fechou a conexão antes de obter qualquer resposta. Literalmente enviou um POST e feche imediatamente a conexão. Isso é resolvido por opção:

proxy_ignore_client_abort em

Nginx doc

DerSkythe
fonte
3
Eu não entendo como isso ajuda
Vladimir Starkov
Talvez não seja o seu caso? O cliente envia os dados e não está interessado no que acontecerá com ele e qual será a resposta. Mas meu aplicativo deve processar os dados. Sem essa opção, os dados simplesmente não têm tempo de chegar ao meu aplicativo.
DerSkythe
Obrigado. Sintomas exatos e solução perfeita.
TTimo
Uau! Isso é quase exatamente o que preciso. A única coisa que eu adicionaria - seria enviar 200 respostas para a fonte do webhook um pouco antes fechar a própria conexão. Caso contrário, eles tendem a desativar os webhooks e não os enviar novamente ... Posso fazer isso para URLs selecionados?
pilat
1
Isso não resolve o problema de seu cliente não obter uma resposta. Ele apenas elimina 499 erros em seus logs e os substitui pelo código de status 200. Má ideia fazer isso. A solução real é dizer ao seu cliente para aumentar a configuração de tempo limite ...
marcinx
7

Acontece que 499 realmente significa "conexão interrompida pelo cliente".

Eu tive um cliente que leu o tempo limite de 60s (e o nginx também tem um proxy_read_timeout padrão de 60s). Então, o que estava acontecendo no meu caso é que o nginx iria error.log upstream timed out (110: Connection timed out) while reading upstreame então nginx tentaria novamente "o próximo servidor proxy no grupo de servidores de backend que você configurou." Isso se você tiver mais de um.

Em seguida, ele tenta o próximo e o próximo até (por padrão ) ter esgotado todos eles. À medida que cada um atinge o tempo limite, ele também os remove da lista de servidores back-end "ativos". Depois que todos estão exaustos, ele retorna um504 gateway timeout.

Então, no meu caso, o nginx marcou o servidor como "indisponível", tentei novamente no próximo servidor, o 60stempo limite do meu cliente (imediatamente) ocorreu, então eu veria um upstream timed out (110: Connection timed out) while reading upstreamlog, seguido imediatamente por um log 499. Mas foi apenas coincidência de tempo.

Relacionado:

Se todos os servidores do grupo estiverem marcados como indisponíveis no momento, ele retornará um 502 Bad Gateway.por 10s também. Veja aqui max_fails e fail_timeout. Entre as toras vai dizerno live upstreams while connecting to upstream.

Se você tiver apenas um back-end de proxy em seu grupo de servidores, tente apenas um servidor e retorne um 504 Gateway Time-out e não remove o único servidor da lista de servidores "ativos", se proxy_read_timeoutfor ultrapassado. Veja aqui "Se houver apenas um único servidor em um grupo, os parâmetros max_fails, fail_timeout e slow_start são ignorados e tal servidor nunca será considerado indisponível."

A parte realmente complicada é que se você especificar proxy_pass para "localhost" e sua caixa também tiver ipv6 e ipv4 "versões de localização" ao mesmo tempo (a maioria das caixas tem por padrão), contará como se você tivesse uma "lista" de vários servidores em seu grupo de servidores, o que significa que você pode entrar na situação acima em que o retorno é "502 por 10s", embora você liste apenas um servidor . Veja aqui "Se um nome de domínio resolver para vários endereços, todos eles serão usados ​​em rodízio." Uma solução alternativa é declará-lo como proxy_pass http://127.0.0.1:5001;(seu endereço ipv4) para evitar que seja ipv6 e ipv4. Então, ele conta como comportamento de "apenas um único servidor".

Existem algumas configurações diferentes que você pode ajustar para tornar isso "menos" problemático. Como aumentar o tempo limite ou fazer com que não marque os servidores como "desativados" quando o tempo limite for atingido ... ou consertar a lista para que tenha apenas tamanho 1, veja acima :)

Veja também: https://serverfault.com/a/783624/27813

rogerdpack
fonte
3

Este erro é muito fácil de reproduzir usando a configuração nginx padrão com php-fpm.

Manter o botão F5 pressionado em uma página criará dezenas de solicitações de atualização para o servidor. Cada solicitação anterior é cancelada pelo navegador em uma nova atualização. No meu caso, encontrei dezenas de 499 no arquivo de log da loja online do meu cliente. Do ponto de vista do nginx: Se a resposta não tiver sido entregue ao cliente antes da próxima solicitação de atualização, o nginx registra o erro 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)

Se o processamento php-fpm demorar mais (como uma página WP pesada), pode causar problemas, é claro. Já ouvi falar de travamentos do php-fpm, por exemplo, mas acredito que eles podem ser evitados configurando serviços adequadamente, como lidar com chamadas para xmlrpc.php.

Karvonen
fonte
2

... veio aqui de uma pesquisa no google

Encontrei a resposta em outro lugar aqui -> https://stackoverflow.com/a/15621223/1093174

que aumentava o tempo limite de inatividade da conexão do meu balanceador de carga elástico da AWS!

(Eu configurei um site Django com proxy reverso nginx / apache, e um trabalho / visualização de back-end de log realmente, realmente, estava expirando)

David Lam
fonte
0

Assim que recebi 499 "A solicitação foi proibida pelo antivírus" como uma resposta http AJAX (falso positivo do Kaspersky Internet Security com análise heurística leve, a análise heurística profunda sabia corretamente que não havia nada de errado).

TeeJay
fonte
0

Eu encontrei esse problema e a causa foi devido ao plug-in Kaspersky Protection no navegador. Se você estiver encontrando isso, tente desabilitar seus plug-ins e veja se isso corrige o seu problema.

EGN
fonte
0

Um dos motivos para esse comportamento pode ser que você está usando httppara em uwsgivez de socket. Use o comando abaixo se estiver usando uwsgidiretamente.

uwsgi --socket :8080 --module app-name.wsgi

O mesmo comando no arquivo .ini é

chdir = /path/to/app/folder
socket = :8080
module = app-name.wsgi
Penkey Suresh
fonte
0

Isso não responde à pergunta dos OPs, mas como acabei aqui depois de procurar furiosamente por uma resposta, queria compartilhar o que descobrimos.

Em nosso caso, esses 499s são esperados. Quando os usuários usam o recurso de digitação antecipada em algumas caixas de pesquisa, por exemplo, vemos algo assim nos logs.

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]

Portanto, no nosso caso, acho seguro usar o proxy_ignore_client_abort onque foi sugerido em uma resposta anterior. Obrigado por isso!

Ron DeFulio
fonte
0

De minha parte eu tinha habilitado, ufwmas esqueci de expor minhas portas upstreams ._.

Alexandre Daubricourt
fonte
0

No meu caso, configurei como

AWS ELB >> ECS(nginx) >> ECS(php-fpm).

Eu havia configurado o grupo de segurança AWS errado para o serviço ECS (php-fpm), então o Nginx não foi capaz de acessar o contêiner de tarefas php-fpm. É por isso que eu estava recebendo erros no log de tarefas do nginx

499 0 - elb-healthchecker/2.0

A verificação de saúde foi configurada para verificar o serviço php-fpm e confirmar que está ativo e dar uma resposta.

Piyush Sonigra
fonte
0

Sei que este é um tópico antigo, mas corresponde exatamente ao que me aconteceu recentemente e pensei em documentá-lo aqui. A configuração (no Docker) é a seguinte:

  • nginx_proxy
  • nginx
  • php_fpm executando o aplicativo real.

O sintoma era um "502 Gateway Timeout" no prompt de login do aplicativo. Exame de registros encontrados:

  • o botão funciona via HTTP POSTpara /login... e então ...
  • O nginx-proxy obteve a /loginsolicitação e, eventualmente, relatou um tempo limite.
  • O nginx retornou uma 499resposta, o que obviamente significa "o host morreu".
  • a /loginsolicitação não apareceu (!) nos logs do servidor FPM!
  • não havia rastreamentos ou mensagens de erro no FPM ... nada, zero, zippo, nenhum.

Descobriu-se que o problema era uma falha ao conectar ao banco de dados para verificar o login. Mas como descobrir isso acabou sendo pura suposição.

A completa ausência de logs de rastreamento do aplicativo ... ou mesmo um registro de que a solicitação foi recebida pelo FPM ... foi uma completa (e devastadora ...) surpresa para mim. Sim, o aplicativo deve registrar as falhas, mas, neste caso, parece que o processo de trabalho do FPM morreu com um erro de tempo de execução, levando à 499resposta do nginx. Agora, isso obviamente é um problema em nosso aplicativo ... em algum lugar. Mas eu queria registrar os detalhes do que aconteceu para o benefício das próximas pessoas que enfrentarem algo assim.

Mike Robinson
fonte