15 de jan. de 2026·6 min de leitura

Bugs de cache no cliente: pare de mostrar dados de usuário errados

Aprenda a evitar bugs de cache no cliente que exibem dados de usuário incorretos auditando chaves de cache, regras de invalidação e casos em dispositivos compartilhados.

Bugs de cache no cliente: pare de mostrar dados de usuário errados

Como “dados de outro usuário” aparecem em apps reais

Esse bug geralmente se manifesta como um momento de “espera, por que estou vendo aquilo?” Alguém abre o app e vê o nome, avatar, endereço ou último pedido de outra pessoa. Às vezes pisca por um segundo antes da tela se corrigir. Outras vezes fica até um refresh forçado ou reinstalação.

Você normalmente verá isso ao:

  • Fazer logout e depois login com outra conta
  • Trocar workspaces/organizações
  • Mudanças de papel (admin para membro, ou vice-versa)
  • Refresh de token e fluxos de “lembrar-me” em dispositivos compartilhados

Um exemplo simples: você testa seu app em um iPad compartilhado. Você entra como Cliente A, navega pelos pedidos e faz logout. Um colega entra como Cliente B e abre Pedidos. O app mostra a lista do Cliente A porque a resposta cacheada foi armazenada sob uma chave genérica como "orders" em vez de algo escopado, tipo "orders:userId".

Isso não é só um glitch de interface. É um problema de privacidade. Mesmo um breve flash pode expor dados pessoais como emails, endereços, faturas ou tickets de suporte. Em indústrias reguladas, vira questão de conformidade. Mesmo quando nada sensível vaza, a confiança cai rápido.

O objetivo é simples: cada pessoa vê apenas seus próprios dados, sempre — através de refreshes, trocas de aba, modo offline e logins em dispositivos compartilhados.

Onde o cache do cliente acontece (mapa rápido)

Quando um app mostra dados da pessoa errada, a causa normalmente não é “o cache” em geral. É um dos vários lugares onde o estado pode persistir após mudanças de identidade. Aqui vai um mapa rápido para você olhar nos pontos certos.

1) Cache HTTP embutido do navegador

Os navegadores podem cachear requisições GET com base na URL, headers e regras de cache. Se suas respostas de API variam por usuário mas a resposta for cacheável, o navegador pode reproduzir a última resposta mesmo após logout e novo login.

Isso é menos comum em APIs JSON autenticadas (normalmente não cacheáveis), mas pode acontecer com headers faltantes ou proxies/CDNs mal configurados.

2) Caches no nível do app que você controla

A maioria dos problemas de “usuário errado” vem de caches dentro do app:

  • Estado em memória (stores globais, singletons, variáveis de nível de módulo)
  • localStorage/sessionStorage (persiste entre abas e reloads)
  • IndexedDB (comum em apps offline-first)
  • “Respostas salvas” guardadas por performance

Se qualquer um desses estiver com chaves muito genéricas (ou sem chave), uma conta pode ver os dados de outra em um dispositivo compartilhado.

3) Bibliotecas de fetch que têm cache de queries

Bibliotecas como React Query, SWR, Apollo e RTK Query cacheiam resultados usando uma chave que você fornece (ou geram). Se essa chave não incluir o contexto de identidade atual, a biblioteca pode servir um resultado em cache da sessão anterior.

Isso frequentemente aparece como: “Trocou de conta e o widget de perfil ainda mostra o nome antigo até o refresh.”

4) Service workers e caches offline

Service workers podem cachear HTML, respostas de API e ativos. Se as regras de cache forem muito amplas, eles podem cachear respostas de API personalizadas e reproduzi-las offline ou durante conexão instável.

5) Divergência SSR/CSR na hidratação

Se você renderiza no servidor, o cliente pode hidratar usando estado obsoleto de uma sessão anterior (ou de uma store persistida). Um padrão comum é a UI carregar dados em cache primeiro e depois “corrigir” após uma busca. Essa “correção” ainda pode ser um vazamento de privacidade.

Audite suas chaves de cache para evitar colisões de usuário

