[{"data":1,"prerenderedAt":922},["ShallowReactive",2],{"blog-es-infinite-recursion-in-policy-for-relation":3,"blog-index-es":568},{"id":4,"title":5,"body":6,"category":525,"cover":526,"coverAlt":527,"description":528,"draft":529,"extension":530,"faq":531,"image":546,"keywords":547,"meta":556,"navigation":557,"ogTitle":558,"path":559,"published":560,"seo":561,"stem":562,"tldr":563,"updated":560,"__hash__":567},"blog_es\u002Fblog\u002Finfinite-recursion-in-policy-for-relation.md","\"Infinite recursion detected in policy\" sin desactivar RLS",{"type":7,"value":8,"toc":512},"minimark",[9,13,24,32,37,40,43,60,63,67,70,78,94,100,103,107,113,119,122,126,134,140,143,146,199,210,218,222,228,231,236,239,242,294,297,300,307,315,319,322,332,335,353,369,373,376,393,396,400,406,415,433,444,461,465,504],[10,11,12],"p",{},"Activaste Row Level Security, escribiste una regla para que los admins vean\ntodos los perfiles y los demás vean solo el suyo, y ahora no carga ni una sola\npantalla de tu app. Cada lectura vuelve con la misma línea:",[14,15,20],"pre",{"className":16,"code":18,"language":19},[17],"language-text","infinite recursion detected in policy for relation \"profiles\"\n","text",[21,22,18],"code",{"__ignoreMap":23},"",[10,25,26,27,31],{},"La mayoría de las respuestas que vas a encontrar dicen lo mismo, y es lo\nequivocado: ",[28,29,30],"strong",{},"desactiva Row Level Security en esa tabla hasta que lo tengas\nresuelto."," El error para, efectivamente. Lo que lo hace parar es que la tabla\nahora la puede leer cualquiera que visite tu sitio.",[33,34,36],"h2",{"id":35},"qué-significa-infinite-recursion-detected-in-policy-for-relation","Qué significa \"infinite recursion detected in policy for relation\"",[10,38,39],{},"Una regla sobre una tabla tuvo que leer esa misma tabla antes de poder\nresponder.",[10,41,42],{},"Imagina un portero que comprueba a cada visitante contra una lista de\ninvitados, y la lista está guardada dentro de la sala que vigila. Para leer la\nlista tiene que entrar. Para entrar tiene que comprobar la lista. No hay primer\npaso, así que se queda en el pasillo y nadie llega a ninguna parte.",[10,44,45,46,49,50,52,53,55,56,59],{},"Eso es lo que le pasó a tu base de datos. Le pidieron unas filas de ",[21,47,48],{},"profiles",",\nfue a la regla de ",[21,51,48],{}," para averiguar cuáles tenías permiso de ver, y la\nregla le dijo que mirara en ",[21,54,48],{},". Postgres nota que está dando vueltas, se\nrinde y lanza el error con el código ",[21,57,58],{},"42P17"," pegado. Supabase se lo pasa a tu\napp sin tocarlo, y por eso se lee como maquinaria.",[10,61,62],{},"La consulta falla para todos a quienes se aplica la regla, así que esto suele\nllegar como una app entera que se queda en blanco de golpe, y no como una sola\npantalla rota.",[33,64,66],{"id":65},"el-círculo-dibujado","El círculo, dibujado",[10,68,69],{},"La regla que lo provoca es la que casi todo el mundo escribe primero, porque\ndice exactamente lo que quieres decir:",[14,71,76],{"className":72,"code":74,"language":75,"meta":23},[73],"language-sql","-- Rechazar: esto lee profiles para decidir quién puede leer profiles.\ncreate policy \"admins read every profile\" on profiles for select\nto authenticated\nusing (\n  exists (\n    select 1 from profiles\n    where id = (select auth.uid()) and role = 'admin'\n  )\n);\n","sql",[21,77,74],{"__ignoreMap":23},[10,79,80,81,84,85,87,88,90,91,93],{},"El ",[21,82,83],{},"exists (select 1 from profiles …)"," es todo el problema. Leer ",[21,86,48],{},"\nsignifica aplicar la regla de ",[21,89,48],{},", que vuelve a ejecutar\n",[21,92,83],{},".",[95,96],"diagram",{"alt":97,"caption":98,"src":99},"La lectura de un visitante llega a una policy, dibujada como un candado. La policy manda una pregunta a la tabla profiles y la tabla la devuelve a la policy, y ese par de flechas forma un bucle cerrado marcado 42P17.","La petición no tiene nada de raro. El bucle está entre la regla y la tabla que la regla protege.","\u002Fblog\u002Finfinite-recursion-in-policy-for-relation\u002Fthe-circle-1600x660.png",[10,101,102],{},"La propia guía de Row Level Security de Supabase nombra la versión de dos\ntablas: policies sobre dos tablas que leen cada una la otra nunca se resuelven,\ny la consulta falla para cada rol al que las policies se aplican. Una tabla\nleyéndose a sí misma es la misma forma sin el rodeo.",[33,104,106],{"id":105},"por-qué-pasa-siempre-en-la-tabla-profiles","¿Por qué pasa siempre en la tabla profiles?",[10,108,109,110,112],{},"Porque en ",[21,111,48],{}," está quién es esta persona.",[10,114,115,116,118],{},"Cualquier regla que trate a un tipo de persona distinto de otro tiene que\naveriguar de qué tipo es quien llama, y ese dato vive en una columna de alguna\ntabla. Se llame como se llame esa tabla, la regla que la protege acaba\nleyéndola. En una app hecha con Lovable, Bolt, v0 o Replit suele ser ",[21,117,48],{},",\nporque ahí es donde acaba la fila de cada persona con sesión iniciada. Una regla\nsobre equipos, organizaciones o documentos compartidos produce el mismo círculo\nen la tabla que guarde la pertenencia.",[10,120,121],{},"Así que la recursión sale de escribir la regla obvia sobre la única tabla que\ntiene que responder una pregunta sobre sí misma.",[33,123,125],{"id":124},"el-arreglo-un-ayudante-que-lee-la-tabla-fuera-de-las-reglas","El arreglo: un ayudante que lee la tabla fuera de las reglas",[10,127,128,129,131,132,93],{},"Mueve la pregunta a una función pequeña que corre como el dueño de la base de\ndatos, para que leer ",[21,130,48],{}," y responderla no pase por la regla de\n",[21,133,48],{},[14,135,138],{"className":136,"code":137,"language":75,"meta":23},[73],"create schema if not exists private;\n\ncreate function private.is_admin()\nreturns boolean\nlanguage sql\nsecurity definer      -- corre como el rol que la creó\nset search_path = ''  -- así cada nombre de dentro hay que escribirlo completo\nstable\nas $$\n  select exists (\n    select 1 from public.profiles\n    where id = (select auth.uid()) and role = 'admin'\n  );\n$$;\n\nrevoke execute on function private.is_admin() from public;\ngrant usage on schema private to authenticated;\ngrant execute on function private.is_admin() to authenticated;\n\n-- La regla ahora pregunta a la función en vez de a la tabla.\ncreate policy \"admins read every profile\" on profiles for select\nto authenticated\nusing ( (select private.is_admin()) );\n",[21,139,137],{"__ignoreMap":23},[10,141,142],{},"El portero tiene ahora la lista de invitados en una mesa del pasillo. La lee sin\nabrir la puerta, y la puerta sigue cerrada para todos los que no están en ella.",[10,144,145],{},"Cuatro líneas de ahí hacen un trabajo concreto, y tres de ellas son la\ndiferencia entre un arreglo y un agujero nuevo:",[147,148,149,162,178,187],"ul",{},[150,151,152,157,158,161],"li",{},[28,153,154],{},[21,155,156],{},"security definer"," es lo que rompe el círculo. La guía de Supabase lo\ndefine como una función que corre bajo el mismo rol que creó la función, y en\nSupabase ese rol es ",[21,159,160],{},"postgres",", que puede leer por encima de las reglas.",[150,163,164,169,170,173,174,177],{},[28,165,166],{},[21,167,168],{},"set search_path = ''"," le corresponde a todas y cada una. Supabase\nrecomienda ponerlo en toda función security definer y escribir los nombres de\ntabla completos, porque sin un ",[21,171,172],{},"search_path"," fijado quien llama puede apuntar\nun nombre sin cualificar a un objeto suyo y hacerlo correr con los privilegios\ndel dueño de la función. Por eso la función escribe ",[21,175,176],{},"public.profiles"," entero.",[150,179,180,186],{},[28,181,182,183],{},"El esquema ",[21,184,185],{},"private"," importa por la misma razón. La guía de Supabase avisa\nde que una función security definer puesta en un esquema expuesto se puede\nllamar por la Data API con los privilegios de quien la creó, y te dice que\nnunca crees una en un esquema listado bajo Exposed schemas en los ajustes de\ntu API.",[150,188,189,194,195,198],{},[28,190,191],{},[21,192,193],{},"(select private.is_admin())",", envuelto en un select propio. Eso deja que\nPostgres calcule la respuesta una vez para toda la sentencia en lugar de una\nvez por fila, que es lo que Supabase documenta como la razón de envolver\n",[21,196,197],{},"auth.uid()"," igual.",[10,200,201,202,205,206,209],{},"Las líneas ",[21,203,204],{},"revoke"," y ",[21,207,208],{},"grant"," dejan la función invocable por los usuarios con\nsesión iniciada y por nadie más.",[10,211,212,213,93],{},"Esa policy cubre la lectura. Si guardar empieza a fallar en cuanto tus pantallas\nvuelven a llenarse, te has encontrado con\n",[214,215,217],"a",{"href":216},"\u002Fblog\u002Fnew-row-violates-row-level-security-policy","otro mensaje con otra causa",[33,219,221],{"id":220},"el-arreglo-que-no-arregla","El arreglo que no arregla",[10,223,224,225,227],{},"Desactivar Row Level Security en ",[21,226,48],{}," también hace que el error pare, en\nun clic, y entrega cada fila de esa tabla a cualquiera que tenga la clave que tu\napp envía al navegador.",[10,229,230],{},"Esa clave está pensada para ser pública. Está en el código de tu sitio, y\ncualquiera puede sacarla en unos segundos. Lo que hasta ahora le impedía\ndevolver tu tabla de usuarios entera eran las reglas que acabas de desactivar.",[95,232],{"alt":233,"caption":234,"src":235},"Dos caminos hacia la misma tabla. En el primero el candado sigue en pie y una función de ayuda a su lado devuelve dos filas de seis. En el segundo el sitio del candado es solo un contorno discontinuo vacío y las seis filas vuelven en rojo.","Las dos hacen que el error pare. Solo una de ellas sigue respondiendo la pregunta que se le hizo a la regla.","\u002Fblog\u002Finfinite-recursion-in-policy-for-relation\u002Ftwo-ways-out-1600x700.png",[10,237,238],{},"Tu app tiene el mismo aspecto después en los dos casos, y esa es la parte que\nhace que esto se quede. Nada en tus pantallas dice cuál de las dos elegiste, y\nel error ha desaparecido en ambas.",[10,240,241],{},"Lo que sí lo dice es tu base de datos, preguntada desde fuera. Hemos escaneado\n31.056 apps en producción hechas con Lovable, Bolt, v0, Replit y las demás, y\nvimos un proyecto de Supabase detrás de 8.435 de ellas. Esto es lo que encontró\nla comprobación de Row Level Security:",[243,244,245,258],"table",{},[246,247,248],"thead",{},[249,250,251,255],"tr",{},[252,253,254],"th",{},"Lo que preguntamos",[252,256,257],{},"Apps",[259,260,261,270,278,286],"tbody",{},[249,262,263,267],{},[264,265,266],"td",{},"Tenían un proyecto de Supabase visible",[264,268,269],{},"8.435",[249,271,272,275],{},[264,273,274],{},"Se les pudo preguntar siquiera",[264,276,277],{},"3.680",[249,279,280,283],{},[264,281,282],{},"Devolvieron filas a una petición sin sesión",[264,284,285],{},"2.096",[249,287,288,291],{},[264,289,290],{},"De esas, desde una tabla de personas, pedidos o mensajes",[264,292,293],{},"394",[10,295,296],{},"La fila de en medio es la honesta. En 4.755 de esas apps la comprobación no\nobtuvo respuesta, y lo decimos en vez de darlas por limpias. Y algunas de las\n2.096 están pensadas para ser legibles, porque una tabla pública es algo que se\npuede querer de verdad; desde fuera, un menú y una lista de socios se parecen.",[10,298,299],{},"No podemos decirte cuántas de esas tablas las abrió alguien quitándose de encima\neste error exacto. Sí podemos decirte que una tabla abierta es el final normal\ndel consejo que sale arriba del todo en los resultados de búsqueda.",[10,301,302,303,93],{},"Si prefieres no tener que pensarlo, nuestro escaneo gratuito lee tu sitio en\nproducción y te dice cuáles de tus tablas le responden a un desconocido. Tarda\nunos 20 segundos y no pide cuenta: ",[214,304,306],{"href":305},"\u002Fsecurity-scanner","escanea tu app",[10,308,309,310,314],{},"Pasa una cosa más cuando lo desactivas. El propio advisor de Supabase empieza a\nreportar la tabla, que es el aviso\n",[214,311,313],{"href":312},"\u002Fblog\u002Fsupabase-rls-disabled-in-public","RLS disabled in public",", y ese aviso\nseguirá ahí dentro de un mes.",[33,316,318],{"id":317},"cómo-saber-si-el-círculo-se-ha-ido-de-verdad","Cómo saber si el círculo se ha ido de verdad",[10,320,321],{},"Que el error pare no es la prueba, porque hay dos cosas distintas que lo hacen\nparar.",[10,323,324,325,327,328,331],{},"Hay además una manera de que la función de ayuda parezca correcta y siga en\nrecursión, y Supabase la documenta: una función security definer solo se salta\nlas reglas si a su dueño le está permitido. En Supabase el dueño es ",[21,326,160],{},",\nque tiene ese privilegio, así que el patrón de arriba funciona. Una función que\npertenezca a un rol sin él, o que lea una tabla puesta en\n",[21,329,330],{},"force row level security",", vuelve a pasar por la regla y se queda en el\ncírculo.",[10,333,334],{},"Dos comprobaciones, y haz las dos:",[10,336,337,340,341,344,345,348,349,352],{},[28,338,339],{},"Escribe los casos y ejecútalos."," El procedimiento de Supabase es un archivo\n",[21,342,343],{},".sql"," bajo ",[21,346,347],{},"supabase\u002Ftests\u002F"," que afirma permiso y denegación para cada\noperación, para un usuario con sesión y para uno anónimo, lanzado con\n",[21,350,351],{},"supabase test db",". Un admin tiene que ver todos los perfiles, un miembro el\nsuyo, un desconocido nada. Hasta que eso pase, lo que sabes es que el error\nparó.",[10,354,355,358,359,363,364,368],{},[28,356,357],{},"Y después pregunta desde fuera, sin sesión."," Esa es la pregunta que hace el\nnavegador de un visitante, y la única que refleja lo que tu app reparte de\nverdad. ",[214,360,362],{"href":361},"\u002Fblog\u002Fcan-anyone-read-your-supabase-database","Si un desconocido puede leer tu base de datos","\nlo recorre paso a paso, y\n",[214,365,367],{"href":366},"\u002Fblog\u002Fsupabase-rls-on-but-table-still-public","Row Level Security puede estar activado y la tabla seguir siendo pública","\ncubre el caso en que las reglas existen y aun así dejan pasar a todo el mundo.",[33,370,372],{"id":371},"cuando-la-recursión-te-dice-que-el-esquema-está-mal","Cuando la recursión te dice que el esquema está mal",[10,374,375],{},"A veces no hay pregunta circular que quitar, porque las dos tablas dependen de\nverdad una de la otra.",[10,377,378,379,382,383,386,387,389,390,392],{},"La guía de Supabase usa compartir como ejemplo: una regla sobre ",[21,380,381],{},"lists"," consulta\n",[21,384,385],{},"list_members"," para ver con quién se compartió la lista, y una regla sobre\n",[21,388,385],{}," consulta ",[21,391,381],{}," para ver de quién es. La regla de cada tabla lee\nla otra, y ninguna puede ir primero. El mismo ayudante lo deshace, con una\nfunción que devuelve los ids de las listas a las que pertenece quien llama, y\nlas dos reglas preguntando a esa función en lugar de preguntarse entre ellas.",[10,394,395],{},"La señal a la que merece la pena atender es cuando necesitas tres o cuatro de\nestas para que un esquema funcione. A esas alturas la pertenencia se está\nreconstruyendo dentro de las reglas cada vez, y una sola tabla que diga\nclaramente quién puede ver qué suele dar menos mantenimiento que las reglas que\nestás desenredando.",[33,397,399],{"id":398},"cómo-mantener-la-tabla-cerrada-después-de-arreglarla","Cómo mantener la tabla cerrada después de arreglarla",[10,401,402,405],{},[28,403,404],{},"Una regla que arreglaste hoy describe la base de datos de hoy."," La siguiente\npolicy, la siguiente tabla y la siguiente vez que alguien se quita un error de\nencima a medianoche pasan todas después de la última vez que miraste, y ninguna\ncambia nada que puedas ver en tus pantallas.",[10,407,408],{},[28,409,410,414],{},[214,411,413],{"href":412},"\u002Fpricing","Reeve Monitor"," le vuelve a hacer a tu app las mismas preguntas, con\nun horario:",[147,416,417,420,427,430],{},[150,418,419],{},"las nueve comprobaciones cada hora, en hasta tres apps",[150,421,422,423,426],{},"un mensaje cuando un resultado ",[28,424,425],{},"cambia",", para que una tabla que se abrió anoche no espere a que te des cuenta",[150,428,429],{},"si la app responde, cada 60 segundos",[150,431,432],{},"un informe mensual de lo que vio",[10,434,435,443],{},[28,436,437,438,442],{},"Si tu app guarda sus datos en tu propio proyecto de Supabase,\n",[214,439,441],{"href":440},"\u002Fsupabase-backups","Reeve Care"," además guarda una copia de esa base de datos.","\nUna regla que deja leer a un desconocido es un problema. Una regla que lo deja\nescribir es el otro, y ninguna comprobación de este artículo devuelve filas\nborradas.",[147,445,446,449,452,455,458],{},[150,447,448],{},"una copia cifrada de tu base de datos de Supabase cada noche, guardada donde tu proyecto no llega",[150,450,451],{},"cada copia verificada antes de contar, contando las filas de cada tabla",[150,453,454],{},"una restauración en un clic cuando la necesites",[150,456,457],{},"también tus archivos subidos, en cuanto conectes una credencial de Storage",[150,459,460],{},"todo lo que hace Monitor",[33,462,464],{"id":463},"qué-hacer-ahora-mismo","Qué hacer ahora mismo",[466,467,468],"key-takeaways",{},[147,469,470,473,482,488,497],{},[150,471,472],{},"Deja Row Level Security activado. El error va de una regla, y desactivar las reglas es un cambio en toda la tabla.",[150,474,475,476,478,479,481],{},"Mueve la comprobación del rol a una función ",[21,477,156],{}," en un esquema no expuesto, con ",[21,480,168],{}," y cada nombre de tabla escrito completo.",[150,483,484,485,487],{},"Apunta la policy a la función, envuelta como ",[21,486,193],{},", para que corra una vez por sentencia y no una vez por fila.",[150,489,490,491,493,494,496],{},"Escribe los casos permitidos y denegados bajo ",[21,492,347],{}," y ejecuta ",[21,495,351],{},": un admin ve todos los perfiles, un miembro el suyo, un desconocido nada.",[150,498,499,500,93],{},"Si ya desactivaste Row Level Security para poder avanzar, vuelve a activarlo hoy y arregla la regla como toca. El advisor de Supabase seguirá reportando esa tabla hasta que lo hagas, y ",[214,501,503],{"href":502},"\u002Fblog\u002Fenable-rls-on-every-supabase-table","le corresponde a todas las tablas que tengas",[10,505,506,507,511],{},"Si quieres el resto de la lista para una app recién lanzada, la\n",[214,508,510],{"href":509},"\u002Fchecklist","checklist de seguridad de 10 minutos"," cubre esto y las otras cosas\nque conviene cerrar antes de que alguien las encuentre.",{"title":23,"searchDepth":513,"depth":513,"links":514},3,[515,517,518,519,520,521,522,523,524],{"id":35,"depth":516,"text":36},2,{"id":65,"depth":516,"text":66},{"id":105,"depth":516,"text":106},{"id":124,"depth":516,"text":125},{"id":220,"depth":516,"text":221},{"id":317,"depth":516,"text":318},{"id":371,"depth":516,"text":372},{"id":398,"depth":516,"text":399},{"id":463,"depth":516,"text":464},"Fundamentos de seguridad","\u002Fblog\u002Finfinite-recursion-in-policy-for-relation\u002Fcover-1200x630.png","Un panel de tabla cerrado con su propia llave dibujada dentro, detrás del cristal, y una barra cruzada sobre la puerta.","\"Infinite recursion detected in policy for relation\" significa que tu policy preguntó a la tabla que protege. Así se rompe el círculo.",false,"md",[532,535,538,541,543],{"q":533,"a":534},"¿Qué provoca \"infinite recursion detected in policy for relation\"?","Una regla sobre una tabla que necesita leer esa misma tabla antes de poder responder. Tu regla viene a decir \"deja pasar a esta persona si su fila en profiles dice que es admin\", así que la base de datos va a leer esa fila en profiles, lo que obliga a comprobar la regla de profiles, que la manda otra vez a leer la fila. Postgres nota que está dando vueltas, se detiene y lanza el error 42P17. Lo mismo ocurre entre dos tablas cuyas reglas leen cada una la otra.",{"q":536,"a":537},"¿Es seguro usar una función security definer en una policy?","Sí, con dos condiciones que Supabase enuncia en su propia guía de Row Level Security. Pon search_path en la cadena vacía y escribe dentro cada nombre de tabla completo, es decir public.profiles y no profiles. Sin eso, alguien puede apuntar un nombre sin cualificar a un objeto suyo y hacerlo correr con los privilegios del dueño de la función. Y crea la función en un esquema que no esté expuesto en la API, porque una función security definer en un esquema expuesto se puede llamar desde fuera con los privilegios de quien la creó.",{"q":539,"a":540},"¿Debería desactivar RLS para arreglarlo?","Hace que el error pare y abre la tabla. Con Row Level Security desactivado, cualquiera que tenga la clave que tu app envía al navegador puede leer cada fila de esa tabla, y esa clave cualquiera puede sacarla de tu sitio. Tu app se comporta igual en los dos casos, así que después nada te dice cuál de las dos elegiste. La función de ayuda de más abajo hace que el error pare y deja la tabla cerrada.",{"q":106,"a":542},"Porque en profiles suele estar la respuesta a \"quién es esta persona\". Una regla que trata distinto a los admins, o distinto a los miembros de un equipo, necesita saber qué es quien llama, y ese dato está guardado en profiles. Así que la regla que protege profiles acaba leyendo profiles. Cualquier tabla que guarde el rol o la pertenencia de quien llama puede producirlo, y en una app hecha con Lovable, Bolt o v0 esa tabla suele ser la que se llama profiles.",{"q":544,"a":545},"¿Cómo compruebo que mi policy funciona de verdad?","Dos cosas distintas hacen que el error pare, así que por sí solo el error desapareciendo dice muy poco. Escribe los casos permitidos y denegados en un archivo .sql bajo supabase\u002Ftests\u002F y ejecuta supabase test db, que es el procedimiento que Supabase publica con la guía: un dueño con sesión iniciada tiene que pasar y un desconocido tiene que ser rechazado. Luego hazle a tu base de datos la misma pregunta desde fuera, sin ninguna sesión, como la haría el navegador de un visitante.","\u002Fblog\u002Finfinite-recursion-in-policy-for-relation\u002Fcard-800x500.png",[548,549,550,551,552,553,554,555],"infinite recursion detected in policy for relation","supabase infinite recursion policy","infinite recursion rls supabase","error recursion policy supabase","42P17 supabase","bucle infinito policy rls","supabase profiles policy recursion","error policy rol supabase",{},true,"Infinite recursion detected in policy","\u002Fblog\u002Finfinite-recursion-in-policy-for-relation","2026-10-08",{"title":5,"description":528},"blog\u002Finfinite-recursion-in-policy-for-relation",[564,565,566],"\"Infinite recursion detected in policy for relation\" es el error 42P17 de Postgres. Una regla de Row Level Security sobre una tabla fue a hacerle una pregunta a esa misma tabla, y la pregunta no tiene final.","No es señal de que Row Level Security no sirviera para tu app. Es señal de que una regla preguntó en círculo.","Una función de ayuda pequeña lee la tabla fuera de las reglas y rompe el círculo. Desactivar Row Level Security en esa tabla también hace que el error pare, entregando cada fila que contiene a cualquiera que tenga la clave que viaja dentro de tu app.","cbTHkIZ2BcnDxd0QnoZEul9cczA36u6y-syEWK2C3oY",[569,575,581,587,588,594,600,606,612,618,624,630,636,642,649,655,661,666,672,678,684,690,696,702,708,714,720,726,732,738,744,750,756,761,766,772,778,784,790,796,802,808,814,820,826,832,838,843,849,855,861,867,873,879,884,889,895,901,906,911,917],{"path":570,"title":571,"description":572,"published":573,"category":525,"image":574,"draft":529},"\u002Fblog\u002Fstorage-upload-violates-row-level-security","\"Row-level security policy for table objects\" al subir","\"New row violates row-level security policy for table objects\" significa que tu subida no tiene regla insert. Hacer público el bucket no añade ninguna.","2026-10-11","\u002Fblog\u002Fstorage-upload-violates-row-level-security\u002Fcard-800x500.png",{"path":576,"title":577,"description":578,"published":579,"category":525,"image":580,"draft":529},"\u002Fblog\u002Fbase44-security-scan","Escaneo de seguridad de Base44: lo que solo él ve","El escaneo de seguridad de Base44 revisa siete tipos de problema desde dentro. Esta es la mitad que lee y nadie más puede leer, y la mitad que nunca mira.","2026-10-10","\u002Fblog\u002Fbase44-security-scan\u002Fcard-800x500.png",{"path":582,"title":583,"description":584,"published":585,"category":525,"image":586,"draft":529},"\u002Fblog\u002Fapi-key-leaked-what-to-do","Tu clave API se filtró. El orden en el que hay que actuar","Una clave API se filtró y no sabes por dónde empezar. No todas las claves de tu frontend lo son, y el orden importa más que la velocidad.","2026-10-09","\u002Fblog\u002Fapi-key-leaked-what-to-do\u002Fcard-800x500.png",{"path":559,"title":5,"description":528,"published":560,"category":525,"image":546,"draft":529},{"path":589,"title":590,"description":591,"published":592,"category":525,"image":593,"draft":529},"\u002Fblog\u002Fno-api-key-found-in-request","\"No API key found in request\" en Supabase, y el arreglo equivocado","\"No API key found in request\" significa que tu petición llegó a Supabase sin clave. Casi todas las respuestas apuntan a las reglas de tu base de datos.","2026-10-07","\u002Fblog\u002Fno-api-key-found-in-request\u002Fcard-800x500.png",{"path":595,"title":596,"description":597,"published":598,"category":525,"image":599,"draft":529},"\u002Fblog\u002Fis-base44-safe","¿Es seguro Base44? Lo que vimos en 5.442 apps Base44","¿Es seguro Base44? Pasamos nueve comprobaciones a 5.442 apps Base44 en producción. Casi todas sacaron B por motivos que su dueño nunca eligió.","2026-10-06","\u002Fblog\u002Fis-base44-safe\u002Fcard-800x500.png",{"path":601,"title":602,"description":603,"published":604,"category":525,"image":605,"draft":529},"\u002Fblog\u002Fmove-a-secret-to-an-edge-function","Ocultar una clave API: muévela a una Edge Function de Supabase","Ocultar una clave API significa sacarla del navegador, y una Edge Function de Supabase es el sitio más pequeño donde ponerla. Dos pasos importan más.","2026-10-05","\u002Fblog\u002Fmove-a-secret-to-an-edge-function\u002Fcard-800x500.png",{"path":607,"title":608,"description":609,"published":610,"category":525,"image":611,"draft":529},"\u002Fblog\u002Fenv-file-exposed-on-your-server","¿Tu archivo .env está expuesto? Las doce rutas a revisar","¿Tu archivo .env está expuesto en tu propio servidor web? Doce direcciones te lo dicen, y un acierto significa que todo lo que contiene ya es público.","2026-10-04","\u002Fblog\u002Fenv-file-exposed-on-your-server\u002Fcard-800x500.png",{"path":613,"title":614,"description":615,"published":616,"category":525,"image":617,"draft":529},"\u002Fblog\u002Fis-v0-safe","Seguridad de v0: las 1.790 aplicaciones escaneadas sacaron una A","Seguridad de v0, medida en 1.790 aplicaciones v0 en vivo: todas sacaron A. Solo 17 nombraban una base de datos, y eso es casi todo lo que mide la A.","2026-10-03","\u002Fblog\u002Fis-v0-safe\u002Fcard-800x500.png",{"path":619,"title":620,"description":621,"published":622,"category":525,"image":623,"draft":529},"\u002Fblog\u002Fis-bolt-safe","¿Es seguro Bolt? Lo que vimos en 1.123 aplicaciones Bolt","¿Es seguro Bolt? Pasamos 1.123 aplicaciones Bolt en vivo por nueve comprobaciones. El hosting salió limpio. Los hallazgos eran claves y tablas abiertas dentro.","2026-10-02","\u002Fblog\u002Fis-bolt-safe\u002Fcard-800x500.png",{"path":625,"title":626,"description":627,"published":628,"category":525,"image":629,"draft":529},"\u002Fblog\u002Fis-supabase-down-or-your-app","¿Supabase está caído o es tu app? Cómo distinguirlo","¿Supabase está caído o es tu app? Un proyecto pausado, una base de datos llena y un fallo tuyo parecen lo mismo. Cuatro comprobaciones los separan.","2026-10-01","\u002Fblog\u002Fis-supabase-down-or-your-app\u002Fcard-800x500.png",{"path":631,"title":632,"description":633,"published":634,"category":525,"image":635,"draft":529},"\u002Fblog\u002Fdoes-supabase-encrypt-my-data","¿Supabase cifra mis datos? Sí. Esto es lo que detiene","¿Supabase cifra los datos? Sí: AES-256 en reposo, TLS en tránsito, SOC 2 e ISO 27001. Qué cubre cada uno, y la fuga que ninguno de ellos detiene.","2026-09-30","\u002Fblog\u002Fdoes-supabase-encrypt-my-data\u002Fcard-800x500.png",{"path":637,"title":638,"description":639,"published":640,"category":525,"image":641,"draft":529},"\u002Fblog\u002Fsupabase-free-plan-limits","¿Cuánto cuesta Supabase? Los límites del plan gratuito","Los límites del plan gratuito de Supabase: 500 MB de base de datos, 5 GB de egress y ningún backup. Qué pasa en cada uno y qué cambia Pro por $25.","2026-09-29","\u002Fblog\u002Fsupabase-free-plan-limits\u002Fcard-800x500.png",{"path":643,"title":644,"description":645,"published":646,"category":647,"image":648,"draft":529},"\u002Fblog\u002Ftest-your-supabase-backup","Prueba tu backup de Supabase antes del día que lo necesites","Cómo probar tu backup de Supabase: restaurarlo en un proyecto de prueba, contar filas, iniciar sesión y buscar la línea que le falta a un archivo cortado.","2026-09-28","Backups","\u002Fblog\u002Ftest-your-supabase-backup\u002Fcard-800x500.png",{"path":650,"title":651,"description":652,"published":653,"category":647,"image":654,"draft":529},"\u002Fblog\u002Fsupabase-backup-github-action","Backup gratis de Supabase con una GitHub Action, y la trampa","Un backup de Supabase con GitHub Action es gratis y sirve a muchas apps. El workflow, el connection string correcto y el egress de cada ejecución.","2026-09-27","\u002Fblog\u002Fsupabase-backup-github-action\u002Fcard-800x500.png",{"path":656,"title":657,"description":658,"published":659,"category":647,"image":660,"draft":529},"\u002Fblog\u002Fdownload-your-supabase-backup","Por qué no puedes descargar tu backup de Supabase","No puedes descargar tu backup de Supabase en un proyecto actual: la copia diaria es una instantánea física. Cómo comprobarlo y tener tu propia copia.","2026-09-26","\u002Fblog\u002Fdownload-your-supabase-backup\u002Fcard-800x500.png",{"path":662,"title":663,"description":664,"published":659,"category":525,"image":665,"draft":529},"\u002Fblog\u002Fsupabase-permission-denied-for-table","Supabase \"permission denied for table\": el grant que falta","Desde el 30 de octubre, una tabla nueva de Supabase responde \"permission denied for table\" hasta que le das acceso. El grant del email es la mitad del arreglo.","\u002Fblog\u002Fsupabase-permission-denied-for-table\u002Fcard-800x500.png",{"path":667,"title":668,"description":669,"published":670,"category":525,"image":671,"draft":529},"\u002Fblog\u002Fopen-api-endpoint-exposed","¿Un endpoint de API abierto es un problema? Mira el JSON","Tu escaneo señaló un endpoint de API abierto. Que importe depende de lo que devolvió, y la mayoría de los que encontramos eran de la plataforma.","2026-09-25","\u002Fblog\u002Fopen-api-endpoint-exposed\u002Fcard-800x500.png",{"path":673,"title":674,"description":675,"published":676,"category":525,"image":677,"draft":529},"\u002Fblog\u002Flovable-security-scan","Escaneo de seguridad de Lovable: lo que no puede probar","Escaneo de seguridad de Lovable: qué revisan el Quick scan y el Deep scan, cuándo corre cada uno, y lo único que ningún escaneo desde dentro puede probar.","2026-09-24","\u002Fblog\u002Flovable-security-scan\u002Fcard-800x500.png",{"path":679,"title":680,"description":681,"published":682,"category":647,"image":683,"draft":529},"\u002Fblog\u002Fsupabase-backup-auth-users","Por qué tu dump de Supabase no tiene usuarios dentro","Ejecuta supabase db dump por sí solo y obtienes la forma de tu base de datos y ninguna de sus filas, sin el esquema auth donde viven tus usuarios.","2026-09-23","\u002Fblog\u002Fsupabase-backup-auth-users\u002Fcard-800x500.png",{"path":685,"title":686,"description":687,"published":688,"category":525,"image":689,"draft":529},"\u002Fblog\u002Fdomain-and-certificate-expiry","Dominio caducado, web caída: qué pasa a partir de ahora","Tu dominio caducó y tu web está caída. Aquí tienes el reloj que acaba de arrancar, por qué el certificado es el menor problema, y cómo comprobar ambos.","2026-09-22","\u002Fblog\u002Fdomain-and-certificate-expiry\u002Fcard-800x500.png",{"path":691,"title":692,"description":693,"published":694,"category":525,"image":695,"draft":529},"\u002Fblog\u002Fis-lovable-safe","¿Es seguro Lovable? Lo que vimos en 18.554 aplicaciones Lovable","¿Es seguro Lovable? Pasamos 18.554 aplicaciones Lovable por nueve comprobaciones. La plataforma fue la más limpia de cinco. Los hallazgos estaban dentro.","2026-09-21","\u002Fblog\u002Fis-lovable-safe\u002Fcard-800x500.png",{"path":697,"title":698,"description":699,"published":700,"category":525,"image":701,"draft":529},"\u002Fblog\u002Fvibe-coded-app-security-checklist","La lista de seguridad del vibe coding, en nueve comprobaciones","Una lista de seguridad del vibe coding con nueve puntos, cada uno verificable en tu app desde fuera por cualquiera, y cada uno con una prueba de una línea.","2026-09-20","\u002Fblog\u002Fvibe-coded-app-security-checklist\u002Fcard-800x500.png",{"path":703,"title":704,"description":705,"published":706,"category":525,"image":707,"draft":529},"\u002Fblog\u002Fstripe-secret-key-in-frontend","Una clave secreta de Stripe en tu frontend puede mover dinero","Una clave secreta de Stripe expuesta en tu frontend puede reembolsar, cobrar y leer cada ficha de cliente que tienes. Tu clave pk_live_ sí debe estar ahí.","2026-09-19","\u002Fblog\u002Fstripe-secret-key-in-frontend\u002Fcard-800x500.png",{"path":709,"title":710,"description":711,"published":712,"category":525,"image":713,"draft":529},"\u002Fblog\u002Fvite-and-next-public-env-vars","Variables de entorno de Vite expuestas: el prefijo dice publica esto","Variables de entorno de Vite expuestas en tu app: hicieron lo que pedía el prefijo. VITE_ y NEXT_PUBLIC_ significan publica esto, y la IA nunca supo el coste.","2026-09-18","\u002Fblog\u002Fvite-and-next-public-env-vars\u002Fcard-800x500.png",{"path":715,"title":716,"description":717,"published":718,"category":525,"image":719,"draft":529},"\u002Fblog\u002Fbase44-source-maps","Seguridad en Base44: qué marca un escaneo y qué te toca arreglar a ti","Seguridad en Base44 en 5.438 apps escaneadas: tres hallazgos en casi todas son de la plataforma, el source map es la insignia de Base44 y lo tuyo es poco.","2026-09-17","\u002Fblog\u002Fbase44-source-maps\u002Fcard-800x500.png",{"path":721,"title":722,"description":723,"published":724,"category":525,"image":725,"draft":529},"\u002Fblog\u002Fis-cursor-ai-safe","¿Es seguro Cursor AI? El editor, el código y la app que publicaste","¿Es seguro Cursor AI? Tres preguntas en una búsqueda: qué guarda Cursor, en qué falla el código que escribe y si la app que publicaste está abierta.","2026-09-16","\u002Fblog\u002Fis-cursor-ai-safe\u002Fcard-800x500.png",{"path":727,"title":728,"description":729,"published":730,"category":525,"image":731,"draft":529},"\u002Fblog\u002Fis-replit-safe","¿Es seguro Replit? Lo que vimos en 3.042 aplicaciones Replit en vivo","¿Es seguro Replit? Pasamos 3.042 aplicaciones Replit en vivo por nueve comprobaciones externas. Los hallazgos estaban en la aplicación publicada, no en el host.","2026-09-15","\u002Fblog\u002Fis-replit-safe\u002Fcard-800x500.png",{"path":733,"title":734,"description":735,"published":736,"category":647,"image":737,"draft":529},"\u002Fblog\u002Fsupabase-storage-backup","Supabase storage backup: tu copia de la base no tiene archivos","Un Supabase storage backup es una tarea aparte. Las copias de la base guardan la lista de tus archivos y ninguno de ellos, así que cada upload queda roto.","2026-09-14","\u002Fblog\u002Fsupabase-storage-backup\u002Fcard-800x500.png",{"path":739,"title":740,"description":741,"published":742,"category":647,"image":743,"draft":529},"\u002Fblog\u002Fsupabase-point-in-time-recovery","Point-in-time recovery de Supabase: precio y límites","El point-in-time recovery de Supabase rebobina tu base de datos a cualquier segundo de la última semana. Cuesta $100 al mes sobre Pro y solo cubre la base.","2026-09-13","\u002Fblog\u002Fsupabase-point-in-time-recovery\u002Fcard-800x500.png",{"path":745,"title":746,"description":747,"published":748,"category":647,"image":749,"draft":529},"\u002Fblog\u002Fsupabase-project-paused-recover","¿Proyecto de Supabase pausado? Tus datos siguen ahí","Supabase pausó tu proyecto tras una semana sin actividad. No se ha borrado nada, Restaurar está junto al nombre y tienes un año antes de que eso cambie.","2026-09-12","\u002Fblog\u002Fsupabase-project-paused-recover\u002Fcard-800x500.png",{"path":751,"title":752,"description":753,"published":754,"category":525,"image":755,"draft":529},"\u002Fblog\u002Fsafest-ai-app-builder","¿Qué builder de apps con IA es más seguro? 30.998 apps escaneadas","¿Qué builder de apps con IA es más seguro? Escaneamos 30.998 apps activas de Lovable, Base44, Replit, v0 y Bolt. El builder no es lo que decide tu nota.","2026-09-11","\u002Fblog\u002Fsafest-ai-app-builder\u002Fcard-800x500.png",{"path":502,"title":757,"description":758,"published":759,"category":525,"image":760,"draft":529},"Activa Row Level Security en cada tabla de Supabase y compruébalo","Activar Row Level Security en Supabase sin política cierra la tabla del todo. Una política sin el interruptor no hace nada. Aquí tienes el SQL y la prueba.","2026-09-10","\u002Fblog\u002Fenable-rls-on-every-supabase-table\u002Fcard-800x500.png",{"path":312,"title":762,"description":763,"published":764,"category":525,"image":765,"draft":529},"Supabase \"RLS disabled in public\": lo que el aviso no ve","Supabase marca \"RLS disabled in public\" como error. No dice nada de la política de lectura que deja tu tabla igual de abierta a cualquiera.","2026-09-09","\u002Fblog\u002Fsupabase-rls-disabled-in-public\u002Fcard-800x500.png",{"path":767,"title":768,"description":769,"published":770,"category":525,"image":771,"draft":529},"\u002Fblog\u002Frotate-supabase-service-role-key","Cómo rotar una clave service_role de Supabase filtrada","Supabase dice que primero cierres la fuga. Otras guías dicen que rotes ya. Cuál es lo correcto depende de dónde se filtró tu clave service_role.","2026-09-08","\u002Fblog\u002Frotate-supabase-service-role-key\u002Fcard-800x500.png",{"path":773,"title":774,"description":775,"published":776,"category":525,"image":777,"draft":529},"\u002Fblog\u002Fvibe-coding-security-scanners-compared","Escáneres de seguridad vibe coding comparados, el nuestro incluido","El mejor escáner de seguridad para vibe coding depende de tres preguntas que ninguna lista de funciones responde. Diez herramientas, con precios.","2026-09-07","\u002Fblog\u002Fvibe-coding-security-scanners-compared\u002Fcard-800x500.png",{"path":779,"title":780,"description":781,"published":782,"category":525,"image":783,"draft":529},"\u002Fblog\u002Fsupabase-security-checker","Supabase security checker: haz tú mismo las cinco comprobaciones","Un Supabase security checker lee tu app publicada en lugar de la configuración de tu proyecto. Estas son las cinco comprobaciones y cómo hacer cada una.","2026-09-06","\u002Fblog\u002Fsupabase-security-checker\u002Fcard-800x500.png",{"path":785,"title":786,"description":787,"published":788,"category":525,"image":789,"draft":529},"\u002Fblog\u002Fvibe-coding-security-scanner","Escáner de seguridad para vibe coding: qué se le escapa","Un escáner de seguridad para vibe coding lee tu app en vivo desde fuera. Qué cubre eso, las cuatro cosas que no puede ver y cómo leer el resultado.","2026-09-05","\u002Fblog\u002Fvibe-coding-security-scanner\u002Fcard-800x500.png",{"path":791,"title":792,"description":793,"published":794,"category":525,"image":795,"draft":529},"\u002Fblog\u002Fmissing-security-headers","Faltan cabeceras de seguridad: cuándo importa de verdad","Que falten cabeceras de seguridad es el hallazgo que más imprime nuestro escáner. Contra qué protegen y cuándo son la línea menos urgente de tu informe.","2026-09-04","\u002Fblog\u002Fmissing-security-headers\u002Fcard-800x500.png",{"path":797,"title":798,"description":799,"published":800,"category":525,"image":801,"draft":529},"\u002Fblog\u002Fopenai-api-key-exposed-in-frontend","Tu clave de API de OpenAI está expuesta en tu frontend. Rótala.","Una clave de API de OpenAI expuesta en tu frontend no se puede atar a un dominio. Rótala hoy, mueve la llamada a tu servidor y limita el gasto.","2026-09-03","\u002Fblog\u002Fopenai-api-key-exposed-in-frontend\u002Fcard-800x500.png",{"path":803,"title":804,"description":805,"published":806,"category":525,"image":807,"draft":529},"\u002Fblog\u002Fwhat-secrets-leak-from-vibe-coded-apps","Clave de API expuesta en el frontend: qué llevan 30.998 apps","Una clave de API expuesta en tu frontend suele ser una clave de Google Maps. Escaneamos 30.998 apps vibe-coded y contamos qué secretos se filtran de verdad.","2026-09-02","\u002Fblog\u002Fwhat-secrets-leak-from-vibe-coded-apps\u002Fcard-800x500.png",{"path":809,"title":810,"description":811,"published":812,"category":647,"image":813,"draft":529},"\u002Fblog\u002Fsupabase-backup-tools-compared","Herramientas de backup de Supabase comparadas, la nuestra incluida","Cuatro tipos de herramienta de backup de Supabase, qué copia cada una y cuándo una GitHub Action gratuita le gana a pagar a cualquiera, nosotros incluidos.","2026-09-01","\u002Fblog\u002Fsupabase-backup-tools-compared\u002Fcard-800x500.png",{"path":815,"title":816,"description":817,"published":818,"category":525,"image":819,"draft":529},"\u002Fblog\u002Freplit-secrets-explained","Cómo usar secrets en Replit, y qué se publica de todos modos","Cómo usar secrets en Replit: añadir uno, leerlo y arreglar las dos causas del undefined. Y las claves que la herramienta Secrets no puede mantener privadas.","2026-08-31","\u002Fblog\u002Freplit-secrets-explained\u002Fcard-800x500.png",{"path":821,"title":822,"description":823,"published":824,"category":647,"image":825,"draft":529},"\u002Fblog\u002Fback-up-supabase-free-tier","Backups del plan gratuito de Supabase: cómo hacer uno sin terminal","No hay backups en el plan gratuito de Supabase: la copia tienes que hacerla tú. Cómo sacarla desde el panel y qué deja fuera la vía CSV.","2026-08-30","\u002Fblog\u002Fback-up-supabase-free-tier\u002Fcard-800x500.png",{"path":827,"title":828,"description":829,"published":830,"category":525,"image":831,"draft":529},"\u002Fblog\u002Fis-supabase-secure","¿Supabase es seguro? Sí. Tu proyecto es otra pregunta","¿Supabase es seguro? La plataforma está auditada, cifrada y sometida a pentests. Su propia documentación dice dónde termina eso y empiezan tus ajustes.","2026-08-29","\u002Fblog\u002Fis-supabase-secure\u002Fcard-800x500.png",{"path":833,"title":834,"description":835,"published":836,"category":525,"image":837,"draft":529},"\u002Fblog\u002Fwhere-to-find-supabase-api-keys","Dónde están tus claves API de Supabase: anon, service_role y URL","La URL del proyecto, la clave anon y la clave service_role están en una sola página del panel. Aquí tienes dónde está, y cuál de los cuatro va en tu app.","2026-08-28","\u002Fblog\u002Fwhere-to-find-supabase-api-keys\u002Fcard-800x500.png",{"path":216,"title":839,"description":840,"published":841,"category":525,"image":842,"draft":529},"New row violates row-level security policy en Supabase. ¿Y ahora?","\"New row violates row-level security policy\" significa que Supabase rechazó una escritura. El arreglo rápido vuelve a abrir la tabla para cualquiera.","2026-08-27","\u002Fblog\u002Fnew-row-violates-row-level-security-policy\u002Fcard-800x500.png",{"path":844,"title":845,"description":846,"published":847,"category":525,"image":848,"draft":529},"\u002Fblog\u002Fcors-wildcard-security-risk","¿Es un comodín CORS un riesgo de seguridad? Casi nunca.","¿Es un comodín CORS un riesgo de seguridad? Casi siempre es el ajuste por defecto de tu builder y no entrega nada que tu servidor no diera ya a cualquiera.","2026-08-26","\u002Fblog\u002Fcors-wildcard-security-risk\u002Fcard-800x500.png",{"path":850,"title":851,"description":852,"published":853,"category":647,"image":854,"draft":529},"\u002Fblog\u002Fsupabase-branching-is-not-a-backup","Supabase branching no es un backup. Solo va hacia adelante.","Por qué el branching de Supabase no es un backup: una rama arranca sin tus datos y un merge solo mueve el esquema. Para qué sirve y qué usar en su lugar.","2026-08-25","\u002Fblog\u002Fsupabase-branching-is-not-a-backup\u002Fcard-800x500.png",{"path":856,"title":857,"description":858,"published":859,"category":647,"image":860,"draft":529},"\u002Fblog\u002Fhow-to-restore-a-supabase-backup","Cómo restaurar un backup de Supabase, y qué se rompe después","Cómo restaurar un backup de Supabase desde el panel o desde un archivo de volcado, qué reemplaza la restauración y por qué tu app puede seguir rota al terminar.","2026-08-24","\u002Fblog\u002Fhow-to-restore-a-supabase-backup\u002Fcard-800x500.png",{"path":862,"title":863,"description":864,"published":865,"category":525,"image":866,"draft":529},"\u002Fblog\u002Fsupabase-storage-bucket-public","Tu bucket de Supabase Storage es público. ¿Es un problema?","Un bucket público de Supabase Storage significa que quien tenga la URL de un archivo puede abrirlo. No significa que alguien pueda listar lo que hay dentro.","2026-08-23","\u002Fblog\u002Fsupabase-storage-bucket-public\u002Fcard-800x500.png",{"path":868,"title":869,"description":870,"published":871,"category":525,"image":872,"draft":529},"\u002Fblog\u002Fgoogle-api-key-exposed-in-frontend","¿Es un problema una clave de API de Google en tu frontend?","Una clave de API de Google en tu frontend es la clave que más encuentra nuestro escáner, y casi siempre no pasa nada. Un ajuste gratuito decide cuál es cuál.","2026-08-22","\u002Fblog\u002Fgoogle-api-key-exposed-in-frontend\u002Fcard-800x500.png",{"path":874,"title":875,"description":876,"published":877,"category":647,"image":878,"draft":529},"\u002Fblog\u002Fai-agent-deleted-my-database","Un agente de IA borró mis datos de Supabase. ¿Qué puedo recuperar?","Un agente de IA borró datos de tu base. Lo que puedes recuperar ya estaba decidido antes de que se ejecutara, y los próximos minutos deciden el resto.","2026-08-21","\u002Fblog\u002Fai-agent-deleted-my-database\u002Fcard-800x500.png",{"path":361,"title":880,"description":881,"published":882,"category":525,"image":883,"draft":529},"¿Puede cualquiera leer tu base de datos Supabase? 3.680 apps","¿Puede cualquiera leer tu base de datos Supabase sin iniciar sesión? Escaneamos 30.998 apps activas hechas con builders de IA y medimos cuántas dicen que sí.","2026-08-18","\u002Fblog\u002Fcan-anyone-read-your-supabase-database\u002Fcard-800x500.png",{"path":885,"title":886,"description":887,"published":882,"category":525,"image":888,"draft":529},"\u002Fblog\u002Fsource-maps-exposed-in-production","Source maps expuestos: tu app está publicando su código original","Un source map expuesto deja que cualquiera lea el código original de tu app, comentarios incluidos. La comprobación de 30 segundos y lo que de verdad importa.","\u002Fblog\u002Fsource-maps-exposed-in-production\u002Fcard-800x500.png",{"path":890,"title":891,"description":892,"published":893,"category":525,"image":894,"draft":529},"\u002Fblog\u002Fsupabase-new-api-keys","Las nuevas claves de API de Supabase: ¿cuál va en tu app?","Supabase sustituyó anon y service_role por claves publicables y secretas. ¿Cuál pertenece a tu app y cuál no debe estar nunca?","2026-08-12","\u002Fblog\u002Fsupabase-new-api-keys\u002Fcard-800x500.png",{"path":896,"title":897,"description":898,"published":899,"category":647,"image":900,"draft":529},"\u002Fblog\u002Fdoes-supabase-back-up-my-database","¿Supabase respalda mi base de datos? Depende de tu plan.","¿Supabase respalda tu base de datos? A diario en los planes de pago, y nada en el gratuito. Cómo saber cuál tienes y qué es lo que esa copia no cubre.","2026-08-11","\u002Fblog\u002Fdoes-supabase-back-up-my-database\u002Fcard-800x500.png",{"path":366,"title":902,"description":903,"published":904,"category":525,"image":905,"draft":529},"Supabase Row Level Security está activado. Tu tabla sigue pública.","Activar Supabase Row Level Security no protege una tabla. Lo hacen tus políticas, y la que arregló tu app rota puede dejar entrar a cualquiera.","2026-08-10","\u002Fblog\u002Fsupabase-rls-on-but-table-still-public\u002Fcard-800x500.png",{"path":907,"title":908,"description":909,"published":904,"category":647,"image":910,"draft":529},"\u002Fblog\u002Fversion-history-is-not-a-backup","El historial de versiones no es un backup. No recupera una tabla.","Lovable y Bolt guardan un historial de versiones de tu código. Tu base de datos es un servicio aparte, y volver atrás no te devuelve tus datos.","\u002Fblog\u002Fversion-history-is-not-a-backup\u002Fcard-800x500.png",{"path":912,"title":913,"description":914,"published":915,"category":647,"image":916,"draft":529},"\u002Fblog\u002Fthree-ways-to-back-up-a-supabase-database","Tres formas de respaldar una base Supabase, y qué falta en cada una","El panel, pg_dump y un servicio gestionado. Qué guarda de verdad cada uno, qué deja fuera sin avisar y cuál sobrevive a perder la cuenta.","2026-08-09","\u002Fblog\u002Fthree-ways-to-back-up-a-supabase-database\u002Fcard-800x500.png",{"path":918,"title":919,"description":920,"published":915,"category":525,"image":921,"draft":529},"\u002Fblog\u002Fwhich-api-keys-are-safe-in-your-frontend","Qué claves de API son seguras en el navegador y cuáles no","Tu clave anon de Supabase está pensada para ser pública. Tu clave service_role no, y se salta todas las reglas que pongas. Cómo distinguirlas.","\u002Fblog\u002Fwhich-api-keys-are-safe-in-your-frontend\u002Fcard-800x500.png",1791705962952]