SSD Nodes Learn 🎉 VPS desde $4.99/mês
Guias Matt ConnorPor Matt Connor · Atualizado 2026-08-07

HTTP: guia de métodos, status e logs para sysadmins

Entenda HTTP no servidor: metodos, codigos de status, cabecalhos, logs do nginx e como TLS e HTTP/3 entram no fluxo de cada requisicao.

O que é HTTP?

HTTP (protocolo de transferência de hipertexto) é o conjunto de regras usado por um cliente e por um servidor web para pedir algo e devolvê-lo. O cliente envia um pedido: um método como GET, um caminho como /pricing, uma versão do protocolo, uma lista de cabeçalhos e, por vezes, um corpo. O servidor responde com um código de estado como 200, seguido pelos seus próprios cabeçalhos e, normalmente, por um corpo. Cada visualização de página e cada chamada de API (interface de programação de aplicações) no seu servidor é essa troca, repetida.

HTTP não mantém estado por si só. O servidor não se lembra do que pediu há um segundo, por isso qualquer comportamento que funcione como memória, como uma sessão de início de sessão, é transportado num cabeçalho em cada pedido. Esta propriedade explica grande parte do que se segue: a colocação em cache é totalmente controlada por cabeçalhos, e um balanceador de carga pode enviar o seu próximo pedido para um backend diferente sem causar problemas.

Tudo o que se segue mostra como esse modelo funciona no lado do servidor, no seu access log e na sua configuração do nginx.

Uma requisição e uma resposta brutas, com anotações

Esta é uma requisição HTTP/1.1 completa. Uma linha em branco termina os cabeçalhos, e tudo o que vem depois dessa linha é o corpo. Uma GET normalmente não tem corpo.