A maioria dos bugs de usuário-errado começa com uma chave de cache muito ampla. Se duas sessões diferentes podem gerar a mesma chave, o app pode “corretamente” retornar uma resposta em cache errada.

Uma chave segura descreve tanto:

  • o que são os dados, quanto
  • para quem os dados são (e sob qual escopo).

Uma boa regra: se uma mudança alteraria o que o servidor pode retornar, ela também deve alterar a chave do cache.

Na prática, chaves (ou namespaces) geralmente precisam incluir:

  • ID do usuário (ou outro identificador de conta estável)
  • ID do tenant/org/workspace (para apps multi-tenant)
  • Papel ou escopo de permissão (admin vs member)
  • Locale (se o conteúdo muda por língua/região)
  • Forma da query, como filtros e paginação

Evite chaves como "me", "profile", "dashboard" ou "inbox" a menos que elas sejam explicitamente escopadas por usuário.

  • Ruim: "profile" ou "me"
  • Bom: "user:123:org:55:profile"

Mesmo ideia com listas:

  • Ruim: "orders?page=1"
  • Bom: "user:123:org:55:orders?status=open&page=1"

Para encontrar colisões rapidamente, busque por strings de chave compartilhadas e veja onde são reutilizadas. Se puder, registre a chave computada em runtime, troque de conta e compare.

Regras de invalidação de cache que batem com ações reais do usuário

A maioria dos bugs de usuário-errado não é complicada. O app continua usando dados que estavam corretos cinco minutos antes, mas para uma pessoa diferente.

Comece decidindo o que precisa ser limpo (ou re-escopado) quando a identidade muda. Logout é óbvio, mas troca de conta é a armadilha comum: a UI atualiza o badge da “conta atual” enquanto queries cacheadas ainda apontam para o usuário anterior.

Uma regra simples que funciona bem: quando o “contexto do usuário atual” muda (user ID, org ID, workspace ID), trate como um mini reboot dos dados escopados por usuário.

Gatilhos práticos para integrar:

  • Login, logout, troca de conta: limpe caches escopados ao usuário, cancele requisições em andamento, refaça o essencial.
  • Qualquer mudança no que o usuário pode ver: atualizações de papel/permissão, trocas de org/projeto.
  • Eventos de auth: falhas em refresh de token, reautenticação forçada, respostas de “sessão expirada”.

TTLs não são só configurações de performance. São controles de privacidade. Conteúdo público pode viver mais tempo, mas tudo ligado à identidade (perfil, cobrança, permissões, atividade recente) deve expirar rápido, especialmente em dispositivos compartilhados.

Mantenha resets de cache fáceis de chamar a partir de um único lugar. Crie uma função única (por exemplo, resetUserState()) que limpe os caches corretos e reinicialize o app, e chame-a nos fluxos de login/logout/troca de conta. Quando a lógica de reset está espalhada, um caminho acabará ficando de fora.

Passo a passo: reproduzir e depurar o problema

Problemas herdados de código gerado por IA
Perfeito se seu app foi construído em Lovable, Bolt, v0, Cursor ou Replit.

A forma mais rápida de consertar esses bugs é fazê-los acontecer sob demanda. Use um perfil de navegador (ou um dispositivo de teste compartilhado) e duas contas de teste com dados claramente diferentes (nome distinto, avatar e ao menos um registro único).

Anote os cliques exatos conforme avança. Pequenos detalhes importam: qual aba você abriu primeiro, se usou back/forward e se fez logout ou apenas trocou de conta.

Um runbook que frequentemente expõe o vazamento:

  • Faça login como Conta A e visite a página que depois mostra os dados errados.
  • Sem fechar a aba, faça logout. Entre como Conta B. Reabra a mesma página usando o mesmo caminho de navegação (incluindo back/forward se usou).
  • Inspecione o armazenamento do navegador (Local Storage, Session Storage, IndexedDB) e painéis de cache. Procure entradas cacheadas que não mudaram durante a troca.
  • Adicione logs temporários em leituras e escritas do cache: registre a chave do cache, o identificador do usuário atual e de onde os dados vieram (memória, armazenamento, rede).
  • Verifique o painel Network. Se a resposta do servidor é da Conta B mas a UI mostra a Conta A, o bug está no cliente.

