Um token JWT expirou quando o horário atual é igual ou posterior à claim exp. O valor de exp é um número de segundos contados desde 1º de janeiro de 1970, em UTC: 1793372400 é 30 de outubro de 2026 às 15:00:00 UTC, ou 12:00 em Brasília. Para conferir, decodifique a parte do meio do token, leia o exp e compare com o relógio. O nosso decodificador de JWT grátis faz isso para você. Cole o token e ele mostra Expirado ou Não expirou, além da data exata no seu fuso horário e em UTC.
É grátis, não precisa de conta e roda inteiro no seu navegador. No nosso teste de 31 de outubro de 2026, a página não enviou nenhuma requisição contendo o token. Visitantes têm 5 usos de ferramentas por dia, e uma conta gratuita do My Botsy (100% grátis, sem spam) remove esse limite diário. Todos os resultados citados são reais. Os do decodificador vêm de testes no mybotsy.com em 31 de outubro de 2026. Os das bibliotecas vêm de PyJWT 2.15.1, jsonwebtoken 9.0.3 e jose 6.2.14, rodadas no mesmo dia.
O que significa a claim exp
O RFC 7519, padrão do JWT, define três claims de tempo. As três são opcionais e todas usam o tipo NumericDate, ou seja, segundos, e não milissegundos:
- exp (expiration time): a partir desse instante, inclusive, o token não deve ser aceito.
- nbf (not before): antes desse instante, o token também não deve ser aceito.
- iat (issued at): quando o token foi emitido. Sozinho, não invalida nada, mas serve para calcular a idade do token.
Para exp e nbf, o RFC permite uma pequena folga, «normalmente não mais que alguns minutos», para compensar a diferença entre relógios. É por causa dessa folga que dois sistemas podem discordar sobre um token que expirou há poucos segundos.
Como verificar se o token JWT expirou, passo a passo
- Copie o token. É aquela string longa com dois pontos que aparece depois de
Authorization: Bearerno cabeçalho da requisição, ou que fica salva num cookie ou no localStorage. - Cole no decodificador de JWT. Não precisa limpar antes: a ferramenta tira
Authorization:,Bearer, aspas, espaços e quebras de linha, e avisa que fez isso. - Veja a linha Status. Ela mostra Expirado, Não expirou, Ainda não é válido (quando o
nbfestá no futuro), Sem expiração (sem exp) ou Expiração ilegível: exp não é um número válido (quando oexpexiste mas não é um número). - Confira a linha Expira em (exp). Ela traz a data no seu fuso, o mesmo instante em UTC, um tempo relativo («ontem», «em 2 meses») e o número original.
- Olhe a Duração. É o
expmenos oiat. Se um token de acesso dura 30 dias, o problema está em como os tokens são emitidos, e não nesse token. - Leia os avisos. O decodificador alerta para datas em milissegundos, para
alg: nonee para tokens com preenchimento. Ele também lembra que não verifica a assinatura.
Exemplos resolvidos (saída real do decodificador)
Assinamos estes tokens de teste com HS256 e um segredo de demonstração descartável. Depois colamos cada um em mybotsy.com/pt/jwt-decoder em 31 de outubro de 2026, por volta das 15:07 UTC, com o navegador no horário de São Paulo.
Exemplo 1: um token expirado
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c2VyXzg4NDEiLCJpc3MiOiJodHRwczovL2F1dGguZXhhbXBsZS5jb20iLCJhdWQiOiJvcmRlcnMtYXBpIiwiaWF0IjoxNzkzMzY4ODAwLCJleHAiOjE3OTMzNzI0MDB9.FJV2oXvW0F-Q7x6rzZRKosQG_DhxLQXXz87wW-Ctq60
| Linha | Saída do decodificador |
|---|---|
| Status | Expirado |
| Emitido em (iat) | sexta-feira, 30 de outubro de 2026 às 11:00:00 BRT · 2026-10-30T14:00:00Z · ontem · 1793368800 |
| Expira em (exp) | sexta-feira, 30 de outubro de 2026 às 12:00:00 BRT · 2026-10-30T15:00:00Z · ontem · 1793372400 |
| Duração | 1 hora |
As três bibliotecas concordaram. O PyJWT lançou ExpiredSignatureError: Signature has expired, o jsonwebtoken lançou TokenExpiredError: jwt expired com expiredAt 2026-10-30T15:00:00.000Z e o jose retornou ERR_JWT_EXPIRED.
Exemplo 2: um token válido colado com o cabeçalho
Colamos a linha inteira, Authorization: Bearer eyJhbGciOiJIUzI1NiIs…. O decodificador avisou «O texto foi limpo antes da decodificação» e mostrou Não expirou, com exp em quinta-feira, 31 de dezembro de 2026 às 21:00:00 BRT, ou seja, 2027-01-01T00:00:00Z («em 2 meses»), e Duração de 61 dias 9 horas. Repare na data: um token que expira à meia-noite UTC do Ano-Novo para de funcionar no Brasil às 21h do dia 31. Olhar só o número, ou só a data local, é o jeito mais comum de errar o dia.
Exemplo 3: ainda não é válido
Um token com nbf 1796083200 e exp 1796169600 deu Ainda não é válido, com Válido a partir de segunda-feira, 30 de novembro de 2026 às 21:00:00 BRT (2026-12-01T00:00:00Z). O PyJWT retornou ImmatureSignatureError e o jsonwebtoken, NotBeforeError: jwt not active. Se um token recém-emitido for recusado, confira o nbf e o relógio do servidor antes de culpar a expiração.
Exemplo 4: sem exp
O payload {"sub":"svc-reporting","iat":1790856000} deu Sem expiração (sem exp), e as três bibliotecas o aceitaram. Se um token assim vazar, ele continua funcionando até a chave ser trocada. No PyJWT, com options={"require": ["exp"]}, o mesmo token passou a dar MissingRequiredClaimError: Token is missing the "exp" claim.
Armadilhas que dão a resposta errada
exp em milissegundos
O erro clássico é gravar Date.now() direto no exp. Assinamos um token com "exp":1793462400000. O decodificador avisou «O valor de exp parece estar em milissegundos» leu o valor como milissegundos e mostrou a data real, 31 de outubro de 2026 às 16:00 UTC, com o status e a duração corretos. O jsonwebtoken e o jose aceitaram o token, ou seja, na prática ele nunca expira. O PyJWT recusou, mas pela checagem do iat («The token is not yet valid (iat)»), não pelo exp. A correção: divida por 1000 ao emitir o token e multiplique por 1000 ao montar um Date do JavaScript a partir do exp.
exp como texto
"exp":"1798761600", entre aspas, quebra o padrão. O jsonwebtoken recusou com invalid exp value, e o jose com "exp" claim must be a number. Já o PyJWT aceitou e ainda aplicou a regra: com uma data vencida em texto, lançou ExpiredSignatureError. Resultado: o mesmo token passa num serviço e falha em outro. O decodificador mostra «Não é uma data válida: deve ser um número de segundos» ao lado do valor, um aviso, e a linha Status diz Expiração ilegível: exp não é um número válido.
Diferença de relógio e folga (leeway)
Criamos um token que tinha expirado 20 segundos antes. Sem folga, as três bibliotecas o recusaram. Com 30 segundos (leeway=30 no PyJWT, clockTolerance: 30 no jsonwebtoken e no jose), as três aceitaram. Mantenha a folga pequena, porque cada segundo a mais estende a vida de todos os tokens, inclusive dos roubados.
Decodificar com base64 --decode
O JWT usa base64url, que troca + e / por - e _. O comando famoso cut -d. -f2 | base64 --decode | jq .exp funciona até o payload ter um desses caracteres. No GNU coreutils 9.7, um payload com - imprimiu metade do JSON e depois base64: invalid input, e o jq falhou com Unfinished string. Troque os caracteres antes com tr '_-' '/+', ou cole o token no decodificador, que lê base64url nativamente. Para outros problemas com Base64, veja como decodificar Base64.
Decodificar não é verificar
Qualquer pessoa pode montar um token com exp no ano 2100. Ler o exp serve para depurar ou para o navegador decidir quando renovar o token. Antes de confiar numa requisição, o servidor precisa verificar a assinatura com uma lista fixa de algoritmos permitidos. O RFC 8725 descreve ataques em que o algoritmo foi trocado para none, e o nosso decodificador mostra um alerta vermelho quando encontra alg: none.
Verificar a expiração no código
Estas funções servem para exibir a data ou agendar a renovação, não para segurança. Testamos as duas com os tokens acima. O expirado retornou true, o válido e o de base64url retornaram false, e os tokens sem exp ou com exp em texto não retornaram data.
// JavaScript (navegador ou Node 16+)
function jwtExpiry(token) {
const part = token.split('.')[1].replace(/-/g, '+').replace(/_/g, '/');
const { exp } = JSON.parse(atob(part));
if (typeof exp !== 'number') return null; // sem exp, ou exp em texto
return new Date(exp * 1000); // segundos -> ms
}
function isExpired(token, leewaySeconds = 0) {
const exp = jwtExpiry(token);
return exp !== null && Date.now() >= exp.getTime() + leewaySeconds * 1000;
}
# Python 3
import base64, json, time
def jwt_exp(token):
seg = token.split(".")[1]
seg += "=" * (-len(seg) % 4) # repõe o preenchimento que o JWT remove
exp = json.loads(base64.urlsafe_b64decode(seg)).get("exp")
return exp if isinstance(exp, (int, float)) else None
def is_expired(token, leeway=0):
exp = jwt_exp(token)
return exp is not None and time.time() >= exp + leeway
A comparação é «maior ou igual» porque, pelo RFC, o token deixa de valer no instante do exp. No servidor, use a função de verificação da sua biblioteca (jwt.decode com chave no PyJWT, jwt.verify no jsonwebtoken, jwtVerify no jose), que confere a assinatura e as datas juntas.
Boas práticas de segurança
- Trate um token válido como uma senha. Até expirar, quem estiver com ele pode agir como aquele usuário. Prefira um decodificador local como o nosso a colar tokens em chats ou chamados.
- Use tokens de acesso de vida curta e exija
expno servidor. - Use um segredo HS256 longo e aleatório. O PyJWT avisou que a nossa chave de teste de 20 bytes estava «below the minimum recommended length of 32 bytes for SHA256». Um segredo de 64 caracteres do gerador de senhas passa com folga desse mínimo. O nosso guia para criar uma senha forte explica por que o tamanho importa.
- Nada secreto vai no payload. Ele é só codificado, e qualquer pessoa consegue lê-lo com um decodificador Base64. Se o payload decodificado não abrir como JSON, passe-o no validador de JSON e veja como corrigir um JSON inválido.
Certificados também expiram. Se o erro de «expirado» vier do HTTPS e não de um token, veja como verificar a data de validade de um certificado SSL.
Confira o seu token agora
Cole o token no decodificador de JWT grátis. É grátis e não precisa de conta. O token fica no seu navegador, e você vê o status, todas as datas no seu fuso e em UTC, e alertas para cada armadilha acima.
Perguntas frequentes
Como sei quando o meu JWT expira?
Decodifique o payload e leia a claim exp, que conta os segundos desde 1º de janeiro de 1970 em UTC. Multiplique por 1000 para ter uma data no JavaScript, ou cole o token no decodificador de JWT do My Botsy, que mostra o exp no seu fuso horário e em UTC.
Um JWT expira exatamente no horário do exp?
Sim. O RFC 7519 diz que o token não deve ser aceito a partir do horário do exp, inclusive, então compare com maior ou igual. Muitos servidores somam alguns segundos de folga por causa da diferença de relógio.
Por que o servidor recusa um token que o decodificador diz que não expirou?
As causas comuns são o relógio do servidor diferente, um nbf no futuro, audiência ou emissor errados, ou uma assinatura inválida. O decodificador só lê as datas. O servidor confere também a assinatura e as outras claims.
E se o JWT não tiver exp?
A maioria das bibliotecas aceita, e o token nunca expira sozinho. Exija o exp no servidor (por exemplo, com a opção require do PyJWT) e use tokens de acesso de vida curta.
É seguro colar um JWT num decodificador online?
Só se o decodificador rodar localmente. O do My Botsy decodifica no seu navegador, e o token nunca é enviado ao nosso servidor. Mesmo assim, trate qualquer token válido como uma senha, e use tokens expirados ou de teste quando compartilhar exemplos.
O decodificador de JWT do My Botsy é grátis?
Sim. É grátis, não precisa de conta e roda no seu navegador. Visitantes têm 5 usos por dia, e uma conta gratuita remove esse limite.