🧠FFVAcademy
🌐

HTTP do zero: request, response, status, headers, cookies

14 min de leitura·+70 XP
Pré-requisitos (0/1)0%

Recomendamos completar os pré-requisitos antes de seguir, mas nada te impede de continuar.

HTTP (HyperText Transfer Protocol) é o protocolo que move a web inteira. APIs REST, webhooks, browsers, apps mobile — tudo fala HTTP. Entender sua estrutura real não é opcional para quem desenvolve software moderno.

Anatomia de uma request e response HTTP

# Request HTTP/1.1 completa (o que vai pelo fio):
GET /api/users/42 HTTP/1.1
Host: api.exemplo.com
Accept: application/json
Authorization: Bearer eyJhbGciOiJSUzI1NiJ9...
User-Agent: curl/8.4.0
Connection: keep-alive

# Request POST com body:
POST /api/users HTTP/1.1
Host: api.exemplo.com
Content-Type: application/json
Content-Length: 52
Authorization: Bearer eyJhbGci...

{"name": "Fernando", "email": "f@exemplo.com"}

# Response HTTP/1.1 completa:
HTTP/1.1 201 Created
Content-Type: application/json
Content-Length: 89
Location: /api/users/43
X-Request-Id: abc-123-def
Cache-Control: no-store
Date: Thu, 17 Apr 2026 10:00:00 GMT

{"id": 43, "name": "Fernando", "email": "f@exemplo.com", "created_at": "..."}

Toda request tem: método + path + versão na primeira linha, headers (um por linha, formato Nome: Valor), linha em branco, e opcionalmente um body. Response tem: versão + status code + reason phrase, headers, linha em branco, e o body da resposta.

Métodos HTTP: semântica importa

MétodoSemânticaIdempotente?Tem body?
GETLeitura — nunca modificaSimNão (prática)
POSTCriação / ação não-idempotenteNãoSim
PUTSubstituição completa do recursoSimSim
PATCHAtualização parcialNão necessariamenteSim
DELETERemoção do recursoSimNão (prática)
HEADIgual GET, mas sem body na responseSimNão
OPTIONSQuais métodos são suportados? (CORS preflight)SimNão
# Idempotência na prática:
# PUT /users/42 com body completo → mesmo resultado toda vez = idempotente
# DELETE /users/42 → mesmo resultado (404 na 2ª vez, mas o estado final é igual)
# POST /orders → cria um novo pedido a cada chamada = NÃO idempotente

# Idempotency key para POST seguro (padrão stripe/paypal):
POST /api/payments
Idempotency-Key: client-generated-uuid-abc123
{"amount": 100, "currency": "BRL"}
# Servidor: se já processou esta key, retorna o resultado original sem processar novamente

Status codes: o que cada faixa significa

FaixaCategoriaExemplos importantes
1xxInformacional101 Switching Protocols (WebSocket upgrade)
2xxSucesso200 OK, 201 Created, 204 No Content
3xxRedirecionamento301 Moved Permanently, 302 Found, 304 Not Modified
4xxErro do cliente400 Bad Request, 401 Unauth, 403 Forbidden, 404 Not Found, 429 Too Many Requests
5xxErro do servidor500 Internal Server Error, 502 Bad Gateway, 503 Service Unavailable
# Status codes que todo dev confunde:

# 200 vs 201 vs 204:
GET /users/42    → 200 OK + body
POST /users      → 201 Created + body + header Location: /users/43
DELETE /users/42 → 204 No Content (sem body)

# 301 vs 302 vs 307 vs 308:
# 301 Moved Permanently  → browser e crawlers atualizam a URL (SEO redirect)
# 302 Found              → redirect temporário (crawlers não atualizam)
# 307 Temporary Redirect → como 302, mas garante manter o método HTTP
# 308 Permanent Redirect → como 301, mas garante manter o método HTTP

# 400 vs 422:
# 400 Bad Request  → request malformada (JSON inválido, campos obrigatórios faltando)
# 422 Unprocessable Entity → sintaxe OK, mas semanticamente inválido
#     (email formatado corretamente mas já cadastrado)

# 401 vs 403:
# 401 → não autenticado → mostre tela de login
# 403 → autenticado mas sem permissão → mostre "acesso negado"

# 429 Too Many Requests → rate limited
# Boas APIs incluem: Retry-After: 60 (segundos para aguardar)

Headers: metadados que controlam tudo

# Headers de Autenticação
Authorization: Bearer <JWT-token>
Authorization: Basic <base64(user:pass)>
X-API-Key: abc123                    # padrão de APIs simples