Um sinal óbvio: se ambos os usuários baterem na mesma chave como profile:me, você pode ler o perfil da Conta A depois da Conta B entrar. Logar a chave junto com a ID do usuário atual deixa isso claro.

Casos de borda em dispositivo compartilhado e multi-conta para testar

Dispositivos compartilhados são onde isso vira embaraço rapidamente. “Logout” muitas vezes limpa um token, mas não os dados cacheados que foram buscados com aquele token.

Um exemplo realista: você testa o app em um tablet da família. Você faz logout, outra pessoa faz login e a tela inicial pisca seu dashboard antes de atualizar. Mesmo que corrija, esse flash é um vazamento de privacidade.

Uso com múltiplas contas no mesmo navegador é outro ponto problemático. Duas abas podem acabar com estados de sessão diferentes, especialmente se uma aba refrescar tokens ou rehidratar estado enquanto a outra ainda renderiza dados antigos. Se seu cache é global (não ligado à identidade), colisões são prováveis.

O botão voltar é um caso especial. Alguns navegadores mantêm páginas em memória usando o back-forward cache (bfcache). Depois do logout, um usuário pode apertar Back e ver a tela de antes do login instantaneamente — incluindo dados em cache — antes do seu app reexecutar cheques de auth.

Execute esses testes antes de enviar:

  • Entre como Usuário A, abra uma página pesada em dados, faça logout e entre como Usuário B sem recarregar o navegador.
  • Repita em um dispositivo compartilhado e também feche e reabra a aba após logout.
  • Abra duas abas: entre como A na aba 1, B na aba 2, então atualize ambas e observe por UI misturada.
  • Depois do logout, pressione Back e confirme que você nunca vê os dados de A, nem que seja por um instante.

Armadilhas de service worker e cache offline

Service workers são ótimos para velocidade, mas também podem reproduzir os dados da pessoa errada. O maior risco é usar padrões cache-first ou stale-while-revalidate em requests que dependem de quem está logado.

Exemplo: um tablet compartilhado numa loja. Usuário A entra e abre o dashboard, depois faz logout. Usuário B entra depois, mas o service worker serve uma resposta cacheada para "/api/me" ou "/api/orders" antes da atualização de rede terminar. Por um momento (ou mais, se a rede falhar), B vê os dados de A.

Cache-first e stale-while-revalidate geralmente são ok para arquivos públicos e estáticos (app shell, ícones, CSS). São arriscados para endpoints escopados ao usuário, especialmente quando a URL da requisição não inclui um identificador de usuário e depende de cookies ou bearer tokens.

Quando em dúvida, pule o cache para qualquer coisa ligada à identidade:

  • Endpoints como "/api/me", "/api/profile", "/api/billing", "/api/orders"
  • Qualquer requisição com header Authorization
  • Qualquer coisa que retorne PII (emails, endereços, faturas)

Se você mudar regras de login, logout, refresh de token ou de papel, trate como uma mudança que quebra o cache também. Versione caches (por exemplo, "app-v5") e delete caches antigos no activate. Caso contrário, respostas antigas podem continuar aparecendo mesmo depois de você achar que consertou.

O modo offline precisa de cuidado extra. Mantenha dados offline estritamente por usuário e limpe-os no sign-out. Se você armazenar requisições enfileiradas ou respostas de API em cache, inclua a ID do usuário na chave de armazenamento e rejeite leituras quando o usuário assinado mudar.

Checagens de segurança e privacidade (simples mas críticas)

Proteja dados sensíveis
Endureça seu app contra segredos expostos, fluxos de autenticação fracos e armazenamento cliente arriscado.

Quando o cache cliente mostra dados da pessoa errada, trate inicialmente como um incidente de segurança e só depois como bug de UI.

A regra chave: nunca confie no cache cliente para decidir quem pode ver algo. Estado em cache pode estar obsoleto, poluído ou copiado entre sessões. Autorização deve ser aplicada no servidor, sempre.

