[{"data":1,"prerenderedAt":1128},["ShallowReactive",2],{"blog-pt-stripe-webhook-no-signatures-found":3,"blog-index-pt":598},{"id":4,"title":5,"body":6,"category":555,"cover":556,"coverAlt":557,"description":558,"draft":559,"extension":560,"faq":561,"image":577,"keywords":578,"meta":586,"navigation":587,"ogTitle":588,"path":589,"published":590,"seo":591,"stem":592,"tldr":593,"updated":590,"__hash__":597},"blog_pt\u002Fblog\u002Fstripe-webhook-no-signatures-found.md","\"No signatures found matching the expected signature\" no Supabase",{"type":7,"value":8,"toc":543},"minimark",[9,13,24,32,37,40,47,68,71,75,78,84,95,104,110,117,123,126,130,133,136,179,206,209,214,218,226,311,318,322,325,345,351,365,368,372,375,389,396,399,419,431,435,441,450,464,474,491,495,530],[10,11,12],"p",{},"Você conectou o Stripe ao seu app, um pagamento de teste passou, e o webhook que\ndeveria marcar o pedido como pago não fez nada. O painel do Stripe mostra a\nentrega falhando, e os logs da sua Edge Function do Supabase dizem isto:",[14,15,20],"pre",{"className":16,"code":18,"language":19},[17],"language-text","No signatures found matching the expected signature for payload.\nAre you passing the raw request body you received from Stripe?\n","text",[21,22,18],"code",{"__ignoreMap":23},"",[10,25,26,27,31],{},"A boa notícia está na origem do erro. Ele é lançado pela parte da sua função que\nconfere se a requisição veio mesmo do Stripe, ou seja, essa conferência existe e\nestá fazendo o trabalho dela. ",[28,29,30],"strong",{},"\"No signatures found matching the expected\nsignature for payload\" tem duas causas, e nenhuma se resolve conferindo\nmenos."," A correção que aparece primeiro quando se pesquisa, embrulhar a\nconferência para o erro ser ignorado, é justamente a que deixa qualquer pessoa\nmarcar um pedido como pago.",[33,34,36],"h2",{"id":35},"o-que-significa-no-signatures-found-matching-the-expected-signature","O que significa \"No signatures found matching the expected signature\"?",[10,38,39],{},"Sua função conferiu a assinatura do Stripe na requisição, e ela não bateu.",[10,41,42,43,46],{},"Pense em cada webhook que o Stripe envia como uma carta fechada com um selo de\ncera. O Stripe pressiona o selo com um sinete que só ele e você têm: o signing\nsecret do seu endpoint, um valor que começa com ",[21,44,45],{},"whsec_",". Quando a carta chega,\nsua função pressiona a própria cópia do sinete contra a cera. Se os desenhos\nbatem, a carta é do Stripe e ninguém mexeu nela no caminho.",[10,48,49,50,53,54,61,62,67],{},"O selo cobre a carta exatamente como foi escrita. A biblioteca do Stripe pega o\ntexto da requisição, o horário do cabeçalho ",[21,51,52],{},"Stripe-Signature"," e o seu secret,\ncalcula como a assinatura deveria ser e procura por ela nesse cabeçalho. A\nmensagem acima está escrita\n",[55,56,60],"a",{"href":57,"rel":58},"https:\u002F\u002Fgithub.com\u002Fstripe\u002Fstripe-node\u002Fblob\u002Fmaster\u002Fsrc\u002FWebhooks.ts",[59],"nofollow","no próprio stripe-node",",\ne é o que ele lança quando nada ali bate. A\n",[55,63,66],{"href":64,"rel":65},"https:\u002F\u002Fdocs.stripe.com\u002Fwebhooks\u002Fsignature",[59],"página do Stripe sobre erros de assinatura","\ndiz sem rodeios: pelo menos um dos três valores que você passou está errado.",[10,69,70],{},"Então há dois lugares para olhar. Ou a carta foi alterada antes de a sua função\nconferir o selo, ou a sua função está com o sinete errado.",[33,72,74],{"id":73},"causa-1-o-corpo-foi-alterado-antes-da-conferência","Causa 1: o corpo foi alterado antes da conferência",[10,76,77],{},"Numa Edge Function, a versão mais comum é um corpo lido primeiro como JSON e\ndepois transformado de volta em texto para a conferência.",[14,79,82],{"className":80,"code":81,"language":19},[17],"const event = await req.json()\nawait stripe.webhooks.constructEventAsync(JSON.stringify(event), signature, secret, undefined, cryptoProvider)\n",[21,83,81],{"__ignoreMap":23},[10,85,86,87,90,91,94],{},"Parece natural e falha toda vez. ",[21,88,89],{},"req.json()"," fica com os dados e joga fora o\ntexto: os espaços, as quebras de linha, a ordem em que o Stripe escreveu as\ncoisas. ",[21,92,93],{},"JSON.stringify"," então escreve um texto novo com os mesmos dados dentro.\nO selo foi pressionado sobre o texto do Stripe, e isto é a cópia de outra pessoa,\ncom outra letra, então nunca vai bater.",[10,96,97,98,103],{},"O próprio\n",[55,99,102],{"href":100,"rel":101},"https:\u002F\u002Fgithub.com\u002Fsupabase\u002Fsupabase\u002Fblob\u002Fmaster\u002Fexamples\u002Fedge-functions\u002Fsupabase\u002Ffunctions\u002Fstripe-webhooks\u002Findex.ts",[59],"exemplo de webhook do Stripe do Supabase","\nfaz diferente: lê o corpo uma vez só, como texto, e repassa esse texto sem\nmexer:",[14,105,108],{"className":106,"code":107,"language":19},[17],"const signature = req.headers.get('Stripe-Signature')\nconst body = await req.text()\nconst event = await stripe.webhooks.constructEventAsync(\n  body,\n  signature,\n  Deno.env.get('STRIPE_WEBHOOK_SIGNING_SECRET'),\n  undefined,\n  cryptoProvider,\n)\n",[21,109,107],{"__ignoreMap":23},[10,111,112,113,116],{},"O comentário nesse exemplo diz por quê: a verificação depende do corpo bruto da\nrequisição, e não do JSON já interpretado. Você não precisa interpretar nada\ndepois, porque ",[21,114,115],{},"constructEventAsync"," devolve o evento já interpretado assim que o\nselo confere.",[118,119],"diagram",{"alt":120,"caption":121,"src":122},"Duas cópias do mesmo corpo de webhook lado a lado. A da esquerda é o texto exatamente como chegou, e o selo dela bate com o sinete, com um visto. A da direita tem os mesmos valores reescritos em outras linhas, e o mesmo sinete não bate mais, riscado em amarelo.","A assinatura cobre o texto que o Stripe enviou, não os dados dentro dele. Um texto reescrito com dados idênticos não passa na conferência.","\u002Fblog\u002Fstripe-webhook-no-signatures-found\u002Fsame-data-other-text-1600x720.png",[10,124,125],{},"O mesmo acontece fora do seu código. A página do Stripe cita frameworks que\ninterpretam ou reformatam um corpo antes de o seu código vê-lo, e a mensagem do\nstripe-node acrescenta que uma ferramenta que repassa webhooks para você precisa\npreservar o corpo exato, incluindo a formatação do JSON e o tipo de quebra de\nlinha. Se há um proxy ou um serviço de repasse entre o Stripe e a sua função,\nconfira se ele entrega o corpo exatamente como chegou.",[33,127,129],{"id":128},"causa-2-o-signing-secret-é-o-errado","Causa 2: o signing secret é o errado",[10,131,132],{},"Se o corpo está intacto, o sinete é o errado, e a página do Stripe diz que essa\né a causa mais comum de todas.",[10,134,135],{},"Três valores diferentes podem acabar no lugar do signing secret, e eles se\nparecem:",[137,138,139,152,165],"ul",{},[140,141,142,145,146,151],"li",{},[28,143,144],{},"O secret de outro endpoint."," O\n",[55,147,150],{"href":148,"rel":149},"https:\u002F\u002Fdocs.stripe.com\u002Fwebhooks",[59],"guia de webhooks do Stripe"," diz que cada\nendpoint recebe o seu próprio secret e que o mesmo endereço configurado no\nmodo de teste e no modo live tem um secret diferente em cada um. Um pagamento\nlive conferido com o secret do endpoint de teste falha, e o contrário também.",[140,153,154,161,162,164],{},[28,155,156,157,160],{},"O secret que o ",[21,158,159],{},"stripe listen"," mostra."," Quando você repassa eventos para a\nsua máquina com a ferramenta de linha de comando do Stripe, ela mostra um\nsecret ",[21,163,45],{}," próprio. O Stripe diz que ele é diferente do secret do\nendpoint do painel e que os dois são confundidos. Ele funciona para os eventos\nque a ferramenta repassa, e para mais nada.",[140,166,167,170,171,174,175,178],{},[28,168,169],{},"Sua chave de API do Stripe."," ",[21,172,173],{},"sk_live_…"," ou ",[21,176,177],{},"sk_test_…"," é a chave com que a\nsua função chama o Stripe. Não é um signing secret, e uma conferência feita\ncom ela nunca vai bater.",[10,180,181,182,185,186,189,190,193,194,197,198,201,202,205],{},"Para achar o certo, abra o endpoint no painel do Stripe em ",[28,183,184],{},"Workbench",", depois\n",[28,187,188],{},"Webhooks",", e clique em ",[28,191,192],{},"Reveal secret",". Antes, veja em qual modo você está.\nDepois compare com o que a sua função lê. No exemplo acima é\n",[21,195,196],{},"STRIPE_WEBHOOK_SIGNING_SECRET",", definido na página ",[28,199,200],{},"Edge Function Secrets"," do\nseu projeto Supabase ou com ",[21,203,204],{},"supabase secrets set",".",[10,207,208],{},"Já que você está lá, confira mais uma coisa. Se o valor foi colado com um espaço\nou uma quebra de linha no fim, o stripe-node acrescenta uma nota ao mesmo erro:\n\"The provided signing secret contains whitespace.\" Apague o valor e cole de novo,\nsem nada depois.",[118,210],{"alt":211,"caption":212,"src":213},"Um envelope selado à esquerda e três sinetes à direita, marcados whsec_, stripe listen e sk_live_. Só um dos três tem o mesmo desenho do selo e está com um visto. Os outros dois, o secret que o stripe listen mostra e uma chave de API sk_live_, têm outros desenhos e estão riscados em amarelo.","Cada endpoint, cada modo e a ferramenta de linha de comando assinam com o próprio secret. Só o que assinou este evento consegue conferi-lo.","\u002Fblog\u002Fstripe-webhook-no-signatures-found\u002Fwhich-stamp-1600x720.png",[33,215,217],{"id":216},"as-mensagens-que-parecem-iguais-e-querem-dizer-outra-coisa","As mensagens que parecem iguais e querem dizer outra coisa",[10,219,220,221,225],{},"O stripe-node lança vários erros da mesma conferência, e cada um aponta para um\nproblema diferente. Todos estão escritos\n",[55,222,224],{"href":57,"rel":223},[59],"no código de webhooks dele",":",[227,228,229,242],"table",{},[230,231,232],"thead",{},[233,234,235,239],"tr",{},[236,237,238],"th",{},"O que seus logs dizem",[236,240,241],{},"O que aconteceu",[243,244,245,256,272,285,295],"tbody",{},[233,246,247,253],{},[248,249,250],"td",{},[21,251,252],{},"No signatures found matching the expected signature for payload",[248,254,255],{},"O corpo foi alterado, ou o secret é o errado. Este artigo.",[233,257,258,263],{},[248,259,260],{},[21,261,262],{},"Webhook payload must be provided as a string or a Buffer",[248,264,265,266,268,269,205],{},"Você passou o objeto interpretado de ",[21,267,89],{},". Passe o texto de ",[21,270,271],{},"req.text()",[233,273,274,279],{},[248,275,276],{},[21,277,278],{},"No stripe-signature header value was provided.",[248,280,281,282,284],{},"A requisição veio sem o cabeçalho ",[21,283,52],{},". Talvez nem tenha vindo do Stripe.",[233,286,287,292],{},[248,288,289],{},[21,290,291],{},"Timestamp outside the tolerance zone",[248,293,294],{},"O selo bateu, mas o evento é mais antigo que o limite. As bibliotecas do Stripe usam 5 minutos.",[233,296,297,302],{},[248,298,299],{},[21,300,301],{},"SubtleCryptoProvider cannot be used in a synchronous context.",[248,303,304,305,308,309,205],{},"Você chamou ",[21,306,307],{},"constructEvent",". Com o provedor Web Crypto de que o Deno precisa, use ",[21,310,115],{},[10,312,313,314,317],{},"E uma que nunca chega ao seu código. Se o painel do Stripe mostra a entrega\nfalhando com um ",[28,315,316],{},"401"," e os logs da sua função estão vazios, a plataforma do\nSupabase recusou a requisição antes de a sua função rodar, por causa de uma\nverificação que precisa estar desligada num webhook.",[33,319,321],{"id":320},"por-que-seu-webhook-precisa-do-verify_jwt-desligado","Por que seu webhook precisa do verify_jwt desligado",[10,323,324],{},"Porque o Stripe não consegue provar quem é do jeito que os usuários do seu app\nprovam, e com a verificação ligada toda entrega é recusada.",[10,326,327,328,331,332,335,336,341,342,225],{},"O Supabase põe na frente de cada Edge Function uma verificação chamada\n",[21,329,330],{},"verify_jwt",", ligada por padrão. Ela pede um token do Supabase no cabeçalho\n",[21,333,334],{},"Authorization"," da requisição, e o Stripe não tem nenhum para enviar. O\n",[55,337,340],{"href":338,"rel":339},"https:\u002F\u002Fsupabase.com\u002Fdocs\u002Fguides\u002Ffunctions\u002Ffunction-configuration",[59],"guia de configuração de funções","\ndo Supabase usa exatamente isso como exemplo, dizendo que webhooks do Stripe\nprecisam ser acessíveis publicamente, e desliga a verificação só para essa\nfunção em ",[21,343,344],{},"supabase\u002Fconfig.toml",[14,346,349],{"className":347,"code":348,"language":19},[17],"[functions.stripe-webhook]\nverify_jwt = false\n",[21,350,348],{"__ignoreMap":23},[10,352,353,354,357,358,361,362,205],{},"O comando ",[21,355,356],{},"supabase functions deploy"," aceita ",[21,359,360],{},"--no-verify-jwt"," para a mesma\ncoisa. O exemplo de Stripe do Supabase traz a mesma instrução num comentário: o\nStripe verifica a requisição pela assinatura, então faça o deploy com\n",[21,363,364],{},"verify_jwt = false",[10,366,367],{},"Com a verificação da plataforma desligada, qualquer requisição da internet chega\nao seu código, e a conferência do selo dentro da sua função é a única coisa que\nsepara as cartas do Stripe das de qualquer outra pessoa.",[33,369,371],{"id":370},"por-que-você-nunca-deve-pular-a-conferência-de-assinatura","Por que você nunca deve pular a conferência de assinatura",[10,373,374],{},"Porque sem ela qualquer pessoa pode dizer ao seu app que um pagamento foi\naprovado.",[10,376,377,378,381,382,385,386,388],{},"O endereço de uma função de webhook é a URL do seu projeto, que está no seu\nfrontend para qualquer um ler, seguida de ",[21,379,380],{},"\u002Ffunctions\u002Fv1\u002F"," e do nome da função,\nque costuma ser o óbvio. O exemplo do guia de configuração chama a função de\n",[21,383,384],{},"stripe-webhook",". Com o ",[21,387,330],{}," desligado, a plataforma deixa toda\nrequisição passar até o seu código. Se o seu código não confere mais o selo, ele\nacredita em toda carta que chega.",[10,390,391,392,395],{},"Então um estranho envia para a sua função uma requisição dizendo\n",[21,393,394],{},"checkout.session.completed",", com um pedido ou um cliente escolhido por ele. Sua\nfunção faz o que faz com uma de verdade: marca o pedido como pago, sobe o plano\nda conta, libera o download. Ninguém pagou. O Stripe não tem registro disso,\nporque o Stripe nunca enviou.",[10,397,398],{},"Três jeitos de pular a conferência terminam todos aí:",[137,400,401,407,413],{},[140,402,403,406],{},[28,404,405],{},"Apagá-la"," porque não passava.",[140,408,409,412],{},[28,410,411],{},"Capturar o erro e seguir em frente",", registrando a falha e processando o\nevento mesmo assim. O exemplo devolve um 400 do bloco catch, e essa é a forma\na manter: uma requisição que falha na conferência para ali.",[140,414,415,418],{},[28,416,417],{},"Colocar a tolerância de tempo em 0."," O guia de webhooks do Stripe diz para\nnão fazer isso: 0 desliga totalmente a verificação de idade, e um evento real\nque alguém capturou pode ser reenviado depois.",[10,420,421,422,426,427,205],{},"Uma chave secreta do Stripe ativa no seu frontend é o outro jeito de os\npagamentos darem errado, e é o que conseguimos ver de fora: encontramos uma em\n",[55,423,425],{"href":424},"\u002Fblog\u002Fstripe-secret-key-in-frontend","3 de 30.998 apps escaneados",". A nossa\nvarredura gratuita lê o JavaScript do seu app publicado como um estranho leria e\naponta cada chave que encontra, dizendo se ela é das que devem estar ali. Leva\nuns 20 segundos e não precisa de conta: ",[55,428,430],{"href":429},"\u002Fsecurity-scanner","escaneie o seu app",[33,432,434],{"id":433},"se-um-pagamento-falso-passar-algum-dia","Se um pagamento falso passar algum dia",[10,436,437,440],{},[28,438,439],{},"Uma conferência que um prompt removeu não avisa."," Um builder a quem você pede\npara fazer o webhook voltar a funcionar pode pegar o atalho acima, e o seu app\ncontinua funcionando enquanto isso.",[10,442,443],{},[28,444,445,449],{},[55,446,448],{"href":447},"\u002Fwebsite-monitoring","O Reeve Monitor"," acompanha o que um estranho consegue ver de fora:",[137,451,452,455,458,461],{},[140,453,454],{},"as nove verificações a cada hora, em até três apps",[140,456,457],{},"um e-mail no dia em que uma nova varredura avaliar o seu app pior que a anterior; uma chave secreta no seu JavaScript limita sozinha a nota a D",[140,459,460],{},"se o app está no ar, a cada 60 segundos",[140,462,463],{},"um relatório mensal do que ele viu",[10,465,466],{},[28,467,468,469,473],{},"Um evento falso grava no seu banco de dados como um verdadeiro.\n",[55,470,472],{"href":471},"\u002Fsupabase-backups","O Reeve Care"," guarda uma cópia do seu banco Supabase de antes\nde acontecer.",[137,475,476,479,482,485,488],{},[140,477,478],{},"uma cópia criptografada conforme uma agenda, guardada no armazenamento do próprio Reeve na UE, fora da sua conta Supabase",[140,480,481],{},"cada cópia verificada antes de contar, contando as linhas de cada tabela",[140,483,484],{},"uma restauração em um clique que salva o que está lá agora antes de substituir",[140,486,487],{},"também os seus arquivos enviados, assim que você conectar uma credencial de Storage",[140,489,490],{},"tudo o que o Monitor faz",[33,492,494],{"id":493},"o-que-fazer-agora","O que fazer agora",[496,497,498],"key-takeaways",{},[137,499,500,503,515,521,527],{},[140,501,502],{},"Abra a entrega que falhou no painel do Stripe e a aba Logs da sua função, e leia a mensagem exata antes de mudar qualquer coisa.",[140,504,505,506,509,510,512,513,205],{},"Leia o corpo com ",[21,507,508],{},"await req.text()"," e passe esse texto intacto para ",[21,511,115],{},". Nunca confira um evento que passou por ",[21,514,93],{},[140,516,517,518,520],{},"Copie o secret ",[21,519,45],{}," do endpoint de onde o Stripe envia, no modo certo, e cole em Edge Function Secrets sem nada depois.",[140,522,523,524,526],{},"Mantenha ",[21,525,364],{}," no webhook, e a conferência de assinatura como a primeira coisa que a função faz.",[140,528,529],{},"Devolva um 400 quando a conferência falhar. Nunca capture o erro para processar o evento mesmo assim.",[10,531,532,533,537,538,542],{},"O resto do que uma função que guarda um secret precisa está em\n",[55,534,536],{"href":535},"\u002Fblog\u002Fmove-a-secret-to-an-edge-function","mover um secret para uma Edge Function",",\ne se o seu erro é o genérico do supabase-js,\n",[55,539,541],{"href":540},"\u002Fblog\u002Fedge-function-returned-a-non-2xx-status-code","ler um código de status non-2xx","\nmostra onde está a mensagem real.",{"title":23,"searchDepth":544,"depth":544,"links":545},3,[546,548,549,550,551,552,553,554],{"id":35,"depth":547,"text":36},2,{"id":73,"depth":547,"text":74},{"id":128,"depth":547,"text":129},{"id":216,"depth":547,"text":217},{"id":320,"depth":547,"text":321},{"id":370,"depth":547,"text":371},{"id":433,"depth":547,"text":434},{"id":493,"depth":547,"text":494},"Noções de segurança","\u002Fblog\u002Fstripe-webhook-no-signatures-found\u002Fcover-1200x630.png","Um envelope fechado com um selo de cera turquesa e, ao lado, um sinete cujo desenho não bate com a marca, destacado em amarelo.","O webhook do Stripe numa Edge Function do Supabase diz \"No signatures found matching the expected signature\"? São duas causas. Continue verificando.",false,"md",[562,565,568,571,574],{"q":563,"a":564},"O que significa \"No signatures found matching the expected signature for payload\"?","Sua função de webhook calculou a assinatura que esperava a partir do corpo da requisição e do seu signing secret, e nenhuma das assinaturas do cabeçalho Stripe-Signature bateu. A biblioteca do Stripe lança esse erro quando o corpo que recebe não é, byte a byte, o que o Stripe enviou, ou quando o secret não é o do endpoint que enviou o evento.",{"q":566,"a":567},"Por que falha quando passo JSON.stringify do evento?","Porque a assinatura cobre o texto exato que o Stripe enviou, com espaços e quebras de linha. Ler o corpo com req.json() e transformá-lo de volta em texto com JSON.stringify dá os mesmos dados em outro texto, e outro texto tem outra assinatura. Leia o corpo uma vez com await req.text() e passe esse texto para constructEventAsync.",{"q":569,"a":570},"Qual signing secret eu uso?","O secret whsec_ do endpoint exato para o qual o Stripe entrega. Cada endpoint tem o seu, o mesmo endereço configurado no modo de teste e no modo live tem um secret diferente em cada um, e o comando stripe listen mostra um secret próprio, que só vale para os eventos que ele repassa. Sua chave de API sk_ não é um signing secret.",{"q":572,"a":573},"Minha Edge Function de webhook do Stripe deve ficar com verify_jwt desligado?","Sim. O Stripe não tem um token do Supabase para enviar, então com a verificação ligada toda entrega é recusada com um 401 antes de o seu código rodar. O próprio exemplo de Stripe do Supabase diz para fazer o deploy com verify_jwt = false. Isso faz da conferência de assinatura dentro da sua função a única verificação que existe, e ela nunca pode ser pulada nem capturada e ignorada.",{"q":575,"a":576},"Posso remover a conferência de assinatura para os pagamentos funcionarem?","Pode, e então qualquer pessoa que encontrar o endereço da função poderá enviar a ela um evento dizendo que um pagamento foi aprovado. O endereço é a URL do seu projeto, que está no seu frontend, mais o nome da função. Sem a conferência, sua função acredita em toda requisição que recebe e libera o que o evento disser que foi pago.","\u002Fblog\u002Fstripe-webhook-no-signatures-found\u002Fcard-800x500.png",[579,580,581,582,583,584,585],"no signatures found matching the expected signature for payload","stripe webhook supabase edge function","stripe signature verification failed","constructEventAsync deno","stripe webhook raw body","stripe webhook signing secret","supabase stripe webhook 401",{},true,"\"No signatures found matching the expected signature\"","\u002Fblog\u002Fstripe-webhook-no-signatures-found","2026-10-29",{"title":5,"description":558},"blog\u002Fstripe-webhook-no-signatures-found",[594,595,596],"\"No signatures found matching the expected signature for payload\" quer dizer que sua função conferiu a assinatura do Stripe e ela não bateu. Ou o corpo foi alterado antes da conferência, ou o signing secret não é aquele com que o Stripe assinou.","Leia o corpo com await req.text() e passe esse texto, intacto, para constructEventAsync. Depois compare o secret whsec_ que sua função usa com o do endpoint exato de onde o Stripe está enviando.","Não faça o erro sumir pulando a conferência. O webhook precisa rodar com verify_jwt = false, então a assinatura é a única coisa que impede um estranho de dizer ao seu app que um pagamento foi aprovado.","UTQv-J6mncml1om_y4B7w-snwxM-N0lOAGUmLacTHWU",[599,605,611,617,623,629,635,641,647,653,659,666,667,673,678,684,690,696,702,708,714,720,726,732,738,744,750,756,762,768,774,780,786,792,798,804,809,815,821,827,833,839,845,851,857,863,868,874,880,886,892,898,904,909,915,921,927,933,939,945,951,957,963,969,975,981,987,993,999,1005,1011,1017,1023,1029,1035,1041,1047,1053,1059,1065,1071,1077,1083,1089,1094,1100,1106,1112,1117,1123],{"path":600,"title":601,"description":602,"published":603,"category":555,"image":604,"draft":559},"\u002Fblog\u002Fmax-client-connections-reached","Supabase \"max client connections reached\", explicado","Supabase \"max client connections reached\": o pooler não tem lugar para mais um cliente. A porta da sua string de conexão define o limite.","2026-11-09","\u002Fblog\u002Fmax-client-connections-reached\u002Fcard-800x500.png",{"path":606,"title":607,"description":608,"published":609,"category":555,"image":610,"draft":559},"\u002Fblog\u002Funsupported-provider-provider-is-not-enabled","Erro do Supabase: \"Unsupported provider: provider is not enabled\"","\"Unsupported provider: provider is not enabled\": o Google está desativado no seu projeto Supabase. Como ativar e corrigir o redirect_uri_mismatch.","2026-11-08","\u002Fblog\u002Funsupported-provider-provider-is-not-enabled\u002Fcard-800x500.png",{"path":612,"title":613,"description":614,"published":615,"category":555,"image":616,"draft":559},"\u002Fblog\u002Fviolates-foreign-key-constraint","Erro do Supabase \"violates foreign key constraint\" ao inserir","Supabase \"insert or update on table violates foreign key constraint\": sua linha aponta para uma linha que não existe. Qual, por quê, e como corrigir.","2026-11-07","\u002Fblog\u002Fviolates-foreign-key-constraint\u002Fcard-800x500.png",{"path":618,"title":619,"description":620,"published":621,"category":555,"image":622,"draft":559},"\u002Fblog\u002Fnull-value-violates-not-null-constraint","Erro do Supabase \"null value in column violates not-null constraint\"","Supabase \"null value in column violates not-null constraint\": uma coluna obrigatória chegou vazia. De onde veio o null, e como corrigir.","2026-11-06","\u002Fblog\u002Fnull-value-violates-not-null-constraint\u002Fcard-800x500.png",{"path":624,"title":625,"description":626,"published":627,"category":555,"image":628,"draft":559},"\u002Fblog\u002Fauth-session-missing","Supabase \"Auth session missing!\": para onde foi a sessão","\"Auth session missing!\" é o supabase-js sem achar nenhuma sessão logada e sem chegar a perguntar ao Supabase. Para onde foi a sessão, e a correção.","2026-11-05","\u002Fblog\u002Fauth-session-missing\u002Fcard-800x500.png",{"path":630,"title":631,"description":632,"published":633,"category":555,"image":634,"draft":559},"\u002Fblog\u002Finvalid-claim-missing-sub-claim","Supabase \"invalid claim: missing sub claim\": ninguém está logado","\"invalid claim: missing sub claim\": o Supabase Auth recebeu um token autêntico sem nenhum usuário, a chave do projeto no lugar de uma sessão.","2026-11-04","\u002Fblog\u002Finvalid-claim-missing-sub-claim\u002Fcard-800x500.png",{"path":636,"title":637,"description":638,"published":639,"category":555,"image":640,"draft":559},"\u002Fblog\u002Ffailed-to-send-a-request-to-the-edge-function","\"Failed to send a request to the Edge Function\": a causa real","\"Failed to send a request to the Edge Function\": o navegador não recebeu uma resposta legível. O console dele diz por quê e se a sua função rodou.","2026-11-03","\u002Fblog\u002Ffailed-to-send-a-request-to-the-edge-function\u002Fcard-800x500.png",{"path":642,"title":643,"description":644,"published":645,"category":555,"image":646,"draft":559},"\u002Fblog\u002Fsupabase-auth-redirects-to-localhost","Por que o Supabase redireciona links de e-mail para localhost:3000","Se o Supabase redireciona para localhost:3000, a Site URL nunca foi trocada ou o seu endereço falta em Redirect URLs. A solução, e o curinga a evitar.","2026-11-02","\u002Fblog\u002Fsupabase-auth-redirects-to-localhost\u002Fcard-800x500.png",{"path":648,"title":649,"description":650,"published":651,"category":555,"image":652,"draft":559},"\u002Fblog\u002Femail-link-is-invalid-or-has-expired","Supabase \"Email link is invalid or has expired\" num link novo","Num link enviado há um minuto, \"Email link is invalid or has expired\" no Supabase costuma significar que um filtro de e-mail o usou antes. Como resolver.","2026-11-01","\u002Fblog\u002Femail-link-is-invalid-or-has-expired\u002Fcard-800x500.png",{"path":654,"title":655,"description":656,"published":657,"category":555,"image":658,"draft":559},"\u002Fblog\u002Fcanceling-statement-due-to-statement-timeout","Supabase \"canceling statement due to statement timeout\", explicado","Supabase \"canceling statement due to statement timeout\": uma consulta passou do limite do seu papel, 3 segundos para um visitante. Veja antes a política RLS.","2026-10-31","\u002Fblog\u002Fcanceling-statement-due-to-statement-timeout\u002Fcard-800x500.png",{"path":660,"title":661,"description":662,"published":663,"category":664,"image":665,"draft":559},"\u002Fblog\u002Fdatabase-error-deleting-user","\"Database error deleting user\" no Supabase, e o que vai junto","\"Database error deleting user\" no Supabase: seu banco recusou a exclusão. O motivo está nos seus logs, e a correção decide o que mais é apagado.","2026-10-30","Backups","\u002Fblog\u002Fdatabase-error-deleting-user\u002Fcard-800x500.png",{"path":589,"title":5,"description":558,"published":590,"category":555,"image":577,"draft":559},{"path":668,"title":669,"description":670,"published":671,"category":555,"image":672,"draft":559},"\u002Fblog\u002Fsupabase-invalid-login","\"Invalid login credentials\" no Supabase: as três causas","O Supabase diz \"Invalid login credentials\" para senha errada, e-mail desconhecido e conta Google. Como saber qual é, e por que é vago.","2026-10-28","\u002Fblog\u002Fsupabase-invalid-login\u002Fcard-800x500.png",{"path":540,"title":674,"description":675,"published":676,"category":555,"image":677,"draft":559},"\"Edge Function returned a non-2xx status code\": o erro real","\"Edge Function returned a non-2xx status code\" quer dizer que sua função do Supabase respondeu com um erro. Veja onde ler o erro real.","2026-10-27","\u002Fblog\u002Fedge-function-returned-a-non-2xx-status-code\u002Fcard-800x500.png",{"path":679,"title":680,"description":681,"published":682,"category":555,"image":683,"draft":559},"\u002Fblog\u002Fcertificate-not-trusted","Certificado não confiável: três causas além do vencimento","Certificado não confiável mesmo com a data certa? Ele cita outro endereço, assinou a si mesmo ou falta um elo na cadeia. Veja como saber qual.","2026-10-26","\u002Fblog\u002Fcertificate-not-trusted\u002Fcard-800x500.png",{"path":685,"title":686,"description":687,"published":688,"category":555,"image":689,"draft":559},"\u002Fblog\u002Finvalid-input-syntax-for-type-uuid","Supabase \"invalid input syntax for type uuid\": leia as aspas","Supabase \"invalid input syntax for type uuid\": seu app enviou algo que não é um id. Leia as aspas, segure a consulta, mantenha a coluna.","2026-10-25","\u002Fblog\u002Finvalid-input-syntax-for-type-uuid\u002Fcard-800x500.png",{"path":691,"title":692,"description":693,"published":694,"category":555,"image":695,"draft":559},"\u002Fblog\u002Fduplicate-key-value-violates-unique-constraint","Supabase \"duplicate key value violates unique constraint\", explicado","Supabase \"duplicate key value violates unique constraint\": o valor já está em uso. Ache a coluna, escolha uma correção e tire o valor da tela.","2026-10-24","\u002Fblog\u002Fduplicate-key-value-violates-unique-constraint\u002Fcard-800x500.png",{"path":697,"title":698,"description":699,"published":700,"category":555,"image":701,"draft":559},"\u002Fblog\u002Fsupabase-relation-does-not-exist","Supabase \"relation does not exist\" e a tabela está lá","Supabase \"relation does not exist\" ou PGRST205: nenhuma tabela tem esse nome nesse schema. Quatro verificações, e o que expor um schema abre.","2026-10-23","\u002Fblog\u002Fsupabase-relation-does-not-exist\u002Fcard-800x500.png",{"path":703,"title":704,"description":705,"published":706,"category":555,"image":707,"draft":559},"\u002Fblog\u002Fsupabase-function-not-found","Supabase PGRST202: \"Could not find the function in the schema cache\"","Supabase \"Could not find the function in the schema cache\" mostra os nomes de argumentos que seu app enviou. Compare-os com a função.","2026-10-22","\u002Fblog\u002Fsupabase-function-not-found\u002Fcard-800x500.png",{"path":709,"title":710,"description":711,"published":712,"category":555,"image":713,"draft":559},"\u002Fblog\u002Fsupabase-schema-cache-column-error","Supabase PGRST204: \"Could not find the column in the schema cache\"","Supabase \"Could not find the column in the schema cache\" costuma ser um nome que não bate. Quatro verificações e a linha que recarrega.","2026-10-21","\u002Fblog\u002Fsupabase-schema-cache-column-error\u002Fcard-800x500.png",{"path":715,"title":716,"description":717,"published":718,"category":555,"image":719,"draft":559},"\u002Fblog\u002Fsupabase-email-rate-limit-exceeded","Supabase \"Email Rate Limit Exceeded\": dois limites, um só código","Supabase \"Email Rate Limit Exceeded\" limita cada email que seu projeto envia: 2 por hora no remetente embutido. O que conta e como aumentar.","2026-10-20","\u002Fblog\u002Fsupabase-email-rate-limit-exceeded\u002Fcard-800x500.png",{"path":721,"title":722,"description":723,"published":724,"category":555,"image":725,"draft":559},"\u002Fblog\u002Fsupabase-users-logged-out","Supabase \"Invalid Refresh Token: Refresh Token Not Found\": 3 causas","Supabase \"Invalid Refresh Token: Refresh Token Not Found\" desconecta seus usuários. Três causas, e como o log do Auth separa cada uma.","2026-10-19","\u002Fblog\u002Fsupabase-users-logged-out\u002Fcard-800x500.png",{"path":727,"title":728,"description":729,"published":730,"category":555,"image":731,"draft":559},"\u002Fblog\u002Fpermission-denied-for-schema-public","Supabase \"permission denied for schema public\": três causas","Supabase \"permission denied for schema public\" é um grant que falta no schema. O que o removeu, e a linha que o devolve.","2026-10-18","\u002Fblog\u002Fpermission-denied-for-schema-public\u002Fcard-800x500.png",{"path":733,"title":734,"description":735,"published":736,"category":555,"image":737,"draft":559},"\u002Fblog\u002Fsupabase-bucket-not-found","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.","2026-10-17","\u002Fblog\u002Fsupabase-bucket-not-found\u002Fcard-800x500.png",{"path":739,"title":740,"description":741,"published":742,"category":555,"image":743,"draft":559},"\u002Fblog\u002Fsupabase-invalid-jwt","Supabase invalid JWT: qual chave parou de funcionar?","Um erro Supabase invalid JWT é o PGRST301: um token assinado não passou na verificação. Num projeto novo isso é a sessão, não a chave de API.","2026-10-16","\u002Fblog\u002Fsupabase-invalid-jwt\u002Fcard-800x500.png",{"path":745,"title":746,"description":747,"published":748,"category":555,"image":749,"draft":559},"\u002Fblog\u002Fsupabase-single-row-error","Supabase \"multiple (or no) rows returned\" quase sempre é nenhuma","O Supabase devolve PGRST116, \"JSON object requested, multiple (or no) rows returned\", quando a consulta não achou nada. Linha escondida parece ausente.","2026-10-15","\u002Fblog\u002Fsupabase-single-row-error\u002Fcard-800x500.png",{"path":751,"title":752,"description":753,"published":754,"category":555,"image":755,"draft":559},"\u002Fblog\u002Fdatabase-error-saving-new-user","\"Database error saving new user\" no Supabase: onde olhar","\"Database error saving new user\" quer dizer que algo preso ao seu cadastro no Supabase falhou e tudo foi desfeito. O erro real está nos seus logs.","2026-10-14","\u002Fblog\u002Fdatabase-error-saving-new-user\u002Fcard-800x500.png",{"path":757,"title":758,"description":759,"published":760,"category":555,"image":761,"draft":559},"\u002Fblog\u002Fwhat-is-supabase","O que é Supabase? O banco de dados que seu builder escolheu","O que é Supabase: o banco de dados, o login e o armazenamento de arquivos do seu app. O Supabase cuida dos servidores; as regras das suas linhas são suas.","2026-10-13","\u002Fblog\u002Fwhat-is-supabase\u002Fcard-800x500.png",{"path":763,"title":764,"description":765,"published":766,"category":555,"image":767,"draft":559},"\u002Fblog\u002Faws-access-key-exposed-in-frontend","Uma chave AWS exposta no seu frontend não é como uma chave OpenAI","Uma chave de acesso da AWS exposta no seu frontend lê e grava no seu armazenamento. A solução costuma ser uma presigned URL, não uma chave nova.","2026-10-12","\u002Fblog\u002Faws-access-key-exposed-in-frontend\u002Fcard-800x500.png",{"path":769,"title":770,"description":771,"published":772,"category":555,"image":773,"draft":559},"\u002Fblog\u002Fstorage-upload-violates-row-level-security","\"Row-level security policy for table objects\" ao enviar","\"New row violates row-level security policy for table objects\" quer dizer que o seu envio não tem regra insert. Deixar o bucket público não cria nenhuma.","2026-10-11","\u002Fblog\u002Fstorage-upload-violates-row-level-security\u002Fcard-800x500.png",{"path":775,"title":776,"description":777,"published":778,"category":555,"image":779,"draft":559},"\u002Fblog\u002Fbase44-security-scan","Scan de segurança do Base44: o que só ele enxerga","O scan de segurança do Base44 verifica sete tipos de problema por dentro. Esta é a metade que ele lê e mais ninguém, e a metade que ele nunca olha.","2026-10-10","\u002Fblog\u002Fbase44-security-scan\u002Fcard-800x500.png",{"path":781,"title":782,"description":783,"published":784,"category":555,"image":785,"draft":559},"\u002Fblog\u002Fapi-key-leaked-what-to-do","Sua chave de API vazou. A ordem em que agir","Uma chave de API vazou e você não sabe o que fazer primeiro. Nem toda chave no seu frontend é um vazamento, e a ordem importa mais que a pressa.","2026-10-09","\u002Fblog\u002Fapi-key-leaked-what-to-do\u002Fcard-800x500.png",{"path":787,"title":788,"description":789,"published":790,"category":555,"image":791,"draft":559},"\u002Fblog\u002Finfinite-recursion-in-policy-for-relation","\"Infinite recursion detected in policy\" sem desligar o RLS","\"Infinite recursion detected in policy for relation\" significa que a sua policy perguntou à tabela que ela protege. Veja como quebrar o círculo.","2026-10-08","\u002Fblog\u002Finfinite-recursion-in-policy-for-relation\u002Fcard-800x500.png",{"path":793,"title":794,"description":795,"published":796,"category":555,"image":797,"draft":559},"\u002Fblog\u002Fno-api-key-found-in-request","\"No API key found in request\" no Supabase, e a correção errada","\"No API key found in request\" significa que sua requisição chegou ao Supabase sem chave. Quase toda resposta aponta para as regras do seu banco.","2026-10-07","\u002Fblog\u002Fno-api-key-found-in-request\u002Fcard-800x500.png",{"path":799,"title":800,"description":801,"published":802,"category":555,"image":803,"draft":559},"\u002Fblog\u002Fis-base44-safe","O Base44 é seguro? O que vimos em 5.442 apps Base44","O Base44 é seguro? Rodamos nove verificações em 5.442 apps Base44 no ar. Quase todas tiraram B por motivos que o dono nunca escolheu.","2026-10-06","\u002Fblog\u002Fis-base44-safe\u002Fcard-800x500.png",{"path":535,"title":805,"description":806,"published":807,"category":555,"image":808,"draft":559},"Esconder uma chave de API: mova para uma Edge Function do Supabase","Esconder uma chave de API é tirá-la do navegador, e uma Edge Function do Supabase é o menor lugar para guardá-la. Dois passos em volta pesam mais.","2026-10-05","\u002Fblog\u002Fmove-a-secret-to-an-edge-function\u002Fcard-800x500.png",{"path":810,"title":811,"description":812,"published":813,"category":555,"image":814,"draft":559},"\u002Fblog\u002Fenv-file-exposed-on-your-server","Seu arquivo .env está exposto? Os doze caminhos para testar","Seu arquivo .env está exposto no seu próprio servidor web? Doze endereços dizem isso em um minuto, e um acerto significa que tudo nele já é público.","2026-10-04","\u002Fblog\u002Fenv-file-exposed-on-your-server\u002Fcard-800x500.png",{"path":816,"title":817,"description":818,"published":819,"category":555,"image":820,"draft":559},"\u002Fblog\u002Fis-v0-safe","Segurança do v0: os 1.790 aplicativos v0 escaneados tiraram A","A segurança do v0 medida em 1.790 aplicativos v0 no ar: todos tiraram A. Só 17 citavam um banco de dados, e é sobretudo isso que esse A mede.","2026-10-03","\u002Fblog\u002Fis-v0-safe\u002Fcard-800x500.png",{"path":822,"title":823,"description":824,"published":825,"category":555,"image":826,"draft":559},"\u002Fblog\u002Fis-bolt-safe","O Bolt é seguro? O que vimos em 1.123 aplicativos Bolt","O Bolt é seguro? Passamos 1.123 aplicativos Bolt no ar por nove verificações. A hospedagem saiu limpa. Os achados: chaves e tabelas abertas nos aplicativos.","2026-10-02","\u002Fblog\u002Fis-bolt-safe\u002Fcard-800x500.png",{"path":828,"title":829,"description":830,"published":831,"category":555,"image":832,"draft":559},"\u002Fblog\u002Fis-supabase-down-or-your-app","O Supabase caiu ou o problema é o seu app? Como saber","O Supabase caiu ou o problema é o seu app? Um projeto pausado, um banco cheio e um erro seu parecem iguais. Quatro verificações separam os casos.","2026-10-01","\u002Fblog\u002Fis-supabase-down-or-your-app\u002Fcard-800x500.png",{"path":834,"title":835,"description":836,"published":837,"category":555,"image":838,"draft":559},"\u002Fblog\u002Fdoes-supabase-encrypt-my-data","O Supabase criptografa meus dados? Sim. Veja o que isso impede","O Supabase criptografa os dados? Sim: AES-256 em repouso, TLS em trânsito, SOC 2 e ISO 27001. O que cada um cobre, e o vazamento que nenhum deles impede.","2026-09-30","\u002Fblog\u002Fdoes-supabase-encrypt-my-data\u002Fcard-800x500.png",{"path":840,"title":841,"description":842,"published":843,"category":555,"image":844,"draft":559},"\u002Fblog\u002Fsupabase-free-plan-limits","O Supabase é gratuito? Sim, com estes limites e sem backups","Sim: o plano gratuito do Supabase vai até 500 MB de banco e 5 GB de egress, sem nenhum backup. O que para em cada limite e o que muda no Pro de $25.","2026-09-29","\u002Fblog\u002Fsupabase-free-plan-limits\u002Fcard-800x500.png",{"path":846,"title":847,"description":848,"published":849,"category":664,"image":850,"draft":559},"\u002Fblog\u002Ftest-your-supabase-backup","Teste o backup do seu Supabase antes do dia em que precisar","Como testar o backup do seu Supabase: restaurar num projeto de teste, contar as linhas, entrar e procurar a linha que falta num arquivo cortado.","2026-09-28","\u002Fblog\u002Ftest-your-supabase-backup\u002Fcard-800x500.png",{"path":852,"title":853,"description":854,"published":855,"category":664,"image":856,"draft":559},"\u002Fblog\u002Fsupabase-backup-github-action","Backup grátis do Supabase com uma GitHub Action, e a pegadinha","Um backup do Supabase com GitHub Action é grátis e serve para muitos apps. O workflow, a connection string certa e o egress de cada execução.","2026-09-27","\u002Fblog\u002Fsupabase-backup-github-action\u002Fcard-800x500.png",{"path":858,"title":859,"description":860,"published":861,"category":664,"image":862,"draft":559},"\u002Fblog\u002Fdownload-your-supabase-backup","Por que você não consegue baixar seu backup do Supabase","Você não consegue baixar seu backup do Supabase num projeto atual, porque a cópia diária é um snapshot físico. Como conferir e como ter uma cópia só sua.","2026-09-26","\u002Fblog\u002Fdownload-your-supabase-backup\u002Fcard-800x500.png",{"path":864,"title":865,"description":866,"published":861,"category":555,"image":867,"draft":559},"\u002Fblog\u002Fsupabase-permission-denied-for-table","Supabase \"permission denied for table\": o grant que falta","A partir de 30 de outubro, uma tabela nova no Supabase responde \"permission denied for table\" até receber um grant. O e-mail mostra só metade.","\u002Fblog\u002Fsupabase-permission-denied-for-table\u002Fcard-800x500.png",{"path":869,"title":870,"description":871,"published":872,"category":555,"image":873,"draft":559},"\u002Fblog\u002Fopen-api-endpoint-exposed","Um endpoint de API aberto é um problema? Leia o JSON","Seu scan apontou um endpoint de API aberto. Se isso importa depende do que voltou, e a maioria dos que encontramos era da própria plataforma.","2026-09-25","\u002Fblog\u002Fopen-api-endpoint-exposed\u002Fcard-800x500.png",{"path":875,"title":876,"description":877,"published":878,"category":555,"image":879,"draft":559},"\u002Fblog\u002Flovable-security-scan","Escaneamento de segurança do Lovable: o que ele não prova","Escaneamento de segurança do Lovable: o que o Quick scan e o Deep scan checam, quando cada um roda, e a única coisa que nenhum escaneamento por dentro prova.","2026-09-24","\u002Fblog\u002Flovable-security-scan\u002Fcard-800x500.png",{"path":881,"title":882,"description":883,"published":884,"category":664,"image":885,"draft":559},"\u002Fblog\u002Fsupabase-backup-auth-users","Por que seu dump do Supabase não tem usuários dentro","Rode supabase db dump sozinho e você recebe a forma do seu banco e nenhuma das linhas dele, sem o schema auth onde seus usuários moram.","2026-09-23","\u002Fblog\u002Fsupabase-backup-auth-users\u002Fcard-800x500.png",{"path":887,"title":888,"description":889,"published":890,"category":555,"image":891,"draft":559},"\u002Fblog\u002Fdomain-and-certificate-expiry","Domínio expirado, site fora do ar: o que acontece agora","Seu domínio expirou e seu site está fora do ar. O relógio que começa a correr, por que o certificado é o problema menor, e como conferir os dois.","2026-09-22","\u002Fblog\u002Fdomain-and-certificate-expiry\u002Fcard-800x500.png",{"path":893,"title":894,"description":895,"published":896,"category":555,"image":897,"draft":559},"\u002Fblog\u002Fis-lovable-safe","O Lovable é seguro? O que vimos em 18.554 aplicativos Lovable","O Lovable é seguro? Passamos 18.554 aplicativos Lovable no ar por nove verificações. A plataforma foi a mais limpa de cinco. Os achados estavam nos aplicativos.","2026-09-21","\u002Fblog\u002Fis-lovable-safe\u002Fcard-800x500.png",{"path":899,"title":900,"description":901,"published":902,"category":555,"image":903,"draft":559},"\u002Fblog\u002Fvibe-coded-app-security-checklist","O checklist de segurança do vibe coding, em nove verificações","Um checklist de segurança do vibe coding com nove itens, cada um verificável no seu app de fora por qualquer pessoa, e cada um com um teste de uma linha.","2026-09-20","\u002Fblog\u002Fvibe-coded-app-security-checklist\u002Fcard-800x500.png",{"path":424,"title":905,"description":906,"published":907,"category":555,"image":908,"draft":559},"Uma chave secreta da Stripe no frontend pode mover dinheiro","Uma chave secreta da Stripe exposta no seu frontend pode reembolsar, cobrar e ler cada ficha de cliente que você tem. Sua chave pk_live_ é que deve estar aí.","2026-09-19","\u002Fblog\u002Fstripe-secret-key-in-frontend\u002Fcard-800x500.png",{"path":910,"title":911,"description":912,"published":913,"category":555,"image":914,"draft":559},"\u002Fblog\u002Fvite-and-next-public-env-vars","Variáveis de ambiente do Vite expostas: o prefixo diz publique isto","Variáveis de ambiente do Vite expostas no seu app: fizeram o que o prefixo pediu. VITE_ e NEXT_PUBLIC_ significam publique isto, e a IA nunca soube o custo.","2026-09-18","\u002Fblog\u002Fvite-and-next-public-env-vars\u002Fcard-800x500.png",{"path":916,"title":917,"description":918,"published":919,"category":555,"image":920,"draft":559},"\u002Fblog\u002Fbase44-source-maps","Segurança no Base44: o que um scan aponta e o que é seu para corrigir","Segurança no Base44 em 5.438 apps escaneados: três achados em quase todos são da plataforma, o source map é o selo do Base44, e a sua parte é uma lista curta.","2026-09-17","\u002Fblog\u002Fbase44-source-maps\u002Fcard-800x500.png",{"path":922,"title":923,"description":924,"published":925,"category":555,"image":926,"draft":559},"\u002Fblog\u002Fis-cursor-ai-safe","O Cursor AI é seguro? O editor, o código e o app que você publicou","O Cursor AI é seguro? Três perguntas em uma busca: o que o Cursor guarda, o que o código que ele escreve erra, e se o app que você publicou está aberto.","2026-09-16","\u002Fblog\u002Fis-cursor-ai-safe\u002Fcard-800x500.png",{"path":928,"title":929,"description":930,"published":931,"category":555,"image":932,"draft":559},"\u002Fblog\u002Fis-replit-safe","O Replit é seguro? O que vimos em 3.042 aplicativos Replit no ar","O Replit é seguro? Passamos 3.042 aplicativos Replit no ar por nove verificações externas. Os achados estavam no aplicativo publicado, não no host.","2026-09-15","\u002Fblog\u002Fis-replit-safe\u002Fcard-800x500.png",{"path":934,"title":935,"description":936,"published":937,"category":664,"image":938,"draft":559},"\u002Fblog\u002Fsupabase-storage-backup","Supabase storage backup: sua cópia do banco não tem nenhum arquivo","Um Supabase storage backup é uma tarefa à parte. Os backups do banco guardam a lista dos seus arquivos e nenhum deles, então cada upload fica quebrado.","2026-09-14","\u002Fblog\u002Fsupabase-storage-backup\u002Fcard-800x500.png",{"path":940,"title":941,"description":942,"published":943,"category":664,"image":944,"draft":559},"\u002Fblog\u002Fsupabase-point-in-time-recovery","Point-in-time recovery do Supabase: preço e limites","O point-in-time recovery do Supabase rebobina seu banco para qualquer segundo da última semana. Custa $100 por mês além do Pro e cobre só o banco.","2026-09-13","\u002Fblog\u002Fsupabase-point-in-time-recovery\u002Fcard-800x500.png",{"path":946,"title":947,"description":948,"published":949,"category":664,"image":950,"draft":559},"\u002Fblog\u002Fsupabase-project-paused-recover","Projeto Supabase pausado? Seus dados continuam lá","O Supabase pausou seu projeto depois de uma semana sem atividade. Nada foi apagado, Restaurar fica ao lado do nome e você tem um ano antes disso mudar.","2026-09-12","\u002Fblog\u002Fsupabase-project-paused-recover\u002Fcard-800x500.png",{"path":952,"title":953,"description":954,"published":955,"category":555,"image":956,"draft":559},"\u002Fblog\u002Fsafest-ai-app-builder","Qual builder de apps com IA é mais seguro? 30.998 apps testados","Qual builder de apps com IA é mais seguro? Escaneamos 30.998 apps no ar do Lovable, Base44, Replit, v0 e Bolt. O builder não é o que decide sua nota.","2026-09-11","\u002Fblog\u002Fsafest-ai-app-builder\u002Fcard-800x500.png",{"path":958,"title":959,"description":960,"published":961,"category":555,"image":962,"draft":559},"\u002Fblog\u002Fenable-rls-on-every-supabase-table","Ative Row Level Security em toda tabela do Supabase e comprove","Ativar Row Level Security no Supabase sem política fecha a tabela por completo. Uma política sem a chave ligada não faz nada. Aqui está o SQL, e o teste.","2026-09-10","\u002Fblog\u002Fenable-rls-on-every-supabase-table\u002Fcard-800x500.png",{"path":964,"title":965,"description":966,"published":967,"category":555,"image":968,"draft":559},"\u002Fblog\u002Fsupabase-rls-disabled-in-public","Supabase \"RLS disabled in public\": o que o aviso não vê","O Supabase marca \"RLS disabled in public\" como erro. Ele não diz nada sobre a política de leitura que deixa a sua tabela igualmente aberta.","2026-09-09","\u002Fblog\u002Fsupabase-rls-disabled-in-public\u002Fcard-800x500.png",{"path":970,"title":971,"description":972,"published":973,"category":555,"image":974,"draft":559},"\u002Fblog\u002Frotate-supabase-service-role-key","Como rotacionar uma chave service_role do Supabase vazada","O Supabase manda fechar o vazamento primeiro. Outros guias mandam rotacionar agora. O que vale depende de para onde a sua chave service_role vazou.","2026-09-08","\u002Fblog\u002Frotate-supabase-service-role-key\u002Fcard-800x500.png",{"path":976,"title":977,"description":978,"published":979,"category":555,"image":980,"draft":559},"\u002Fblog\u002Fvibe-coding-security-scanners-compared","Scanners de segurança para vibe coding comparados, o nosso incluído","O melhor scanner de segurança para vibe coding depende de três perguntas que nenhuma lista de recursos responde. Oito ferramentas comparadas, com preços.","2026-09-07","\u002Fblog\u002Fvibe-coding-security-scanners-compared\u002Fcard-800x500.png",{"path":982,"title":983,"description":984,"published":985,"category":555,"image":986,"draft":559},"\u002Fblog\u002Fsupabase-security-checker","Supabase security checker: faça você mesmo as cinco verificações","Um Supabase security checker lê o seu app publicado em vez das configurações do projeto. Aqui estão as cinco verificações e como fazer cada uma sozinho.","2026-09-06","\u002Fblog\u002Fsupabase-security-checker\u002Fcard-800x500.png",{"path":988,"title":989,"description":990,"published":991,"category":555,"image":992,"draft":559},"\u002Fblog\u002Fvibe-coding-security-scanner","Scanner de segurança para vibe coding: o que ele não vê","Um scanner de segurança para vibe coding lê seu app no ar, de fora. O que isso cobre, as quatro coisas que ele não consegue ver e como ler o resultado.","2026-09-05","\u002Fblog\u002Fvibe-coding-security-scanner\u002Fcard-800x500.png",{"path":994,"title":995,"description":996,"published":997,"category":555,"image":998,"draft":559},"\u002Fblog\u002Fmissing-security-headers","Cabeçalhos de segurança faltando: quando isso importa","Cabeçalhos de segurança faltando é o achado que nosso scanner mais imprime. Contra o que eles protegem e quando são a linha menos urgente do relatório.","2026-09-04","\u002Fblog\u002Fmissing-security-headers\u002Fcard-800x500.png",{"path":1000,"title":1001,"description":1002,"published":1003,"category":555,"image":1004,"draft":559},"\u002Fblog\u002Fopenai-api-key-exposed-in-frontend","Sua chave de API da OpenAI está exposta no frontend. Troque-a.","Uma chave de API da OpenAI exposta no frontend não pode ser presa a um domínio. Troque-a hoje, mova a chamada para o seu servidor e limite o gasto.","2026-09-03","\u002Fblog\u002Fopenai-api-key-exposed-in-frontend\u002Fcard-800x500.png",{"path":1006,"title":1007,"description":1008,"published":1009,"category":555,"image":1010,"draft":559},"\u002Fblog\u002Fwhat-secrets-leak-from-vibe-coded-apps","Chave de API exposta no frontend: o que 30.998 apps entregam","Uma chave de API exposta no seu frontend costuma ser uma chave do Google Maps. Escaneamos 30.998 apps vibe-coded e contamos quais segredos vazam de verdade.","2026-09-02","\u002Fblog\u002Fwhat-secrets-leak-from-vibe-coded-apps\u002Fcard-800x500.png",{"path":1012,"title":1013,"description":1014,"published":1015,"category":664,"image":1016,"draft":559},"\u002Fblog\u002Fsupabase-backup-tools-compared","Ferramentas de backup do Supabase comparadas, a nossa incluída","Quatro tipos de ferramenta de backup do Supabase, o que cada uma copia de verdade e quando uma GitHub Action gratuita ganha de pagar, nós inclusive.","2026-09-01","\u002Fblog\u002Fsupabase-backup-tools-compared\u002Fcard-800x500.png",{"path":1018,"title":1019,"description":1020,"published":1021,"category":555,"image":1022,"draft":559},"\u002Fblog\u002Freplit-secrets-explained","Como usar secrets no Replit, e o que ainda assim é publicado","Como usar secrets no Replit: adicionar um, ler de volta e resolver as duas causas do undefined. E as chaves que a ferramenta Secrets não consegue proteger.","2026-08-31","\u002Fblog\u002Freplit-secrets-explained\u002Fcard-800x500.png",{"path":1024,"title":1025,"description":1026,"published":1027,"category":664,"image":1028,"draft":559},"\u002Fblog\u002Fback-up-supabase-free-tier","Backups do plano gratuito do Supabase: como fazer sem terminal","No plano gratuito do Supabase não existe backup nenhum: a cópia é você que faz. Como fazer uma pelo painel e o que a rota do CSV deixa para trás.","2026-08-30","\u002Fblog\u002Fback-up-supabase-free-tier\u002Fcard-800x500.png",{"path":1030,"title":1031,"description":1032,"published":1033,"category":555,"image":1034,"draft":559},"\u002Fblog\u002Fis-supabase-secure","O Supabase é seguro? É. Seu projeto é outra pergunta","O Supabase é seguro? A plataforma é auditada, criptografada e passa por pentests. A documentação deles diz onde isso termina e começam suas configurações.","2026-08-29","\u002Fblog\u002Fis-supabase-secure\u002Fcard-800x500.png",{"path":1036,"title":1037,"description":1038,"published":1039,"category":555,"image":1040,"draft":559},"\u002Fblog\u002Fwhere-to-find-supabase-api-keys","Onde ficam as chaves de API do Supabase: anon, service_role e URL","A URL do projeto, a chave anon e a chave service_role ficam todas numa página do painel. Aqui está onde ela fica, e qual dos quatro valores vai no seu app.","2026-08-28","\u002Fblog\u002Fwhere-to-find-supabase-api-keys\u002Fcard-800x500.png",{"path":1042,"title":1043,"description":1044,"published":1045,"category":555,"image":1046,"draft":559},"\u002Fblog\u002Fnew-row-violates-row-level-security-policy","New row violates row-level security policy no Supabase. E agora?","\"New row violates row-level security policy\" significa que o Supabase recusou uma escrita. O conserto rápido reabre a tabela para qualquer um.","2026-08-27","\u002Fblog\u002Fnew-row-violates-row-level-security-policy\u002Fcard-800x500.png",{"path":1048,"title":1049,"description":1050,"published":1051,"category":555,"image":1052,"draft":559},"\u002Fblog\u002Fcors-wildcard-security-risk","Um curinga CORS é um risco de segurança? Quase nunca.","Um curinga CORS é um risco de segurança? Em geral é o padrão do seu builder e não entrega nada que o seu servidor já não desse a qualquer um que pedisse.","2026-08-26","\u002Fblog\u002Fcors-wildcard-security-risk\u002Fcard-800x500.png",{"path":1054,"title":1055,"description":1056,"published":1057,"category":664,"image":1058,"draft":559},"\u002Fblog\u002Fsupabase-branching-is-not-a-backup","Branching do Supabase não é backup. Ele só anda para a frente.","Por que o branching do Supabase não é backup: um branch começa sem os seus dados e um merge só leva o esquema. Para que ele serve e o que usar no lugar.","2026-08-25","\u002Fblog\u002Fsupabase-branching-is-not-a-backup\u002Fcard-800x500.png",{"path":1060,"title":1061,"description":1062,"published":1063,"category":664,"image":1064,"draft":559},"\u002Fblog\u002Fhow-to-restore-a-supabase-backup","Como restaurar um backup do Supabase, e o que quebra depois","Como restaurar um backup do Supabase pelo painel ou por um arquivo de dump, o que a restauração substitui e por que o seu app pode continuar quebrado no fim.","2026-08-24","\u002Fblog\u002Fhow-to-restore-a-supabase-backup\u002Fcard-800x500.png",{"path":1066,"title":1067,"description":1068,"published":1069,"category":555,"image":1070,"draft":559},"\u002Fblog\u002Fsupabase-storage-bucket-public","Seu bucket do Supabase Storage é público. Isso é um problema?","Um bucket público do Supabase Storage significa que quem tem a URL de um arquivo pode abri-lo. Não significa que alguém possa listar o que está lá dentro.","2026-08-23","\u002Fblog\u002Fsupabase-storage-bucket-public\u002Fcard-800x500.png",{"path":1072,"title":1073,"description":1074,"published":1075,"category":555,"image":1076,"draft":559},"\u002Fblog\u002Fgoogle-api-key-exposed-in-frontend","Uma chave de API do Google no frontend é um problema?","Uma chave de API do Google exposta no seu frontend é a chave que nosso scanner mais encontra, e quase sempre está tudo bem. Um ajuste gratuito decide isso.","2026-08-22","\u002Fblog\u002Fgoogle-api-key-exposed-in-frontend\u002Fcard-800x500.png",{"path":1078,"title":1079,"description":1080,"published":1081,"category":664,"image":1082,"draft":559},"\u002Fblog\u002Fai-agent-deleted-my-database","Um agente de IA apagou meus dados do Supabase. O que eu recupero?","Um agente de IA apagou dados do seu banco. O que você recupera já estava decidido antes de ele rodar, e os próximos minutos decidem o resto.","2026-08-21","\u002Fblog\u002Fai-agent-deleted-my-database\u002Fcard-800x500.png",{"path":1084,"title":1085,"description":1086,"published":1087,"category":555,"image":1088,"draft":559},"\u002Fblog\u002Fcan-anyone-read-your-supabase-database","Qualquer um consegue ler seu banco Supabase? Testamos 3.680 apps","Qualquer um consegue ler seu banco de dados Supabase sem fazer login? Escaneamos 30.998 apps no ar criadas com builders de IA e medimos quantas dizem que sim.","2026-08-18","\u002Fblog\u002Fcan-anyone-read-your-supabase-database\u002Fcard-800x500.png",{"path":1090,"title":1091,"description":1092,"published":1087,"category":555,"image":1093,"draft":559},"\u002Fblog\u002Fsource-maps-exposed-in-production","Source maps expostos: seu app está publicando o código original","Um source map exposto deixa qualquer um ler o código original do seu app, comentários incluídos. A verificação de 30 segundos e o que importa de verdade.","\u002Fblog\u002Fsource-maps-exposed-in-production\u002Fcard-800x500.png",{"path":1095,"title":1096,"description":1097,"published":1098,"category":555,"image":1099,"draft":559},"\u002Fblog\u002Fsupabase-new-api-keys","Supabase publishable key: é a mesma coisa que a anon key?","O Supabase trocou anon e service_role por chaves publicáveis e secretas. Qual delas pertence ao seu app e qual nunca deve estar lá?","2026-08-12","\u002Fblog\u002Fsupabase-new-api-keys\u002Fcard-800x500.png",{"path":1101,"title":1102,"description":1103,"published":1104,"category":664,"image":1105,"draft":559},"\u002Fblog\u002Fdoes-supabase-back-up-my-database","O Supabase faz backup do meu banco de dados? Depende do plano.","O Supabase faz backup do seu banco de dados? Todo dia nos planos pagos, e nada no gratuito. Como descobrir o que você tem e o que essa cópia não cobre.","2026-08-11","\u002Fblog\u002Fdoes-supabase-back-up-my-database\u002Fcard-800x500.png",{"path":1107,"title":1108,"description":1109,"published":1110,"category":555,"image":1111,"draft":559},"\u002Fblog\u002Fsupabase-rls-on-but-table-still-public","O Supabase Row Level Security está ligado. A tabela continua pública.","Ligar o Supabase Row Level Security não protege uma tabela. Quem protege são as políticas, e a que consertou o seu app pode deixar qualquer um entrar.","2026-08-10","\u002Fblog\u002Fsupabase-rls-on-but-table-still-public\u002Fcard-800x500.png",{"path":1113,"title":1114,"description":1115,"published":1110,"category":664,"image":1116,"draft":559},"\u002Fblog\u002Fversion-history-is-not-a-backup","O histórico de versões não é backup. Ele não desfaz um delete.","Lovable e Bolt guardam um histórico de versões do seu código. Seu banco de dados é um serviço à parte, e voltar atrás não traz os seus dados de volta.","\u002Fblog\u002Fversion-history-is-not-a-backup\u002Fcard-800x500.png",{"path":1118,"title":1119,"description":1120,"published":1121,"category":664,"image":1122,"draft":559},"\u002Fblog\u002Fthree-ways-to-back-up-a-supabase-database","Três formas de fazer backup de um banco Supabase, e o que falta","O painel, o pg_dump e um serviço gerenciado. O que cada um salva de verdade, o que deixa de fora sem avisar e qual sobrevive a perder a conta.","2026-08-09","\u002Fblog\u002Fthree-ways-to-back-up-a-supabase-database\u002Fcard-800x500.png",{"path":1124,"title":1125,"description":1126,"published":1121,"category":555,"image":1127,"draft":559},"\u002Fblog\u002Fwhich-api-keys-are-safe-in-your-frontend","Quais chaves de API são seguras no navegador e quais não são","Sua chave anon do Supabase foi feita para ser pública. Sua chave service_role não, e ela ignora todas as regras que você criar. Como diferenciar as duas.","\u002Fblog\u002Fwhich-api-keys-are-safe-in-your-frontend\u002Fcard-800x500.png",1794213649050]