# Headers de Conteúdo
Content-Type: application/json       # formato do BODY enviado
Content-Type: multipart/form-data    # upload de arquivo
Accept: application/json             # formato que o cliente ACEITA na resposta
Content-Length: 1234                 # tamanho em bytes do body
Content-Encoding: gzip               # body está comprimido

# Headers de Cache
Cache-Control: no-cache              # sempre revalida com o servidor
Cache-Control: no-store              # não cacheia (dados sensíveis)
Cache-Control: max-age=3600          # cacheia por 1 hora
Cache-Control: public, max-age=86400 # cacheia em CDN por 1 dia
ETag: "abc123"                       # identifier da versão do recurso
If-None-Match: "abc123"              # request condicional → 304 se não mudou

# Headers de Rate Limiting (de facto padrão)
X-RateLimit-Limit: 1000              # total de requests permitidas
X-RateLimit-Remaining: 999           # quanto sobra na janela atual
X-RateLimit-Reset: 1713350400        # quando a janela reseta (Unix timestamp)
Retry-After: 60                      # segundos para aguardar (em 429)

# Headers de Segurança (response)
Strict-Transport-Security: max-age=31536000; includeSubDomains
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
Content-Security-Policy: default-src 'self'

Cookies: persistência do lado do cliente

# Server seta cookie via header Set-Cookie:
HTTP/1.1 200 OK
Set-Cookie: session_id=abc123; Path=/; HttpOnly; Secure; SameSite=Strict; Max-Age=3600

# Atributos importantes:
# Path=/          → cookie enviado em qualquer path do domínio
# HttpOnly        → JavaScript não pode acessar (proteção contra XSS)
# Secure          → só enviado em HTTPS
# SameSite=Strict → não enviado em requests cross-site (proteção CSRF)
# SameSite=Lax    → enviado em navegação top-level (clique em link)
# SameSite=None   → enviado em qualquer contexto (necessita Secure)
# Max-Age=3600    → expira em 1 hora (0 = deleta o cookie)
# Domain=.exemplo.com → válido em todos os subdomínios

# Browser envia automaticamente cookies do domínio em todas as requests:
GET /api/perfil HTTP/1.1
Host: api.exemplo.com
Cookie: session_id=abc123; preferencia=dark-mode
⚠️
Cookies sem HttpOnly são acessíveis via JavaScript — vulneráveis a XSS. Sem SameSite=Strict/Lax são vulneráveis a CSRF. Sem Secure trafegam em HTTP. Sempre use os três para cookies de sessão.

CORS: por que o browser bloqueia, mas curl não

# CORS: Cross-Origin Resource Sharing
# Origin = protocolo + host + porta: https://app.com vs https://api.com = origens diferentes

# 1. PREFLIGHT (request OPTIONS automática do browser antes de POST/PUT/custom headers):
OPTIONS /api/users HTTP/1.1
Origin: https://app.meusite.com
Access-Control-Request-Method: POST
Access-Control-Request-Headers: Authorization, Content-Type

# 2. Server responde ao preflight:
HTTP/1.1 204 No Content
Access-Control-Allow-Origin: https://app.meusite.com  # ou: * para qualquer origem
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Access-Control-Allow-Headers: Authorization, Content-Type
Access-Control-Max-Age: 86400        # cacheia o preflight por 1 dia

# 3. Browser envia a request real
# Se Access-Control-Allow-Origin não incluir a origem → browser bloqueia

# Configuração em Express.js:
app.use(cors({
  origin: ['https://app.meusite.com', 'https://admin.meusite.com'],
  credentials: true,   // necessário para enviar cookies cross-origin
  methods: ['GET', 'POST', 'PUT', 'DELETE'],
  allowedHeaders: ['Content-Type', 'Authorization'],
}));

# CORS com credentials (cookies/auth headers cross-origin):
# Server: Access-Control-Allow-Credentials: true + Access-Control-Allow-Origin NÃO pode ser *
# Client: fetch(url, { credentials: 'include' })
Ferramentas para explorar HTTP: curl -v (veja headers completos), httpie (syntax mais amigável), Postman/Insomnia (GUI), DevTools → Network (browser) → clique na request → Headers.
💡
Próximo: DNS, TLS e certificados — o que acontece nos milissegundos antes do seu request HTTP chegar ao servidor.
🧩

Quiz rápido

3 perguntas · Acerte tudo e ganhe o badge 🎯 Gabarito

Continue lendo