GET /pricing HTTP/1.1
Host: example.com
User-Agent: curl/8.5.0
Accept: */*
Accept-Encoding: gzip
  • GET é o método, que indica a operação pretendida. GET lê dados, POST envia dados, PUT substitui um recurso, DELETE remove um recurso e HEAD solicita os cabeçalhos de uma GET sem o corpo.
  • /pricing é o caminho. O nome do host não faz parte da linha de requisição, por isso existe o cabeçalho seguinte.
  • HTTP/1.1 é a versão do protocolo usada pelo cliente.
  • Host: example.com identifica o site pretendido pelo cliente. HTTP/1.1 exige esse cabeçalho, por isso nginx responde a uma requisição sem ele com 400 Bad Request.
  • Os restantes cabeçalhos definem preferências. Accept-Encoding: gzip indica que o cliente consegue descomprimir dados, portanto o servidor pode comprimir o corpo.

A resposta tem a mesma estrutura, mas começa por uma linha de estado.

HTTP/1.1 200 OK
Date: Thu, 06 Aug 2026 09:12:44 GMT
Server: nginx
Content-Type: text/html; charset=utf-8
Content-Length: 5310
Cache-Control: public, max-age=300

<!doctype html>...
  • 200 OK é o código de estado com a respetiva frase descritiva. O código é o que importa. A frase é apenas decorativa e os clientes ignoram-na.
  • Content-Type indica ao cliente como deve tratar os bytes seguintes.
  • Content-Length é o tamanho do corpo em bytes, para que o cliente saiba onde o corpo termina. Quando o tamanho não é conhecido antecipadamente, o servidor envia Transfer-Encoding: chunked e assinala o fim com um bloco de tamanho zero.
  • Cache-Control indica ao navegador e a qualquer cache intermédia durante quanto tempo podem manter esta resposta.
  • A linha em branco depois dos cabeçalhos separa-os do corpo, nos dois sentidos.

Os nomes dos cabeçalhos não diferenciam maiúsculas de minúsculas, e cada linha termina com um carriage return seguido de um line feed, em vez de uma quebra de linha isolada. Não irá escrever estes elementos manualmente, mas irá encontrá-los numa captura de pacotes.

Para observar um par real, execute isto contra um site que administre:

curl -sS -o /dev/null -D - https://example.com/

-D - escreve os cabeçalhos da resposta no terminal e -o /dev/null descarta o corpo. Prefira isto a curl -I, porque -I envia uma requisição HEAD. Um servidor de aplicações que trate HEAD de forma diferente de GET, como acontece frequentemente, irá mostrar-lhe cabeçalhos que nenhum navegador recebe. curl -v imprime os dois lados, com as linhas de requisição marcadas como > e as linhas de resposta marcadas como <.

Como é a linha de pedido no seu log de acesso do nginx

O nginx fornece um formato de log combined, cuja definição é:

log_format combined '$remote_addr - $remote_user [$time_local] '
                    '"$request" $status $body_bytes_sent '
                    '"$http_referer" "$http_user_agent"';

Uma linha produzida por esse formato:

203.0.113.45 - - [06/Aug/2026:09:12:44 +0000] "GET /pricing HTTP/1.1" 200 5310 "https://example.com/" "Mozilla/5.0 (X11; Linux x86_64) Chrome/127.0.0.0 Safari/537.36"
  • 203.0.113.45 é $remote_addr, o endereço que abriu a ligação TCP (protocolo de controlo de transmissão). Atrás de um proxy, este é o endereço do proxy, não o do visitante.
  • O primeiro - é um marcador fixo. O segundo é $remote_user, preenchido apenas quando é usada autenticação básica HTTP.
  • "GET /pricing HTTP/1.1" é $request, a linha de pedido copiada exatamente como chegou.
  • 200 é o estado devolvido pelo seu servidor, não o estado observado pelo visitante.
  • 5310 é $body_bytes_sent, apenas o corpo da resposta. Os cabeçalhos da resposta não são contabilizados, por isso este número é sempre inferior ao número de bytes efetivamente enviados.
  • Os dois últimos campos entre aspas são Referer e User-Agent. Ambos vêm do cliente, por isso podem conter qualquer valor.

Como $request é copiado sem alterações, conteúdo inválido aparece sem alterações. Um cliente que fala TLS (segurança da camada de transporte) com a sua porta 80 em texto simples deixa uma linha 400 cujo campo de pedido começa com bytes escapados como "\x16\x03\x01\x02\x00\x01". \x16 é o tipo de registo do handshake TLS, por isso esses bytes são o início de um ClientHello e não de uma linha de pedido. O seu servidor está a comportar-se corretamente. Há algo a apontar HTTPS para uma porta HTTP.

Adicione também $server_protocol ao formato do log. Este campo apresenta HTTP/1.1, HTTP/2.0 ou HTTP/3.0 e é a forma mais rápida de provar que uma alteração de protocolo teve realmente efeito.

O que significam os códigos de estado comuns quando o seu próprio site os devolve

O primeiro dígito indica a classe, e é a classe que deve consultar primeiro.

2xx significa que funcionou. 200 OK para uma leitura normal. 201 Created depois de um POST que criou algo. 204 No Content para uma operação bem-sucedida sem conteúdo a devolver, que é a resposta habitual a um DELETE.

3xx significa que deve procurar noutro local. 301 é permanente e os browsers fazem uma cache persistente, por vezes até o utilizador limpar o seu perfil; por isso, um 301 que aponte para o nome de host errado é difícil de desfazer. Use 302 enquanto ainda está a testar um redirecionamento. 304 Not Modified é uma operação bem-sucedida, não um erro: o cliente enviou If-None-Match com um ETag (entity tag) que ainda reconhece, por isso respondeu com cabeçalhos e sem corpo. Uma grande quantidade de 304s no log significa que o armazenamento em cache está a funcionar.

4xx significa que o pedido estava errado. 400 Bad Request indica uma entrada malformada. 401 Unauthorized significa realmente que não houve autenticação e deve incluir um cabeçalho WWW-Authenticate que indique o esquema. 403 Forbidden significa que o pedido foi compreendido, mas recusado. 404 Not Found é um caminho que não existe. 405 Method Not Allowed é o caminho correto com o método errado, que é o que um POST para uma localização de ficheiros estáticos devolve. 413 indica um corpo maior do que client_max_body_size do nginx, que por predefinição é de 1 megabyte; o log de erros confirma-o com client intended to send too large body.

Um 403 num ficheiro estático é quase sempre um problema do sistema de ficheiros, não de uma regra HTTP. Consulte /var/log/nginx/error.log antes de alterar qualquer configuração. open() "/srv/site/index.html" failed (13: Permission denied) significa que o utilizador de trabalho do nginx não consegue ler o ficheiro, na maioria dos casos porque um diretório pai não tem permissão de execução para outros utilizadores. directory index of "/srv/site/" is forbidden significa que o caminho correspondeu a um diretório sem ficheiro de índice, enquanto autoindex está desativado.

5xx significa que o problema está do seu lado. 500 é um erro não tratado na sua aplicação. 502 Bad Gateway significa que o nginx não conseguiu obter uma resposta utilizável do upstream, e o log de erros indica a causa: connect() failed (111: Connection refused) while connecting to upstream significa que não há nada a escutar no endereço em proxy_pass. 504 Gateway Timeout significa que o upstream aceitou a ligação e depois não respondeu dentro de proxy_read_timeout, que é de 60 segundos por predefinição; o log regista isto como upstream timed out (110: Connection timed out) while reading response header from upstream. 503 Service Unavailable é uma recusa deliberada. Tenha em atenção que o limitador de taxa do nginx devolve 503, porque limit_req_status tem como predefinição 503. Se estiver à procura de 429 Too Many Requests no log e encontrar 503, essa é a razão. Defina limit_req_status 429; para obter o código correto.

Os cabeçalhos importantes quando o servidor está em execução

Host seleciona o site. Um endereço IP pode servir centenas de nomes de host, e o nginx compara Host com server_name para decidir qual bloco server responde. Se não houver correspondência, o nginx usa o servidor predefinido, que é o primeiro bloco a escutar nesse endereço e nessa porta, exceto se outro estiver marcado como default_server. Receber o site errado de um novo host virtual quase sempre significa isto: o nome não correspondeu e o pedido passou para o servidor predefinido. Teste sem alterar o DNS:

curl -sS -o /dev/null -D - -H 'Host: app.example.com' http://127.0.0.1/

User-Agent é uma autodescrição enviada pelo cliente e contém texto livre. Use-o como indicação ao analisar logs. Nunca o use como mecanismo de controlo, porque um cliente que queira falsificá-lo simplesmente o fará. Por isso, bloquear um scraper por User-Agent filtra apenas os clientes que se identificam corretamente.

Content-Type determina como os bytes são interpretados: application/json para um pedido de API e text/html; charset=utf-8 para uma página. O nginx associa extensões de ficheiro a tipos através de /etc/nginx/mime.types, e o nginx.conf incluído no pacote define default_type application/octet-stream;. Assim, um ficheiro cuja extensão o nginx não conhece é disponibilizado para download em vez de ser renderizado. O sintoma visível é uma página que carrega sem estilos enquanto a consola do navegador apresenta Refused to apply style from ... because its MIME type ('text/plain') is not a supported stylesheet MIME type. MIME significa multipurpose internet mail extensions, o esquema de nomenclatura de onde vêm essas strings de tipo.

Cache-Control permite controlar todas as caches entre o servidor e o leitor. public, max-age=31536000, immutable é adequado para recursos cujo nome de ficheiro contém um hash do conteúdo, porque o nome muda quando o conteúdo muda. no-store deve ser usado em qualquer conteúdo específico do utilizador, porque uma cache partilhada que mantenha uma página com sessão iniciada irá entregá-la à pessoa seguinte que pedir o mesmo URL. private é a opção intermédia: o navegador pode mantê-la, mas uma cache partilhada não.

X-Forwarded-For existe porque um proxy oculta o visitante. Depois de um pedido passar por um reverse proxy, $remote_addr contém o endereço do proxy. Assim, os logs, a geolocalização e a limitação de taxa veem sempre o mesmo cliente. O proxy tem de encaminhar o endereço original:

proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;

Depois, é necessário configurar o servidor que recebe o pedido para confiar nesse endereço e indicar exatamente em quem deve confiar:

set_real_ip_from 10.0.0.0/8;
real_ip_header X-Forwarded-For;

Liste apenas intervalos que controla. X-Forwarded-For é texto simples que qualquer cliente pode enviar, por isso set_real_ip_from 0.0.0.0/0; permite que um visitante escolha o endereço registado nos logs e o endereço contabilizado pelo limitador de taxa.

X-Forwarded-Proto evita uma falha específica e muito comum. O proxy termina o TLS e encaminha o pedido para a aplicação através de HTTP simples. A aplicação vê um pedido simples, decide que o visitante deve usar HTTPS e responde com 301 https://example.com/. O navegador segue esse redirecionamento, o proxy termina novamente o TLS e encaminha novamente HTTP simples. O ciclo repete-se até o navegador desistir com ERR_TOO_MANY_REDIRECTS. Enviar X-Forwarded-Proto: https informa a aplicação de que o visitante já está a usar HTTPS, para que deixe de redirecionar.

HTTP/1.1 vs HTTP/2 vs HTTP/3: o que muda para si

HTTP/1.1 usa texto e processa um pedido de cada vez por ligação. Connection: keep-alive permite que o pedido seguinte reutilize a mesma ligação TCP, poupando o custo de estabelecer uma nova ligação, mas as respostas continuam a chegar pela ordem em que foram pedidas. Uma resposta lenta bloqueia tudo o que estiver em fila atrás dela. Isto é bloqueio de início de fila, e os browsers contornam o problema abrindo várias ligações ao mesmo hostname em simultâneo.

HTTP/2 mantém os mesmos métodos e códigos de estado, mas altera o enquadramento para binário. Vários pedidos partilham uma ligação como streams independentes, e os cabeçalhos repetidos são comprimidos. Isto é relevante porque um pedido moderno transporta muitos cabeçalhos. A ligação continua a usar TCP, portanto a perda de um pacote interrompe todos os streams dessa ligação até à chegada da retransmissão. O bloqueio de início de fila não desapareceu. Desceu da camada HTTP para a camada de transporte. O server push fazia parte do HTTP/2 e, na prática, deixou de ser usado porque o Chrome removeu o suporte em 2022.

HTTP/3 mantém novamente a mesma semântica e substitui o TCP pelo QUIC, um transporte baseado em UDP (user datagram protocol). Os streams QUIC são independentes até à camada inferior, portanto a perda de um pacote interrompe apenas o stream a que esse pacote pertencia. O TLS 1.3 está integrado no handshake do QUIC, em vez de ser colocado sobre ele, pelo que uma nova ligação precisa de menos round trips. Daqui resultam duas consequências práticas: a porta UDP 443 tem de estar aberta em todas as firewalls no caminho, e qualquer rede que limite ou bloqueie UDP fará com que os clientes regressem ao HTTP/2.

O que muda para si, concretamente. Os browsers nunca começam com HTTP/3. Ligam-se através de HTTP/2 ou HTTP/1.1, veem um cabeçalho Alt-Svc: h3=":443"; ma=86400 na resposta e usam HTTP/3 em ligações posteriores a esse host. Por isso, o cabeçalho não é uma decoração opcional. É o mecanismo de descoberta. No nginx, o HTTP/2 passou a ter a sua própria diretiva na versão 1.25.1 (http2 on; dentro do bloco server, substituindo o parâmetro antigo listen ... http2), e o QUIC chegou à versão mainline 1.25.0, na qual um site HTTP/3 precisa de listen 443 quic reuseport; além do listen 443 ssl; normal.

Os proxies diferem quanto ao nível de maturidade desta funcionalidade, e deve verificar isto em relação à versão que executa. Em agosto de 2026, o Caddy serve HTTP/3 por predefinição, sem configuração adicional. O nginx precisa do listener quic explícito e do cabeçalho Alt-Svc descrito acima. O Traefik ativa-o por entry point através de uma opção http3 explícita. Se terminar o TLS no Traefik à frente de várias aplicações Docker, a versão do protocolo recebida pelos visitantes é decidida nesse ponto, e o salto do proxy para o seu container é normalmente HTTP/1.1 simples, independentemente do protocolo negociado pelo browser.

Verifique em vez de assumir. curl --http3 -sS -o /dev/null -D - https://example.com/ só funciona se curl -V listar HTTP3 entre as suas funcionalidades, e a maioria das compilações das distribuições não o inclui. A verificação fiável é o seu próprio log: adicione $server_protocol ao formato e leia o que os browsers reais negociam. Antes disso, confirme que a porta UDP 443 está efetivamente aberta, porque uma firewall que permita apenas TCP 443 fará com que o HTTP/3 falhe silenciosamente, enquanto o site continua a funcionar através de HTTP/2. Saber quais as portas abertas e em escuta no seu servidor Linux é a primeira verificação a fazer.

HTTPS: HTTP e o protocolo, TLS e o encapsulamento

HTTPS nao e um protocolo separado. Sao os mesmos pedidos e os mesmos codigos de estado transportados dentro de uma sessao TLS. A porta 80 transporta-os sem cifragem, e a porta 443 transporta-os cifrados. O handshake TLS termina primeiro; depois, o pedido HTTP circula dentro do canal cifrado. E por isso que um problema de certificado nunca tem um codigo de estado associado: a falha ocorre antes de ser enviado um unico byte HTTP, portanto nao existe resposta a numerar.

Um detalhe da ordem e importante num servidor que aloja varios sites. O certificado e escolhido com base no SNI (server name indication), um campo do handshake TLS que transporta o nome do host sem cifragem, antes de existir qualquer cabecalho HTTP. Assim, o servidor escolhe primeiro um certificado a partir do SNI e depois escolhe um virtual host a partir do cabecalho Host. Sao duas pesquisas separadas que normalmente coincidem. Quando nao coincidem, o browser apresenta um erro de incompatibilidade de nome, como NET::ERR_CERT_COMMON_NAME_INVALID, e nao envia qualquer pedido, porque foi apresentado o certificado do servidor predefinido para um nome que ele nao abrange.

Para um site publico, obtenha um certificado valido e configure a renovacao automatica. Certbot com Let's Encrypt no nginx escreve os caminhos dos certificados no seu bloco de servidor e instala o temporizador de renovacao. Para um nome de host que nenhuma autoridade publica consiga validar, como um nome interno ou um endereco IP simples na sua propria rede, um certificado autoassinado no Ubuntu e a opcao adequada, desde que aceite que todos os clientes tenham de ser configurados para confiar nele.

Quando o TLS funcionar, encaminhe tudo da porta 80 para a porta 443:

server {
    listen 80;
    server_name example.com;
    return 301 https://$host$request_uri;
}

Adicione Strict-Transport-Security apenas quando tiver a certeza. O cabecalho add_header Strict-Transport-Security "max-age=63072000; includeSubDomains" always; informa os browsers de que devem recusar HTTP simples para esse nome de host durante dois anos, e eles obedecem-lhe a partir da propria cache. Isto significa que remover o cabecalho mais tarde nao o anula. Comece com um max-age de algumas horas, confirme que todos os subdominios utilizam realmente HTTPS e depois aumente o valor.

FAQ

Qual é a diferença entre HTTP e HTTPS?

HTTPS é HTTP transportado dentro de uma sessão TLS (transport layer security). Os métodos e os códigos de estado são idênticos. O que muda é que os bytes são cifrados entre o cliente e o componente que termina o TLS, e a porta predefinida passa de 80 para 443. Como o handshake TLS termina antes de ser enviado o primeiro byte HTTP, uma falha de certificado nunca produz um código de estado. Por isso, o aviso de certificado do browser apresenta um nome de erro como NET::ERR_CERT_COMMON_NAME_INVALID, em vez de um número como 403.

Porque é que o meu site devolve 502 Bad Gateway?

Um 502 do nginx significa que o nginx não conseguiu obter uma resposta utilizável do upstream para o qual encaminha os pedidos. O pedido do visitante estava correto, mas algo atrás do nginx não estava. Consulte /var/log/nginx/error.log. connect() failed (111: Connection refused) while connecting to upstream significa que não existe nada a escutar no endereço e na porta indicados em proxy_pass. Verifique se a aplicação está em execução e associada ao endereço esperado. no live upstreams while connecting to upstream significa que todos os servidores do bloco upstream foram marcados como indisponíveis depois de falhas repetidas. Compare com 504 Gateway Timeout, que significa que o upstream aceitou a ligação, mas não respondeu dentro de proxy_read_timeout.

Porque é que o meu access log mostra o mesmo endereço IP para todos os visitantes?

Porque $remote_addr regista o endereço que abriu a ligação TCP. Atrás de um reverse proxy ou de uma content delivery network, esse endereço é o do proxy. O endereço do visitante chega no cabeçalho X-Forwarded-For. Configure proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; no proxy. Depois, no nginx que recebe os pedidos, configure set_real_ip_from com o intervalo de endereços do proxy e real_ip_header X-Forwarded-For;. Indique apenas intervalos que controla, porque esse cabeçalho é texto que qualquer cliente pode enviar. Se confiar nele a partir de toda a internet, um visitante poderá escolher o endereço que regista e o endereço ao qual aplica o rate limit.

Preciso de ativar HTTP/2 ou HTTP/3?

Vale a pena ativar HTTP/2, porque basta uma diretiva num site que já tenha TLS e porque elimina o limite de pedidos por ligação que torna lenta uma página com muitos ficheiros pequenos. HTTP/3 proporciona um ganho menor e menos previsível. Também exige uma porta UDP 443 aberta e uma compilação do proxy com suporte para QUIC. Tenha em conta que os browsers só mudam para HTTP/3 depois de receberem um cabeçalho Alt-Svc numa resposta anterior. Sem esse cabeçalho, nada muda, independentemente do conteúdo da linha listen. Adicione $server_protocol ao formato do log e meça o protocolo que os visitantes realmente negociam antes de investir tempo nesta configuração.

O que significa 403 Forbidden quando o ficheiro existe?

Num site estático, um 403 é normalmente causado por permissões do sistema de ficheiros, e não por uma regra HTTP. open() ... failed (13: Permission denied) em /var/log/nginx/error.log significa que o utilizador dos workers do nginx não consegue ler o ficheiro. Na maioria dos casos, falta permissão de execução para outros utilizadores num diretório pai, e não existe um problema com o modo do próprio ficheiro. directory index of ... is forbidden significa que o pedido correspondeu a um diretório sem ficheiro de índice, enquanto autoindex está desativado. Uma regra deny explícita no bloco location correspondente também devolve 403. Consulte esse bloco quando o error log não indicar a causa.

#http#https#web-server#headers#http3