Noções de segurança
Supabase "Bucket not found": as três coisas que isso significa
Supabase "Bucket not found" é uma de três coisas: um link público para um bucket privado, um nome que não bate exatamente ou o projeto errado.

Em resumo
- Supabase "Bucket not found" significa que o Storage procurou um bucket com exatamente esse nome, no projeto que sua requisição alcançou, e não encontrou nenhum que estivesse disposto a servir para essa requisição. O bucket pode continuar lá, no seu painel.
- Um link criado pelo getPublicUrl para um bucket privado dá esse erro quando é aberto, porque o Storage só serve esses links a partir de buckets públicos.
- Num upload, significa que o nome não existe nesse projeto. Confira a grafia e as maiúsculas, e depois para qual projeto seu app aponta.
- Deixar o bucket público acaba com a mensagem e permite que qualquer pessoa com o endereço de um arquivo o abra. Para arquivos que pertencem a uma só pessoa, use um link assinado.
Seu app mostra uma imagem quebrada onde deveria haver uma foto. Ou um upload falha, e a mensagem que volta tem três palavras: "Bucket not found". Aí você abre o Supabase, vai em Storage, e o bucket está ali mesmo, na lista.
Abra o endereço da imagem numa aba separada e o Storage responde isto:
{
"statusCode": "404",
"code": "NoSuchBucket",
"error": "Bucket not found",
"message": "Bucket not found"
}
Esse é o corpo que o código-fonte do Supabase Storage monta hoje, lido em 17 de outubro de 2026. Supabase "Bucket not found" cobre três situações diferentes com esse mesmo corpo, e na primeira delas o bucket está exatamente onde você o deixou.
E é aqui que guia após guia erra: a primeira solução que você vai encontrar é deixar o bucket público, e quando ela funciona, funciona porque cada arquivo desse bucket também passa a ser público. Para duas das três causas, ela não muda absolutamente nada. Para a que sobra, ela troca uma imagem quebrada por um bucket que qualquer pessoa com o endereço de um arquivo consegue ler.
O que significa Supabase "Bucket not found"?
O Storage procurou um bucket com exatamente o nome da sua requisição, dentro do projeto que sua requisição alcançou, e não encontrou nenhum que estivesse disposto a servir para essa requisição.
Essa última parte está ali porque um bucket tem mais de uma porta, e cada porta faz a pergunta do seu jeito. O código-fonte do Storage mostra três delas:
- Um endereço público de arquivo, o que o
getPublicUrlgera. Essa porta só conta os buckets marcados como públicos, então um bucket privado aparece aqui como "não encontrado" mesmo existindo. - Um upload. Essa porta conta todos os buckets do projeto, os privados inclusive. "Não encontrado" aqui significa que esse nome de fato não existe nesse projeto.
- Um pedido da lista de arquivos de um bucket. Essa porta nunca diz "Bucket not found". Um bucket que não existe responde com uma lista vazia, a mesma resposta que um bucket vazio dá, e isso a gente vê de fora toda vez que nosso scanner pergunta.
Então a primeira tarefa é descobrir por qual porta a requisição que falhou entrou.
Os dois números dessa mensagem não batem, e isso confunde o tratamento de
erros. O corpo diz "statusCode": "404", enquanto a requisição em si falha
com o status HTTP 400. O Storage envia esse erro com um 400 e guarda o próprio
404 dentro do corpo. A biblioteca supabase-js repassa os dois para o seu código:
error.status é 400 e error.statusCode é "404". O código que seu builder
escreveu para pegar um 404 vai deixar esse passar.
Causa 1: um link público para um bucket privado
Essa é a causa das imagens quebradas. O endereço foi montado para um bucket público, e o bucket é privado.
O getPublicUrl não pergunta nada ao Supabase. Ele junta o endereço do seu
projeto, o nome do bucket e o nome do arquivo num link, dentro do seu app, e
devolve esse link. A nota acima dessa função, na própria biblioteca cliente do
Supabase, diz isso em duas frases:
Esta função não verifica se o bucket é público. Se uma URL pública for criada para um bucket que não é público, você não vai conseguir baixar o arquivo.
Mais tarde, o navegador de um visitante pede esse link, e o Storage procura o bucket só entre os buckets públicos. Um bucket privado não passa nessa busca, igualzinho a um que não existe, e a resposta é "Bucket not found".
Dá para reconhecer essa causa pelo próprio link. Um endereço público de arquivo
contém /storage/v1/object/public/ seguido do nome do bucket. Se esse bucket
não tem nenhuma marca de público em Storage → Buckets, o link nunca ia
abrir, seja qual for a grafia do nome.
Buckets novos começam privados, então a configuração está se comportando como deveria. O que aconteceu é que o código, seu ou do seu builder, foi escrito para um bucket público e apontado para um privado.
Causa 2: um nome que não é bem o nome do bucket
O Storage compara o nome do bucket caractere por caractere, então Avatars e
avatars são dois buckets diferentes, assim como avatar e avatars.
No código-fonte do Storage, a busca é uma igualdade simples sobre o id do bucket. Maiúsculas contam, uma letra faltando conta, e um hífen onde o bucket tem um sublinhado conta. Cada um desses casos volta como as mesmas três palavras.
Um app feito com uma ferramenta de IA pode chegar aqui sem ninguém ter digitado nada errado. O código que envia ou mostra um arquivo é escrito numa etapa, e o bucket que ele cita deveria ser criado em outra. Se essa segunda etapa nunca rodou, ou rodou com um nome um pouco diferente, o código está pedindo um bucket que ninguém criou.
A conferência pede duas listas. Abra Storage → Buckets e anote o nome de cada bucket exatamente como aparece. Depois diga ao seu builder, com palavras simples: liste cada nome de bucket do Storage que este app usa, e me mostre onde cada um é usado. Qualquer nome da lista dele que falte na sua é o que está falhando.
Causa 3: o bucket está em outro projeto
Seu app conversa com um único projeto do Supabase, o do endereço do Supabase dele, e um bucket criado em qualquer outro projeto simplesmente não existe para o seu app.
É num upload que essa causa aparece com mais clareza. Num upload, o Storage procura o bucket no projeto inteiro sem aplicar suas regras de acesso, então "Bucket not found" num upload nunca é um problema de permissão. Quando o bucket existe e suas regras recusam o arquivo, o Storage responde com outra mensagem, que fala de row-level security. Então "Bucket not found" num upload significa que esse nome não existe no projeto em que seu app está gravando.
Existem jeitos bem comuns de acabar com dois projetos: um projeto de teste ao lado do de produção, uma cópia do app ligada a um projeto novo, um projeto criado na mão para testar alguma coisa. O bucket é criado naquele que estava aberto no painel naquele momento.
A conferência é uma comparação entre duas sequências curtas. O endereço do
Supabase do seu app tem a forma https://<ref>.supabase.co, e a parte antes de
.supabase.co é a referência do projeto. O endereço do seu painel traz a mesma
referência depois de /project/. Se as duas forem diferentes, você está olhando
para um projeto enquanto seu app conversa com outro.
Você deve deixar o bucket público para resolver?
Só quando cada arquivo do bucket for feito para qualquer pessoa que tenha o endereço, e só para a primeira causa. Com um nome errado ou um projeto errado, o botão não muda nada.
Um bucket público faz uma coisa só, e a documentação do Supabase a descreve em uma linha: qualquer pessoa que tenha o endereço de um arquivo consegue abri-lo. Nenhum login entra nisso. Os próprios exemplos do Supabase são fotos de perfil, mídia pública e conteúdo de posts de blog, e logos ou fotos de produtos entram no mesmo grupo.
É a configuração errada para notas fiscais, documentos de identidade escaneados, exportações ou qualquer coisa que pertença a uma só pessoa. O botão vale para o bucket inteiro e para cada arquivo que chegar nele depois, inclusive arquivos de uma funcionalidade que você ainda nem construiu.
O mesmo atalho tem um gêmeo do lado das regras, e é esse que a gente mede. Uma
regra em storage.objects que só cita o bucket faz os downloads funcionarem
para todo mundo, e também entrega a lista de nomes do bucket. Escaneamos 8.435
apps no ar que citam um projeto do Supabase. A verificação de buckets teve
resposta em 4.703 deles, e 792 responderam ao nosso pedido de lista com os
nomes do que havia dentro, sem ninguém logado. Isso é mais ou menos um em
cada seis dos apps que conseguimos consultar. Quando um bucket é público e a
lista dele também está aberta, cada nome dessa lista é um link que funciona.
Como mostrar um arquivo privado sem deixá-lo público
Dê à página um link assinado: um endereço que funciona pelo número de segundos que você escolher e depois para de funcionar.
A documentação do Supabase cita duas formas de tirar um arquivo de um bucket
privado. Uma é um download que leva a sessão da pessoa logada. A outra é o
createSignedUrl, que gera um link "que pode ser acessado por um tempo
limitado". É essa segunda que entra no lugar do getPublicUrl em qualquer
página que mostre uma imagem.
Um link assinado continua obedecendo às suas regras. Antes de o Storage
assinar qualquer coisa, o código-fonte dele confere se a pessoa que está pedindo
pode ler aquele arquivo, então um bucket privado precisa de uma regra select
em storage.objects que diga quem pode ler o quê.
O erro de upload explica como essas regras são escritas,
inclusive por que uma regra de leitura que só cita o bucket também abre a lista
dele.
Para passar isso ao seu builder, com palavras simples: para o bucket que dá o erro "Bucket not found", troque getPublicUrl por createSignedUrl, mantenha o bucket privado e adicione uma policy de Storage que deixe cada pessoa ler só os arquivos que ela deve ver. Depois recarregue a página que estava com a imagem quebrada.
O que seus buckets estão mostrando a estranhos agora
Duas telas do painel dizem o que você configurou, e uma verificação de fora diz o que um estranho recebe.
Storage → Buckets mostra quais buckets são públicos. Para cada um, pergunte
se cada arquivo ali é algo que você mostraria a um estranho. Storage →
Policies mostra as regras. Uma regra select cuja única condição é o nome do
bucket deixa qualquer pessoa ler esse bucket, lista de arquivos incluída.
Nosso scan gratuito pede ao seu projeto no ar a lista de arquivos de cada bucket usando só a chave que já está no código do seu app, do jeito que um estranho faria, e diz quais buckets responderam com nomes. Ele lê nomes e nunca baixa um arquivo. Leva uns 20 segundos e não precisa de conta: escaneie seu app.
Mantendo seus arquivos onde você os colocou
Depois da correção de hoje, duas coisas ainda podem mudar: alguém mexe numa configuração enquanto corre atrás de outro bug, e um arquivo de que alguém precisava é apagado.
Reeve Monitor continua verificando de fora:
- a verificação de segurança completa, repetida num cronograma, incluindo a da listagem de buckets
- se seu app está no ar
- uma mensagem quando um resultado muda, para que um bucket que abriu durante a noite não fique esperando você olhar
- um relatório mensal, em linguagem simples, do que ele viu
Reeve Care guarda uma cópia do que tem lá dentro:
- uma cópia criptografada do seu banco de dados do Supabase num cronograma, no armazenamento próprio da Reeve na UE, fora da sua conta do Supabase
- cada cópia verificada antes de valer, contando as linhas de cada tabela
- também seus arquivos enviados, assim que você conecta uma credencial do Storage
- uma restauração com um clique, com uma cópia de segurança do estado atual feita antes
- tudo o que o Monitor faz
O que fazer hoje
O que fazer
- Abra o link que falha numa aba separada. Se o endereço contém
/storage/v1/object/public/e o bucket é privado, é a primeira causa. - Copie o nome do bucket em Storage → Buckets e compare, maiúsculas incluídas, com o nome que seu app usa.
- Compare a referência do projeto no endereço do Supabase do seu app com a do endereço do seu painel.
- Mantenha um bucket privado a menos que cada arquivo dele seja feito para qualquer pessoa que tenha o endereço, e sirva arquivos privados por links assinados.
- Leia cada regra
selectemstorage.objects. Uma que só cita o bucket entrega também a lista de arquivos.
Comece pela requisição que falhou, porque a porta por onde ela entrou diz qual das três causas conferir. O checklist de segurança de 10 minutos cobre o que um app recém-lançado costuma deixar aberto além disso, e o que um bucket público expõe é a próxima leitura se algum dos seus se revelou público.
Perguntas frequentes
Por que o Supabase diz que meu bucket não existe se eu estou vendo ele?
Porque o Storage procura um bucket de jeitos diferentes conforme a requisição. Um endereço público de arquivo, o que o getPublicUrl gera, só é procurado entre os buckets públicos, então um bucket privado que você vê no painel aparece ali como não encontrado. Se o erro veio de um upload, o nome no seu código não bate exatamente com nenhum bucket do projeto ao qual seu app está conectado.
O nome de um bucket do Supabase diferencia maiúsculas e minúsculas?
Sim. O Storage compara o nome caractere por caractere, então Avatars e avatars são dois buckets diferentes, assim como avatar e avatars. Copie o nome em Storage → Buckets no seu painel e compare com o nome que seu app usa.
Devo deixar meu bucket público para resolver "Bucket not found"?
Só se cada arquivo dele for feito para qualquer pessoa que tenha o endereço, como fotos de perfil ou fotos de produtos. Público significa que qualquer pessoa com o endereço de um arquivo pode abri-lo sem entrar na conta. Isso só resolve o erro quando um link público foi criado para um bucket privado; um nome errado ou um projeto errado continua quebrado de qualquer jeito. Para arquivos que pertencem a uma só pessoa, mantenha o bucket privado e use um link assinado.
Como mostro um arquivo de um bucket privado?
Com um link assinado. O createSignedUrl gera um endereço que funciona pelo número de segundos que você escolher e depois para de funcionar, e ele entra no lugar do getPublicUrl em qualquer página que mostre o arquivo. Quem pede o link precisa ter permissão para ler o arquivo, então o bucket precisa de uma regra select em storage.objects que diga quem pode ler o quê.
Por que o erro diz 404 se a requisição falhou com 400?
O Supabase Storage envia esse erro com o status HTTP 400 e coloca o próprio status, 404, dentro do corpo como statusCode. No supabase-js isso chega como error.status 400 e error.statusCode "404", e é por isso que um código escrito para pegar um 404 deixa esse erro passar sem perceber.