Início / RLS / Erro JWT expired (PGRST303) · Read in English

Supabase Auth · Correção de erro

Erro JWT expired (PGRST303) no Supabase

Supabase Auth · Next.js✓ código testado num banco de verdade
TL;DR

O token de acesso da requisição passou da validade, e a Data API responde 401 PGRST303 JWT expired. No navegador, o supabase-js renova sozinho. No servidor, ele só fica válido se algo renovar: no Next.js, é o proxy.ts (middleware.ts antes do Next 16) do @supabase/ssr, chamando supabase.auth.getClaims().

01 O que é o erro

resposta

{ "code": "PGRST303", "message": "JWT expired" }

Os tokens de acesso do Supabase duram pouco e vêm com um refresh token que gera um novo. O erro quer dizer que uma requisição saiu com um token velho que ninguém renovou. Não é problema de RLS: suas policies nem chegaram a rodar.

02 De onde vem

CAUSA 1 · mais comum

Código de servidor que lê a sessão sem renovar

Server Components do Next.js não podem gravar cookies, então não conseguem salvar um token renovado. Se nenhum proxy/middleware renovar antes, as queries do servidor saem com token vencido.

CAUSA 2

Token passado na mão

Um cliente criado com o header fixo Authorization: Bearer ..., ou um token guardado no seu próprio estado, para de funcionar quando vence: nada renova ele.

CAUSA 3

Aba que hibernou

Quando o notebook volta da hibernação, a primeira requisição pode sair antes da renovação. O supabase-js renova sozinho; tentar de novo depois da renovação funciona.

03 A correção no Next.js

Use o @supabase/ssr e renove no proxy, que roda antes das páginas e pode gravar cookies. Seguindo o guia do Supabase, o proxy cria um cliente de servidor e chama getClaims(), que renova um token vencido:

proxy.ts (middleware.ts antes do Next 16)

// dentro do proxy, depois de criar o cliente de servidor com os cookies da requisição
await supabase.auth.getClaims();   // renova o token e grava os cookies novos

Não confie no getSession() no código do servidor: ele lê o cookie sem conferir. E crie o cliente do Supabase a cada requisição, a partir dos cookies, nunca de um token guardado.

04 Não confunda com

◆ GRÁTIS · 30 SEGUNDOS · SEM CADASTRO

Seu app está vazando agora?

Cole a URL do seu app no ar. O scanner lê a página e o JavaScript atrás de uma chave service_role do Supabase, o vazamento que passa por cima de todas as policies deste guia.

◆ GRÁTIS · MIT · npm + GitHub Action

Pegue isso antes de ir pro ar

airlock-rls é um gate de CI que quebra o build quando uma tabela vai pro ar exposta ou uma policy é permissiva: o mesmo tipo de bug, pego no CI contra um banco com as suas migrations aplicadas, antes de chegar em produção.

npx airlock-rls→

Ou comece pelo nextjs-supabase-starter: auth + uma tabela com RLS + um teste de isolamento, pra começar de um padrão que já vem selado.

PDF GRÁTIS

Baixe o cheat sheet de RLS do Supabase

As regras de ouro, as armadilhas que vazam em produção, policies corretas e o teste de isolamento, numa página só (em inglês).

FAQ

Devo aumentar a validade do JWT?

Isso esconde o sintoma e deixa um token roubado útil por mais tempo. Corrija a renovação: com o proxy funcionando, a validade deixa de importar pro usuário.

A chave service_role expira?

Ela não é uma sessão de usuário, então não há o que renovar. E ela ignora o RLS, então fica só no servidor, nunca como solução pra um token de usuário vencido.