Skip to content

Código de status HTTP · Erros do cliente (4xx)

Erro 401 Unauthorized

O erro 401 Unauthorized significa que o servidor não aceitou a sua identificação: o pedido chegou sem credenciais, ou com credenciais inválidas ou vencidas. Apesar do nome, é um problema de autenticação, e a causa mais comum é uma sessão ou token de acesso expirado.

Dados sobre este código de status
Classe4xx, Erros do cliente
Definido emRFC 9110 §15.5.2
Pode ir para o cache por padrãoSó com Cache-Control ou Expires explícitos
Pode repetir o pedidoSim, com credenciais novas ou válidas no cabeçalho Authorization
Cabeçalhos relevantes
  • WWW-Authenticate: obrigatório; informa o esquema que o cliente deve usar, como Bearer ou Basic
  • Authorization: cabeçalho do pedido que leva as credenciais na nova tentativa

O que significa o 401

A RFC 9110, seção 15.5.2, define o 401 como um pedido sem credenciais de autenticação válidas para o recurso. O nome "Unauthorized" é um deslize histórico: o código trata de identidade (autenticação), enquanto recusar alguém que o servidor já identificou é o 403.

Quem envia 401 é obrigado a incluir o cabeçalho WWW-Authenticate com pelo menos um desafio, como Basic realm="admin" ou Bearer realm="api". O desafio diz ao cliente como se autenticar. Com Basic, o navegador abre a janelinha nativa de login; com Bearer, servidores OAuth 2.0 acrescentam error="invalid_token" (RFC 6750) para indicar que o token foi recusado, e não que faltou.

Se o pedido já tinha credenciais, o 401 quer dizer que elas foram rejeitadas. O cliente pode tentar de novo com outro cabeçalho Authorization, que é justamente o que a lógica de renovação de token faz: captura o 401, troca o refresh token por um novo access token e repete o pedido uma vez.

Causas comuns

Se você está visitando o site

  • A sua sessão expirou ou você saiu da conta em outra aba, e o site não reconhece mais você.
  • Usuário ou senha errados numa página protegida por autenticação HTTP Basic (a janelinha do navegador volta sempre).
  • Cookies de terceiros bloqueados quando o login fica em outro domínio que não o da página.

Se você administra o servidor

  • JWT ou access token OAuth expirado, muitas vezes sem lógica de renovação no cliente.
  • Cabeçalho Authorization malformado: sem o prefixo "Bearer ", token com aspas ou quebra de linha, credenciais Basic sem base64.
  • Um proxy, CDN ou balanceador removendo o cabeçalho Authorization antes de ele chegar à aplicação.
  • Relógios dessincronizados entre servidores, e um token recém-emitido parece ainda não válido (nbf) ou já vencido (exp).
  • Preflight de CORS: o navegador envia OPTIONS sem credenciais e um middleware que exige login em todo método responde 401, o que o navegador mostra como erro de CORS.

Como resolver

Se você está visitando o site

  • Saia da conta e entre de novo; isso gera uma sessão nova.
  • Apague os cookies do site se, logo depois do login, você cair de novo no 401.
  • Na janelinha de autenticação Basic, confirme o usuário e a senha com quem administra o site; o navegador guarda os dados até ser fechado.

Se você administra o servidor

  • Decodifique o token (um decodificador de JWT mostra exp, nbf, iss e aud) e compare as claims com o que a API espera.
  • Implemente renovação ao receber 401: renove o access token uma vez, repita o pedido e deslogue o usuário se a renovação também falhar.
  • Deixe os pedidos OPTIONS passarem pelo middleware de autenticação para o preflight de CORS funcionar.
  • Verifique se cada proxy no caminho repassa o Authorization; algumas configurações exigem isso de forma explícita.
  • Inclua sempre o WWW-Authenticate; clientes e bibliotecas dependem dele, e a especificação exige.

Como enviar um 401

O auth_basic do nginx envia o 401 e o desafio WWW-Authenticate: Basic por você; crie o arquivo de senhas com o htpasswd.

Express (Node.js)
app.get('/me', (req, res) => {
  res.set('WWW-Authenticate', 'Bearer realm="api", error="invalid_token"');
  res.status(401).json({ error: 'Access token is missing or expired' });
});
Next.js App Router (route handler)
// app/me/route.ts
export async function GET() {
  return Response.json(
    { error: 'Access token is missing or expired' },
    { status: 401, headers: { 'WWW-Authenticate': 'Bearer realm="api", error="invalid_token"' } }
  );
}
Go net/http
mux.HandleFunc("GET /me", func(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("WWW-Authenticate", `Bearer realm="api", error="invalid_token"`)
	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(http.StatusUnauthorized) // 401
	w.Write([]byte(`{"error":"Access token is missing or expired"}`))
})
Python FastAPI
from fastapi import FastAPI, HTTPException

app = FastAPI()

@app.get("/me")
def get_me():
    raise HTTPException(
        status_code=401,
        detail="Access token is missing or expired",
        headers={"WWW-Authenticate": 'Bearer realm="api", error="invalid_token"'},
    )
Nginx
# nginx answers 401 with a WWW-Authenticate: Basic challenge
location /admin/ {
  auth_basic "Admin area";
  auth_basic_user_file /etc/nginx/.htpasswd;
}

Costuma ser confundido com

401 vs 403
O 401 quer dizer "não sei quem você é, autentique-se"; o 403 quer dizer "sei quem você é, ou tanto faz, e a resposta é não".
401 vs 407
O 407 é o mesmo desafio vindo de um proxy no meio do caminho, com Proxy-Authenticate no lugar de WWW-Authenticate.

Perguntas frequentes

Qual a diferença entre erro 401 e 403?
O 401 é de autenticação: o servidor precisa de credenciais válidas, e um novo login pode resolver. O 403 é de autorização: o servidor sabe quem você é, ou nem precisa saber, e mesmo assim recusa, então logar de novo não adianta.
Por que recebo 401 com um token que funcionava há um minuto?
Access tokens têm vida curta, muitas vezes de 5 a 60 minutos. Decodifique o JWT e confira a claim exp. Relógios dessincronizados entre o emissor e a API também fazem o token parecer vencido antes da hora.
Por que minha API dá 401 só quando chamo pelo navegador?
Quase sempre é o preflight de CORS. O navegador manda antes um OPTIONS sem o cabeçalho Authorization; se o middleware exige autenticação no OPTIONS, o preflight falha e o pedido real nem sai.
Posso enviar 401 sem o cabeçalho WWW-Authenticate?
Não. A RFC 9110 diz que o servidor DEVE enviar o WWW-Authenticate com pelo menos um desafio. Em APIs com token, Bearer realm="api" já basta.

Revisado em por Arielton Oberek.