Um check rápido de realidade: se alguém altera a user ID numa requisição (ou reaplica uma requisição antiga), o servidor ainda retorna dados? Se sim, o problema não é só a UI.

Checagens mínimas que valem a pena:

  • Verifique que cada endpoint checa identidade no servidor e escopa dados para aquela identidade.
  • Confirme que o logout invalida sessões do lado do servidor e refresh tokens, não só o estado da UI.
  • Reduza dados sensíveis em armazenamento de longa duração (localStorage, IndexedDB). Mantenha em memória quando possível.
  • Garanta que respostas cacheadas não sejam compartilhadas entre contas no mesmo dispositivo.
  • Busque no código por segredos expostos ou tokens hardcoded.

Mais um exemplo: se sua UI mostra brevemente um "/me" em cache após logout, isso é ruim. Se o servidor também aceitar tokens antigos, piora: a próxima pessoa pode carregar dados reais, não só pixels obsoletos.

Erros comuns que causam dados de outro usuário

Bugs de dados de outro usuário geralmente vêm de um problema subjacente: o app esquece que dados em cache devem estar vinculados a uma identidade, não só a uma tela.

Causas comuns:

  • Um store global ou singleton sobrevive ao logout, então o próximo login herda os dados do último usuário.
  • Requisições iniciadas antes do logout terminam depois e suas respostas são gravadas no cache da nova sessão.
  • Chaves de cache são muito genéricas ("/me", "dashboard") e não incluem usuário, tenant, papel ou ambiente.
  • Atualizações otimistas escrevem em um cache compartilhado sem checar o escopo do usuário ativo.
  • Logout limpa apenas o que você vê (estado da UI) mas deixa a fonte da verdade intacta (cache em memória, storage, caches de service worker).

Dois checks rápidos que pegam muitos casos:

  1. Faça logout e entre como outro usuário enquanto a rede está lenta. Observe o que renderiza antes da nova chamada API terminar.
  2. Dispare uma requisição lenta, faça logout no meio do carregamento e entre de novo. Se a resposta lenta cair e atualizar a nova sessão, você tem um problema de escrita em voo.

Checklist rápido antes de liberar: cache e identidade

Encontre estado de usuário persistente
Identificamos onde o estado antigo sobrevive ao logout e como reiniciá-lo com segurança.

Antes de liberar a correção, faça um último passe focado em identidade. Após qualquer mudança de auth, o app não deve mostrar dados do usuário anterior — nem que seja por um momento.

Teste no mesmo dispositivo e perfil de navegador (é aí que a maioria das surpresas acontece):

  • Faça um swap A->B: entre como Usuário A, abra telas com muitos dados, faça logout, entre como Usuário B e revisite as mesmas telas.
  • Confirme que chaves de cache são escopadas à identidade em todo lugar que você armazena resultados (caches em memória, caches de query, localStorage, IndexedDB).
  • Faça do logout um reset completo: limpe tokens, refresh tokens, respostas de API cacheadas, stores persistidos e valores de “última conta selecionada”.
  • Trate troca de conta como operação mais forte que navegação: invalide queries escopadas ao usuário, cancele requisições em andamento e refaça sob a nova identidade.
  • Verifique comportamento do service worker: versionamento de caches no deploy e não servir respostas de API específicas do usuário de um cache compartilhado.

Depois disso, escaneie por elementos “pegajosos” de identidade como menus de perfil, itens recentes e badges de notificação. Eles frequentemente vêm de um cache diferente do feed principal.

Próximos passos: fazer a correção durar

Escolha uma regra de aceitação e torne-a inegociável: após logout (ou troca de conta), o app não deve mostrar nenhum dado do usuário anterior, nem por um instante. Use essa frase para guiar testes manuais e automatizados.

Uma ordem prática de trabalho que pega a maioria dos problemas:

  • Corrija chaves de cache primeiro para que fiquem escopadas por user/org/role.
  • Faça logout e troca de conta limparem caches escopados, resetarem estado em memória e cancelarem requisições em andamento.
  • Reveja regras do service worker e remova cache de endpoints autenticados, salvo quando for intencional.
  • Adicione um teste automatizado “Usuário A então Usuário B” para evitar regressões.

Se você está lidando com uma codebase gerada por IA (de ferramentas como Lovable, Bolt, v0, Cursor ou Replit), esses problemas de identidade e cache são especialmente comuns porque padrões são copiados de forma inconsistente entre telas. FixMyMess (fixmymess.ai) foca em diagnosticar e reparar exatamente esses problemas — colisões de chave de cache, limpeza de logout quebrada e erros de cache de service worker — e pode começar com uma auditoria de código gratuita para apontar onde os dados de outro usuário estão vindo.

Perguntas Frequentes

Por que meu app mostra os dados de outro usuário após logout e login?

Geralmente é uma colisão de cache ou estado: dados buscados para o Usuário A foram armazenados sob uma chave que o Usuário B também usa, ou não foram limpos quando a identidade mudou. O resultado é que o app lê corretamente dados em cache, mas da pessoa errada.

O que uma chave de cache “segura” deve incluir para evitar colisões entre usuários?

Inclua tanto o que são os dados quanto para quem eles são. Um padrão seguro é escopar pela ID do usuário mais o tenant/org/workspace e qualquer contexto que mude permissões como role; depois acrescente filtros de consulta e paginação para que listas não colidam.

O que exatamente deve acontecer no logout ou na troca de conta?

Trate como um mini reboot do estado escopado ao usuário. Limpe ou re-escopo os caches do usuário, cancele requisições em andamento, reinicie quaisquer stores persistidos e refaça consultas essenciais sob a nova identidade para que nada antigo possa renderizar primeiro.

Como conserto isso se estou usando React Query, SWR, Apollo ou RTK Query?

Garanta que a query key mude quando o contexto do usuário mudar e limpe/invalide queries explicitamente no logout/troca. Considere também desabilitar comportamentos de “manter dados anteriores” para queries vinculadas à identidade, para evitar flashes de resultados antigos.

Um service worker pode causar dados de outro usuário, e como eu evito isso?

Por padrão, não armazene respostas de API específicas do usuário no service worker. Cacheie o app shell e ativos estáticos, mas deixe requisições autenticadas irem à rede, e delete caches antigos no activate para evitar que respostas antigas voltem após deploys.

Como SSR/CSR na hidratação pode causar um breve “flash” do perfil errado?

Pode. Se você hidratar a UI a partir de estado persistido no cliente ou reutilizar estado renderizado no servidor de uma sessão anterior, o cliente pode renderizar informações de usuário obsoletas antes da primeira busca terminar. Corrija escopando estado persistido por usuário e adie a exibição de campos sensíveis até a identidade ser confirmada.

Qual a forma mais rápida de reproduzir e depurar um vazamento de dados entre usuários?

Reproduza com um perfil de navegador e duas contas de teste; então registre leituras e escritas no cache com a chave computada e a ID do usuário atual. Se a resposta de rede está correta para o Usuário B mas a UI mostra o Usuário A, você está servindo estado cliente obsoleto ou lendo a chave errada.

Quais casos de borda em dispositivo compartilhado e multi-abas devo testar antes de enviar?

Use o mesmo dispositivo ou perfil, alterne contas sem fechar abas, teste navegação back/forward e limite a rede para tornar condições de corrida visíveis. Teste também duas abas com contas diferentes, pois caches globais e refresh de token podem misturar contextos de forma surpreendente.

Isso é só um bug de UI ou um problema de segurança de verdade?

Sempre aplique autorização no servidor para cada requisição, mesmo que a UI normalmente oculte dados. Trate o bug no cliente como um incidente de privacidade, verifique se tokens/sessões são invalidados no logout e reduza o que você armazena a longo prazo em localStorage ou IndexedDB.

Como o FixMyMess pode ajudar se isto veio de um código gerado por IA?

Codebases geradas por IA frequentemente têm padrões de estado e chaves de cache inconsistentes, tornando esses vazamentos difíceis de rastrear. FixMyMess pode começar com uma auditoria de código gratuita para localizar a colisão ou escrita obsoleta, então reparar limpeza de logout, escopo de cache e regras de service worker para eliminar o flash de dados de outro usuário.