[{"data":1,"prerenderedAt":1144},["ShallowReactive",2],{"blog-es-database-error-deleting-user":3,"blog-index-es":618},{"id":4,"title":5,"body":6,"category":576,"cover":577,"coverAlt":578,"description":579,"draft":580,"extension":581,"faq":582,"image":597,"keywords":598,"meta":606,"navigation":607,"ogTitle":608,"path":609,"published":610,"seo":611,"stem":612,"tldr":613,"updated":610,"__hash__":617},"blog_es\u002Fblog\u002Fdatabase-error-deleting-user.md","\"Database error deleting user\" en Supabase, y qué se va con él",{"type":7,"value":8,"toc":562},"minimark",[9,13,24,27,34,39,42,59,65,69,72,75,98,101,120,124,131,134,140,155,166,181,184,192,206,210,213,219,222,225,231,234,238,241,305,321,324,330,333,347,352,356,365,378,392,397,401,404,432,443,450,454,457,473,476,484,488,494,502,524,528,554],[10,11,12],"p",{},"Abres Authentication → Users en tu panel de Supabase y borras una cuenta. Quizá\nla persona te pidió que la eliminaras, quizá es una cuenta de prueba que creaste\nla semana pasada. El panel responde:",[14,15,20],"pre",{"className":16,"code":18,"language":19},[17],"language-text","Failed to delete user: Database error deleting user\n","text",[21,22,18],"code",{"__ignoreMap":23},"",[10,25,26],{},"La cuenta sigue en la lista. La app que hiciste con Lovable, Bolt, v0, Cursor,\nClaude Code o Replit ha funcionado todo el tiempo, y la frase no nombra ninguna\ntabla ni ningún motivo.",[10,28,29,30,33],{},"La primera respuesta que encontrarás es una línea de SQL: añade\n",[21,31,32],{},"on delete cascade"," a lo que estorba. Funciona. También decide, para cada cuenta\nque se borre a partir de entonces, qué otras filas se borran con ella, y nadie\nvuelve a preguntarte por eso. Así que lee primero el error real y toma después\nesa decisión tabla por tabla.",[35,36,38],"h2",{"id":37},"qué-significa-database-error-deleting-user-en-supabase","¿Qué significa \"Database error deleting user\" en Supabase?",[10,40,41],{},"Tu base de datos rechazó el borrado y Supabase deshizo todo, así que la cuenta\nestá exactamente como estaba.",[10,43,44,45,48,49,52,53,58],{},"Borrar un usuario es trabajo de Supabase Auth, el servicio que está detrás del\ninicio de sesión de tu app. En el código fuente de Auth, el borrado ocurre dentro\nde una sola transacción, un grupo de cambios que se aplican todos o ninguno, y su\npaso central quita la fila de la persona de ",[21,46,47],{},"auth.users",". Si algo en tu base de\ndatos se opone, la transacción se revierte y Auth responde con esta única frase.\nSupabase la recoge como HTTP 500 con el código ",[21,50,51],{},"unexpected_failure",", junto a su\ngemela del registro,\n",[54,55,57],"a",{"href":56},"\u002Fblog\u002Fdatabase-error-saving-new-user","\"Database error saving new user\"",".",[10,60,61,62,64],{},"Piensa en tu base de datos como en un club de socios. ",[21,63,47],{}," es el registro\nde socios, y cada socio tiene un número: su id de usuario. Tus otros libros copian\nese número en sus líneas, sea un perfil, un pedido, una publicación o un\ncomentario. Tachar a alguien del registro es cosa de una línea. Pero cada libro\nsigue llevando el número, y mientras nadie haya escrito qué hacer con esas líneas,\nel secretario del club se niega.",[35,66,68],{"id":67},"dónde-está-el-error-real","¿Dónde está el error real?",[10,70,71],{},"En los logs de Postgres de tu proyecto, en el momento en que falló el borrado.",[10,73,74],{},"La nota de troubleshooting de Supabase sobre los errores al gestionar usuarios\nmarca el camino. Intenta el borrado una vez y apunta la hora. Después, en el\npanel:",[76,77,78,91],"ol",{},[79,80,81,82,86,87,90],"li",{},"Abre ",[83,84,85],"strong",{},"Logs"," y lee los logs de ",[83,88,89],{},"Auth"," a esa hora. La entrada es el registro\nque el propio Auth guardó del borrado fallido.",[79,92,93,94,97],{},"Abre los logs de ",[83,95,96],{},"Postgres"," a la misma hora. Esa línea es el error real, y\nnombra una tabla, una regla o un permiso.",[10,99,100],{},"La misma nota da tres causas, y la línea del log te dice cuál tienes:",[102,103,104,110,115],"ul",{},[79,105,106,107,109],{},"un trigger sobre la tabla ",[21,108,47],{}," que falla",[79,111,112,113],{},"una restricción que no se cumple, que en un borrado significa una foreign key\nde otra tabla que apunta a ",[21,114,47],{},[79,116,117,118],{},"Prisma, que según la nota puede romper los permisos sobre ",[21,119,47],{},[35,121,123],{"id":122},"una-tabla-que-todavía-apunta-al-usuario","Una tabla que todavía apunta al usuario",[10,125,126,127,130],{},"Si la línea de Postgres dice ",[21,128,129],{},"violates foreign key constraint",", una fila de alguna\nde tus tablas todavía guarda el id de este usuario, y Postgres no elimina a una\npersona a la que algo sigue apuntando.",[10,132,133],{},"La nota de Supabase imprime la forma que toma:",[14,135,138],{"className":136,"code":137,"language":19},[17],"ERROR:  23503: update or delete on table \"users\" violates foreign key constraint \"profiles_id_fkey\" on table \"profiles\"\nDETAIL:  Key (id)=(7428a53c-75b7-4531-9ae9-1567d9c4ac0a) is still referenced from table \"profiles\".\n",[21,139,137],{"__ignoreMap":23},[10,141,142,143,146,147,150,151,154],{},"Una foreign key es una regla sobre una columna que dice que cada valor que\ncontiene debe existir en otra tabla. Aquí la regla se llama ",[21,144,145],{},"profiles_id_fkey",",\nvive en tu tabla ",[21,148,149],{},"profiles",", y la línea ",[21,152,153],{},"Key"," es el id del usuario que intentaste\nborrar. En el club, es una línea del libro de perfiles con el número del socio, y\nel secretario negándose a tacharlo mientras siga ahí.",[10,156,157,158,161,162,58],{},"La misma regla también rechaza desde el otro lado. Una fila nueva que nombra a un\nusuario que no existe vuelve como\n",[21,159,160],{},"insert or update on table … violates foreign key constraint",", y\n",[54,163,165],{"href":164},"\u002Fblog\u002Fviolates-foreign-key-constraint","ese mensaje tiene su propio artículo",[10,167,168,169,172,173,176,177,180],{},"La negativa es lo que viene por defecto. Una foreign key creada sin nada detrás de\n",[21,170,171],{},"references auth.users"," recibe el comportamiento que Postgres llama ",[21,174,175],{},"no action",",\ny la guía de Supabase sobre borrados en cascada lo dice con todas las letras:\n",[21,178,179],{},"NO ACTION"," es el comportamiento por defecto si no especificas nada. Una tabla que\ntu builder creó así funciona perfectamente hasta la primera vez que se borra a\nalguien.",[10,182,183],{},"Para ver cada tabla que apunta a tus usuarios, y qué hace cada una en un borrado,\nejecuta esto en el SQL Editor:",[14,185,190],{"className":186,"code":188,"language":189,"meta":23},[187],"language-sql","select conrelid::regclass as table_name,\n       conname as rule,\n       case confdeltype\n         when 'c' then 'cascade'\n         when 'n' then 'set null'\n         when 'd' then 'set default'\n         when 'r' then 'restrict'\n         else 'no action'\n       end as on_delete\nfrom pg_constraint\nwhere contype = 'f'\n  and confrelid = 'auth.users'::regclass\n  and connamespace \u003C> 'auth'::regnamespace;\n","sql",[21,191,188],{"__ignoreMap":23},[10,193,194,195,198,199,201,202,205],{},"Solo lee el catálogo propio de Postgres y no cambia nada. La última línea deja\nfuera las tablas de Supabase en el esquema ",[21,196,197],{},"auth",". Cada fila que diga\n",[21,200,175],{}," o ",[21,203,204],{},"restrict"," es una tabla que rechazará el borrado siempre que tenga\nuna fila de ese usuario.",[35,207,209],{"id":208},"qué-borra-on-delete-cascade","¿Qué borra on delete cascade?",[10,211,212],{},"Cada fila que apunta al usuario, luego cada fila que apunta a esas, en cada\nborrado a partir de ese momento.",[10,214,215,216,218],{},"Es el arreglo que te dan primero, y la guía de gestión de usuarios de Supabase lo\nusa para una tabla de perfiles: indica que se especifique ",[21,217,32],{}," en\nla referencia. Para un perfil es lo correcto. Un perfil describe a la persona y\nnada más, así que debe irse cuando ella se va.",[10,220,221],{},"El problema empieza cuando esas mismas palabras van en todas las tablas. La guía\nde cascada de Supabase describe el comportamiento sin rodeos: cuando se borra una\nfila de la tabla padre, también se borran todas las filas relacionadas de las\ntablas hijas. Y sigue bajando. En el propio ejemplo de esa guía, un único borrado\nen la tabla de arriba vacía las dos tablas que tiene debajo.",[10,223,224],{},"En el club, una cascada le dice al secretario que arranque cada línea con el\nnúmero del socio, y después cada línea de cualquier otro libro que apuntaba a una\nlínea arrancada. Imagina un usuario que escribió publicaciones, con comentarios\nde otras personas que apuntan a ellas. Con cascada en ambas, borrar a ese único\nusuario elimina sus publicaciones y los comentarios de todos los demás en ellas.",[226,227],"diagram",{"alt":228,"caption":229,"src":230},"Tres libros en fila unidos por flechas. En el registro de socios de la izquierda hay una línea tachada. En el libro del medio, las dos líneas con la ficha de ese socio están arrancadas. En el libro de la derecha también están arrancadas las líneas que apuntaban a esas dos, y la mayoría lleva fichas de otros socios en otros colores.","Una cascada sigue cada regla que apunta a la fila borrada, y luego cada regla que apunta a esas. El último libro pierde filas que eran de otras personas.","\u002Fblog\u002Fdatabase-error-deleting-user\u002Fthe-chain-1600x700.png",[10,232,233],{},"Nada te dice qué se fue con él. Auth responde a un borrado correcto con una\nrespuesta vacía, el panel muestra una cuenta menos, y las filas que se fueron en\nese mismo momento faltan la próxima vez que alguien las busca.",[35,235,237],{"id":236},"qué-conserva-cada-tabla","Qué conserva cada tabla",[10,239,240],{},"Decide tabla por tabla, porque cada una guarda algo distinto sobre la persona que\nse fue.",[242,243,244,262],"table",{},[245,246,247],"thead",{},[248,249,250,254,259],"tr",{},[251,252,253],"th",{},"Qué guarda la tabla",[251,255,256],{},[21,257,258],{},"on delete",[251,260,261],{},"Cuando se borra el usuario",[263,264,265,279,292],"tbody",{},[248,266,267,271,276],{},[268,269,270],"td",{},"Solo a la persona: un perfil, sus ajustes",[268,272,273],{},[21,274,275],{},"cascade",[268,277,278],{},"Las filas se van con él",[248,280,281,284,289],{},[268,282,283],{},"Cosas que otros todavía necesitan: un comentario en el hilo de otro, un registro de pago",[268,285,286],{},[21,287,288],{},"set null",[268,290,291],{},"Las filas se quedan, con la columna de dueño vacía",[248,293,294,297,302],{},[268,295,296],{},"Cualquier cosa de la que quieras ocuparte tú primero",[268,298,299,301],{},[21,300,175],{}," (por defecto)",[268,303,304],{},"El borrado se rechaza mientras queden esas filas",[10,306,307,309,310,316,317,320],{},[21,308,288],{}," trae dos condiciones. La columna tiene que admitir valores vacíos, o el\nborrado vuelve a fallar con un mensaje propio,\n",[54,311,313],{"href":312},"\u002Fblog\u002Fnull-value-violates-not-null-constraint",[21,314,315],{},"null value in column … violates not-null constraint",". Y una política escrita como\n",[21,318,319],{},"user_id = auth.uid()"," no volverá a mostrar esa fila a nadie, porque en SQL nada\nes igual a un valor vacío. La fila se conserva, y tu app deja de mostrársela a\ncualquiera.",[10,322,323],{},"Cambiar una regla significa quitarla y volver a añadirla con el comportamiento\nnuevo, que es lo que recomienda la nota de Supabase:",[14,325,328],{"className":326,"code":327,"language":189,"meta":23},[187],"alter table public.profiles\n  drop constraint profiles_id_fkey,\n  add constraint profiles_id_fkey\n    foreign key (id) references auth.users (id) on delete cascade;\n",[21,329,327],{"__ignoreMap":23},[10,331,332],{},"Usa el nombre de regla que imprimió la consulta de arriba, con tu propia tabla y\ncolumna.",[10,334,335,336,339,340,343,344,346],{},"Hay una vía más, y conserva todas las filas. Supabase Auth puede hacer un borrado\nsuave de una cuenta: ",[21,337,338],{},"auth.admin.deleteUser()"," acepta una opción\n",[21,341,342],{},"shouldSoftDelete",", y con ella Auth conserva la fila en ",[21,345,47],{},", la marca\ncomo borrada, revuelve el email y el teléfono, vacía la contraseña y los\nmetadatos, y cierra las sesiones. No se eliminó nada, así que no salta ninguna\nforeign key. Se ejecuta desde un servidor con tu clave secreta, nunca desde el\nnavegador.",[226,348],{"alt":349,"caption":350,"src":351},"El mismo socio borrado dibujado tres veces, un carril para no action, cascade y set null. En el primero una barrera detiene el borrado y tanto la línea del socio como la que apunta a ella se quedan. En el segundo la línea que apunta al socio se arranca con él. En el tercero esa línea se queda, con un hueco discontinuo vacío donde estaba la ficha del socio.","El mismo borrado con tres reglas. La primera se niega y no cambia nada, la segunda se lleva la línea que apunta al socio, y la tercera conserva esa línea con un hueco vacío.","\u002Fblog\u002Fdatabase-error-deleting-user\u002Fthree-outcomes-1600x760.png",[35,353,355],{"id":354},"un-trigger-que-no-alcanza-tu-tabla","Un trigger que no alcanza tu tabla",[10,357,126,358,361,362,364],{},[21,359,360],{},"permission denied for table",", un trigger sobre\n",[21,363,47],{}," se ejecutó durante el borrado e intentó tocar una tabla en la que no\ntiene permiso para entrar.",[10,366,367,368,371,372,374,375,58],{},"El rol con el que trabaja Supabase Auth, ",[21,369,370],{},"supabase_auth_admin",", solo tiene los\npermisos que su propio trabajo necesita. La nota de Supabase lo explica así:\ncuando un trigger que ejecuta ",[21,373,370],{}," actúa fuera del esquema auth,\nprovoca un error de permisos. Un trigger que limpia tras una cuenta borrada, por\nejemplo escribiendo en una tabla de registro tuya, falla exactamente así salvo\nque su función se haya creado con ",[21,376,377],{},"security definer",[10,379,380,381,383,384,387,388,391],{},"El arreglo es el mismo que en el registro: una función ",[21,382,377],{},", creada\npor el rol ",[21,385,386],{},"postgres",", que hace una sola cosa concreta.\n",[54,389,390],{"href":56},"La versión de este error en el registro","\nla recorre línea a línea, incluido el search path vacío que la mantiene segura.",[10,393,394,395,58],{},"Si tu app usa Prisma, lee primero la tercera causa de la nota de Supabase, porque\nnombra a Prisma como una de las formas de romper los permisos sobre\n",[21,396,47],{},[35,398,400],{"id":399},"qué-pasa-con-los-archivos-que-subió","¿Qué pasa con los archivos que subió?",[10,402,403],{},"En un proyecto actual el borrado se completa, y los archivos se quedan donde\nestán.",[10,405,406,407,410,411,414,415,417,418,421,422,425,426,428,429,431],{},"La guía de gestión de usuarios de Supabase todavía advierte que no puedes borrar\na un usuario que es dueño de objetos en Supabase Storage. Era cierto mientras\n",[21,408,409],{},"storage.objects",", la tabla donde Storage guarda una fila por cada archivo, tenía\nuna foreign key llamada ",[21,412,413],{},"objects_owner_fkey"," hacia ",[21,416,47],{},". Storage ya quitó\nesa clave en sus propias migraciones de esquema\n(",[21,419,420],{},"0017-drop-owner-foreign-key.sql","), y su guía sobre la propiedad de archivos dice\nahora que el dueño se guarda en una columna ",[21,423,424],{},"owner_id",". Si la consulta de antes\nlista ",[21,427,409],{}," con ",[21,430,413],{},", tu proyecto todavía tiene la\nregla antigua y la advertencia de la guía te afecta.",[10,433,434,435,438,439,58],{},"Si no, cada archivo que la persona subió se queda en su bucket cuando ella ya no\nestá. En el club, los abrigos siguen colgados en sus perchas con una ficha que ya\nno tiene nadie. Una política escrita como ",[21,436,437],{},"owner_id = auth.uid()::text"," no volverá\na coincidir con ellos, y un archivo de un bucket público se sigue sirviendo a\ncualquiera que tenga su enlace. Si un desconocido puede listar los archivos de\nuno de tus buckets es una de las comprobaciones del\n",[54,440,442],{"href":441},"\u002Fsecurity-scanner","escaneo gratuito de Reeve",[10,444,445,446,58],{},"Para quitarlos, ve a través del propio Storage, desde la página de Storage del\npanel o con la API de Storage. Un borrado SQL directo sobre sus tablas se\nrechaza, y Storage dice por qué: \"Direct deletion from storage tables is not\nallowed. Use the Storage API instead.\" Qué guarda una copia de la base de datos\nde esos archivos, y qué deja fuera, está en\n",[54,447,449],{"href":448},"\u002Fblog\u002Fsupabase-storage-backup","cómo hacer backup de Supabase Storage",[35,451,453],{"id":452},"haz-una-copia-antes-de-cambiar-una-foreign-key","Haz una copia antes de cambiar una foreign key",[10,455,456],{},"Postgres no tiene papelera, y una cascada borra filas de tablas que no estabas\nmirando.",[10,458,459,460,462,463,467,468,472],{},"Lo que cambia una cascada o un ",[21,461,288],{}," es definitivo en cuanto el borrado se\ncompleta, y la única vuelta atrás es una copia hecha antes. El plan gratuito de\nSupabase no guarda ningún backup. Pro guarda 7 días de copias diarias, Team 14 y\nEnterprise hasta 30, y todas viven dentro del mismo proyecto de Supabase.\n",[54,464,466],{"href":465},"\u002Fblog\u002Fthree-ways-to-back-up-a-supabase-database","Copiar tu base de datos por tu cuenta","\nlleva unos pocos comandos, y\n",[54,469,471],{"href":470},"\u002Fblog\u002Fsupabase-backup-auth-users","una copia que incluya a tus usuarios"," necesita\nel tercero de los tres comandos que publica Supabase.",[10,474,475],{},"Después prueba las reglas nuevas con una cuenta de prueba antes de tocar una real.\nCrea una cuenta, dale una fila en cada tabla que listó la consulta, bórrala y\nmira qué queda en cada tabla.",[10,477,478,479,483],{},"Si prefieres que esa copia ya exista antes de que alguien pulse borrar, para eso\nestán los ",[54,480,482],{"href":481},"\u002Fsupabase-backups","backups de Supabase"," de Reeve.",[35,485,487],{"id":486},"una-vuelta-atrás-cuando-un-borrado-se-lleva-más-de-lo-previsto","Una vuelta atrás cuando un borrado se lleva más de lo previsto",[10,489,490,493],{},[83,491,492],{},"Una cascada no informa de nada de lo que eliminó."," Te enteras cuando alguien\npregunta dónde están sus comentarios.",[10,495,496],{},[83,497,498,501],{},[54,499,500],{"href":481},"Reeve Care"," guarda una copia de tu base de datos de\nSupabase de antes del borrado:",[102,503,504,507,510,513,516],{},[79,505,506],{},"una copia cifrada según un calendario, guardada en el almacenamiento propio de Reeve en la UE, fuera de tu cuenta de Supabase",[79,508,509],{},"cada copia verificada antes de contar, contando las filas de cada tabla",[79,511,512],{},"una restauración en un clic que guarda lo que hay ahora antes de reemplazarlo",[79,514,515],{},"también tus archivos subidos, en cuanto conectes una credencial de Storage",[79,517,518,519,523],{},"todo lo que hace ",[54,520,522],{"href":521},"\u002Fwebsite-monitoring","Reeve Monitor",": las nueve comprobaciones de tu app en producción cada hora, y una comprobación de disponibilidad cada 60 segundos",[35,525,527],{"id":526},"qué-hacer-hoy","Qué hacer hoy",[529,530,531],"key-takeaways",{},[102,532,533,536,539,548,551],{},[79,534,535],{},"Intenta el borrado una vez, apunta la hora y lee la línea del log de Postgres de ese momento antes de cambiar nada.",[79,537,538],{},"Si nombra una foreign key, ejecuta la consulta de arriba para listar cada tabla que apunta a tus usuarios y qué hace cada una en un borrado.",[79,540,541,542,544,545,547],{},"Decide tabla por tabla: ",[21,543,275],{}," para filas que solo describen a la persona, ",[21,546,288],{}," para filas que otros todavía necesitan, el comportamiento por defecto para lo que quieras quitar a mano.",[79,549,550],{},"Haz una copia de la base de datos antes de cambiar cualquiera de esas reglas, y prueba las reglas nuevas primero con una cuenta de prueba.",[79,552,553],{},"Quita también los archivos de la persona a través de Storage. En un proyecto actual la cuenta se va y los archivos se quedan.",[10,555,556,557,561],{},"Si ya se han ido más filas de las que querías,\n",[54,558,560],{"href":559},"\u002Fblog\u002Fai-agent-deleted-my-database","qué puedes recuperar después de un borrado","\nrepasa dónde mirar, en orden.",{"title":23,"searchDepth":563,"depth":563,"links":564},3,[565,567,568,569,570,571,572,573,574,575],{"id":37,"depth":566,"text":38},2,{"id":67,"depth":566,"text":68},{"id":122,"depth":566,"text":123},{"id":208,"depth":566,"text":209},{"id":236,"depth":566,"text":237},{"id":354,"depth":566,"text":355},{"id":399,"depth":566,"text":400},{"id":452,"depth":566,"text":453},{"id":486,"depth":566,"text":487},{"id":526,"depth":566,"text":527},"Backups","\u002Fblog\u002Fdatabase-error-deleting-user\u002Fcover-1200x630.png","Un registro de socios con una línea tachada y, detrás, dos libros más de los que se arrancaron las líneas con la misma ficha.","\"Database error deleting user\" en Supabase: tu base de datos rechazó el borrado. La causa está en tus logs y el arreglo decide qué más se borra.",false,"md",[583,585,588,591,594],{"q":38,"a":584},"Significa que Supabase Auth intentó quitar la fila de esa persona de auth.users, algo en tu base de datos se negó, y todo el borrado se deshizo. La cuenta queda exactamente como estaba. El mensaje no nombra ninguna tabla, y la línea que sí la nombra está en los logs de Postgres de tu proyecto en ese mismo momento.",{"q":586,"a":587},"¿Debo añadir on delete cascade para arreglarlo?","En una tabla que solo describe a la persona, como una tabla de perfiles, sí, y la propia guía de Supabase hace exactamente eso. En una tabla con cosas que otras personas todavía necesitan, como comentarios o pedidos, decide antes. Cascade borra cada fila que apunta al usuario y cada fila que apunta a esas, en cada borrado a partir de entonces, y nada te informa de lo que eliminó.",{"q":589,"a":590},"¿Puedo borrar un usuario y conservar sus filas?","Sí, de dos formas. Pon la foreign key en on delete set null y las filas se quedan con la columna de dueño vacía, lo que exige que esa columna admita valores vacíos. O llama a auth.admin.deleteUser() desde un servidor con la opción shouldSoftDelete: Supabase Auth conserva la fila, la marca como borrada, revuelve el email y el teléfono y vacía la contraseña, así que no se toca nada de lo que apunta a ella.",{"q":592,"a":593},"¿Por qué dice Supabase que no puedo borrar a un usuario que subió archivos?","Su guía de gestión de usuarios todavía lo dice, y era cierto mientras storage.objects tenía una foreign key llamada objects_owner_fkey hacia auth.users. Supabase Storage ya quitó esa clave en sus propias migraciones de esquema. En un proyecto actual el borrado se completa y los archivos se quedan en su bucket, con un owner_id que ya no tiene ningún usuario.",{"q":595,"a":596},"¿Borrar un usuario cierra su sesión?","No en el acto. Según la guía de Supabase, un token de acceso ya emitido sigue siendo válido hasta que caduca, incluso después de borrar la cuenta. Borrar al usuario elimina sus sesiones, así que la app no puede conseguir un token nuevo, pero el que ya tiene funciona hasta su hora de caducidad.","\u002Fblog\u002Fdatabase-error-deleting-user\u002Fcard-800x500.png",[599,600,601,602,603,604,605],"database error deleting user supabase","database error deleting user","failed to delete user database error deleting user","supabase borrar usuario foreign key","supabase on delete cascade auth users","supabase no puedo borrar usuario storage","update or delete on table users violates foreign key constraint",{},true,"\"Database error deleting user\" en Supabase","\u002Fblog\u002Fdatabase-error-deleting-user","2026-10-30",{"title":5,"description":579},"blog\u002Fdatabase-error-deleting-user",[614,615,616],"\"Database error deleting user\" en Supabase significa que el borrado llegó a tu base de datos y fue rechazado, así que no se eliminó nada. La línea que dice la causa está en tus logs de Postgres, y la propia nota de troubleshooting de Supabase te manda allí.","Cuando esa línea dice que se viola una foreign key, una fila de alguna de tus tablas todavía guarda el id del usuario. Postgres no elimina a una persona mientras algo siga apuntando a ella, salvo que le hayas dicho qué hacer con esas filas.","La primera respuesta que encontrarás es on delete cascade. Borra cada fila que apunta al usuario, y cada fila que apunta a esas, en cada borrado a partir de ese momento. Decide tabla por tabla y haz una copia antes de cambiar una regla.","r_ravlmdoJbjR07gOBDJ07eNqPKOEFe1G4sMsB7MtkQ",[619,626,632,637,642,648,654,660,666,672,678,679,685,691,697,703,709,715,721,727,733,739,745,751,757,763,769,774,780,786,792,798,804,810,816,822,828,834,840,846,852,858,864,870,876,882,887,893,899,904,910,916,922,928,934,940,946,952,957,963,969,975,981,987,993,999,1005,1011,1017,1023,1029,1035,1041,1047,1053,1059,1065,1071,1077,1083,1089,1095,1100,1106,1111,1117,1123,1129,1134,1139],{"path":620,"title":621,"description":622,"published":623,"category":624,"image":625,"draft":580},"\u002Fblog\u002Fmax-client-connections-reached","Supabase \"max client connections reached\", explicado","Supabase \"max client connections reached\": el pooler no tiene sitio para un cliente más. El puerto de tu cadena de conexión decide el límite.","2026-11-09","Fundamentos de seguridad","\u002Fblog\u002Fmax-client-connections-reached\u002Fcard-800x500.png",{"path":627,"title":628,"description":629,"published":630,"category":624,"image":631,"draft":580},"\u002Fblog\u002Funsupported-provider-provider-is-not-enabled","Error de Supabase: \"Unsupported provider: provider is not enabled\"","\"Unsupported provider: provider is not enabled\": Google está desactivado en tu proyecto Supabase. Cómo activarlo y arreglar redirect_uri_mismatch.","2026-11-08","\u002Fblog\u002Funsupported-provider-provider-is-not-enabled\u002Fcard-800x500.png",{"path":164,"title":633,"description":634,"published":635,"category":624,"image":636,"draft":580},"Error de Supabase \"violates foreign key constraint\" al insertar","Supabase \"insert or update on table violates foreign key constraint\": tu fila apunta a una fila que no existe. Cuál, por qué, y cómo arreglarlo.","2026-11-07","\u002Fblog\u002Fviolates-foreign-key-constraint\u002Fcard-800x500.png",{"path":312,"title":638,"description":639,"published":640,"category":624,"image":641,"draft":580},"Error de Supabase \"null value in column violates not-null constraint\"","Supabase \"null value in column violates not-null constraint\": una columna obligatoria llegó vacía. De dónde vino el null, y cómo arreglarlo.","2026-11-06","\u002Fblog\u002Fnull-value-violates-not-null-constraint\u002Fcard-800x500.png",{"path":643,"title":644,"description":645,"published":646,"category":624,"image":647,"draft":580},"\u002Fblog\u002Fauth-session-missing","Supabase \"Auth session missing!\": adónde fue la sesión","\"Auth session missing!\" es supabase-js sin encontrar ninguna sesión iniciada y sin llegar a preguntar a Supabase. Adónde fue la sesión, y el arreglo.","2026-11-05","\u002Fblog\u002Fauth-session-missing\u002Fcard-800x500.png",{"path":649,"title":650,"description":651,"published":652,"category":624,"image":653,"draft":580},"\u002Fblog\u002Finvalid-claim-missing-sub-claim","Supabase \"invalid claim: missing sub claim\": no hay nadie conectado","\"invalid claim: missing sub claim\": Supabase Auth recibió un token auténtico sin ningún usuario, la clave del proyecto en lugar de una sesión.","2026-11-04","\u002Fblog\u002Finvalid-claim-missing-sub-claim\u002Fcard-800x500.png",{"path":655,"title":656,"description":657,"published":658,"category":624,"image":659,"draft":580},"\u002Fblog\u002Ffailed-to-send-a-request-to-the-edge-function","\"Failed to send a request to the Edge Function\": la causa real","\"Failed to send a request to the Edge Function\": el navegador no recibió una respuesta legible. Su consola dice por qué y si tu función se ejecutó.","2026-11-03","\u002Fblog\u002Ffailed-to-send-a-request-to-the-edge-function\u002Fcard-800x500.png",{"path":661,"title":662,"description":663,"published":664,"category":624,"image":665,"draft":580},"\u002Fblog\u002Fsupabase-auth-redirects-to-localhost","Por qué los enlaces de email de Supabase redirigen a localhost:3000","Si Supabase redirige a localhost:3000, la Site URL nunca se cambió o tu dirección falta en Redirect URLs. La solución, y el comodín que debes evitar.","2026-11-02","\u002Fblog\u002Fsupabase-auth-redirects-to-localhost\u002Fcard-800x500.png",{"path":667,"title":668,"description":669,"published":670,"category":624,"image":671,"draft":580},"\u002Fblog\u002Femail-link-is-invalid-or-has-expired","Supabase \"Email link is invalid or has expired\" con un enlace nuevo","Con un enlace enviado hace un minuto, \"Email link is invalid or has expired\" en Supabase suele indicar que un filtro de correo lo usó antes. Cómo arreglarlo.","2026-11-01","\u002Fblog\u002Femail-link-is-invalid-or-has-expired\u002Fcard-800x500.png",{"path":673,"title":674,"description":675,"published":676,"category":624,"image":677,"draft":580},"\u002Fblog\u002Fcanceling-statement-due-to-statement-timeout","Supabase \"canceling statement due to statement timeout\", explicado","Supabase \"canceling statement due to statement timeout\": una consulta superó el límite de su rol, 3 segundos para un visitante. Revisa antes la política RLS.","2026-10-31","\u002Fblog\u002Fcanceling-statement-due-to-statement-timeout\u002Fcard-800x500.png",{"path":609,"title":5,"description":579,"published":610,"category":576,"image":597,"draft":580},{"path":680,"title":681,"description":682,"published":683,"category":624,"image":684,"draft":580},"\u002Fblog\u002Fstripe-webhook-no-signatures-found","\"No signatures found matching the expected signature\" en Supabase","¿Tu webhook de Stripe en una Edge Function de Supabase dice \"No signatures found matching the expected signature\"? Tiene dos causas. Sigue verificando.","2026-10-29","\u002Fblog\u002Fstripe-webhook-no-signatures-found\u002Fcard-800x500.png",{"path":686,"title":687,"description":688,"published":689,"category":624,"image":690,"draft":580},"\u002Fblog\u002Fsupabase-invalid-login","\"Invalid login credentials\" en Supabase: las tres causas","Supabase dice \"Invalid login credentials\" ante una contraseña errónea, un email desconocido y una cuenta de Google. Cómo saber cuál es.","2026-10-28","\u002Fblog\u002Fsupabase-invalid-login\u002Fcard-800x500.png",{"path":692,"title":693,"description":694,"published":695,"category":624,"image":696,"draft":580},"\u002Fblog\u002Fedge-function-returned-a-non-2xx-status-code","\"Edge Function returned a non-2xx status code\": el error real","\"Edge Function returned a non-2xx status code\" significa que tu función de Supabase respondió con un error. Aquí ves dónde leer el real.","2026-10-27","\u002Fblog\u002Fedge-function-returned-a-non-2xx-status-code\u002Fcard-800x500.png",{"path":698,"title":699,"description":700,"published":701,"category":624,"image":702,"draft":580},"\u002Fblog\u002Fcertificate-not-trusted","Certificado no confiable: tres causas aparte de la caducidad","¿Certificado no confiable aunque la fecha está bien? Nombra otra dirección, se firmó a sí mismo o a su cadena le falta un eslabón. Cómo saber cuál.","2026-10-26","\u002Fblog\u002Fcertificate-not-trusted\u002Fcard-800x500.png",{"path":704,"title":705,"description":706,"published":707,"category":624,"image":708,"draft":580},"\u002Fblog\u002Finvalid-input-syntax-for-type-uuid","Supabase \"invalid input syntax for type uuid\": lee las comillas","Supabase \"invalid input syntax for type uuid\" significa que tu app envió algo que no es un id. Lee las comillas, frena la consulta, conserva la columna.","2026-10-25","\u002Fblog\u002Finvalid-input-syntax-for-type-uuid\u002Fcard-800x500.png",{"path":710,"title":711,"description":712,"published":713,"category":624,"image":714,"draft":580},"\u002Fblog\u002Fduplicate-key-value-violates-unique-constraint","Supabase \"duplicate key value violates unique constraint\", explicado","Supabase \"duplicate key value violates unique constraint\": el valor ya está ocupado. Encuentra la columna, elige un arreglo y aparta el valor de la pantalla.","2026-10-24","\u002Fblog\u002Fduplicate-key-value-violates-unique-constraint\u002Fcard-800x500.png",{"path":716,"title":717,"description":718,"published":719,"category":624,"image":720,"draft":580},"\u002Fblog\u002Fsupabase-relation-does-not-exist","Supabase \"relation does not exist\" aunque la tabla está ahí","Supabase \"relation does not exist\" o PGRST205: ninguna tabla tiene ese nombre en ese esquema. Cuatro comprobaciones, y lo que abre exponer un esquema.","2026-10-23","\u002Fblog\u002Fsupabase-relation-does-not-exist\u002Fcard-800x500.png",{"path":722,"title":723,"description":724,"published":725,"category":624,"image":726,"draft":580},"\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\" muestra los nombres de argumentos que envió tu app. Compáralos con la función.","2026-10-22","\u002Fblog\u002Fsupabase-function-not-found\u002Fcard-800x500.png",{"path":728,"title":729,"description":730,"published":731,"category":624,"image":732,"draft":580},"\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\" suele ser un nombre que no coincide. Cuatro comprobaciones y la línea que recarga.","2026-10-21","\u002Fblog\u002Fsupabase-schema-cache-column-error\u002Fcard-800x500.png",{"path":734,"title":735,"description":736,"published":737,"category":624,"image":738,"draft":580},"\u002Fblog\u002Fsupabase-email-rate-limit-exceeded","Supabase \"Email Rate Limit Exceeded\": dos límites, un solo código","Supabase \"Email Rate Limit Exceeded\" limita cada email que envía tu proyecto: 2 por hora con el remitente integrado. Qué cuenta y cómo subirlo.","2026-10-20","\u002Fblog\u002Fsupabase-email-rate-limit-exceeded\u002Fcard-800x500.png",{"path":740,"title":741,"description":742,"published":743,"category":624,"image":744,"draft":580},"\u002Fblog\u002Fsupabase-users-logged-out","Supabase \"Invalid Refresh Token: Refresh Token Not Found\": 3 causas","Supabase \"Invalid Refresh Token: Refresh Token Not Found\" cierra la sesión de tus usuarios. Tres causas, y cómo las separa el log de Auth.","2026-10-19","\u002Fblog\u002Fsupabase-users-logged-out\u002Fcard-800x500.png",{"path":746,"title":747,"description":748,"published":749,"category":624,"image":750,"draft":580},"\u002Fblog\u002Fpermission-denied-for-schema-public","Supabase \"permission denied for schema public\": tres causas","Supabase \"permission denied for schema public\" es un grant que falta en el esquema. Qué lo quitó y la línea que lo devuelve.","2026-10-18","\u002Fblog\u002Fpermission-denied-for-schema-public\u002Fcard-800x500.png",{"path":752,"title":753,"description":754,"published":755,"category":624,"image":756,"draft":580},"\u002Fblog\u002Fsupabase-bucket-not-found","Supabase \"Bucket not found\": las tres cosas que significa","Supabase \"Bucket not found\" es una de tres cosas: un enlace público a un bucket privado, un nombre que no coincide exactamente o el proyecto equivocado.","2026-10-17","\u002Fblog\u002Fsupabase-bucket-not-found\u002Fcard-800x500.png",{"path":758,"title":759,"description":760,"published":761,"category":624,"image":762,"draft":580},"\u002Fblog\u002Fsupabase-invalid-jwt","Supabase invalid JWT: ¿qué clave dejó de funcionar?","Un error Supabase invalid JWT es PGRST301: un token firmado no pasó la verificación. En un proyecto nuevo se trata de la sesión, no de la clave de API.","2026-10-16","\u002Fblog\u002Fsupabase-invalid-jwt\u002Fcard-800x500.png",{"path":764,"title":765,"description":766,"published":767,"category":624,"image":768,"draft":580},"\u002Fblog\u002Fsupabase-single-row-error","Supabase \"multiple (or no) rows returned\" casi siempre es ninguna","Supabase devuelve PGRST116, \"JSON object requested, multiple (or no) rows returned\", cuando tu consulta no encontró nada. Una fila oculta parece ausente.","2026-10-15","\u002Fblog\u002Fsupabase-single-row-error\u002Fcard-800x500.png",{"path":56,"title":770,"description":771,"published":772,"category":624,"image":773,"draft":580},"\"Database error saving new user\" en Supabase: dónde mirar","\"Database error saving new user\" significa que algo unido a tu registro de Supabase falló y se deshizo entero. El error real está en tus logs.","2026-10-14","\u002Fblog\u002Fdatabase-error-saving-new-user\u002Fcard-800x500.png",{"path":775,"title":776,"description":777,"published":778,"category":624,"image":779,"draft":580},"\u002Fblog\u002Fwhat-is-supabase","¿Qué es Supabase? La base de datos que eligió tu builder","Qué es Supabase: la base de datos, el login y el almacenamiento de archivos de tu app. Supabase lleva los servidores; las reglas sobre tus filas son tuyas.","2026-10-13","\u002Fblog\u002Fwhat-is-supabase\u002Fcard-800x500.png",{"path":781,"title":782,"description":783,"published":784,"category":624,"image":785,"draft":580},"\u002Fblog\u002Faws-access-key-exposed-in-frontend","Una clave AWS expuesta en tu frontend no es como una clave OpenAI","Una clave de acceso de AWS expuesta en tu frontend lee y escribe tu almacenamiento. La solución suele ser una presigned URL, no una clave nueva.","2026-10-12","\u002Fblog\u002Faws-access-key-exposed-in-frontend\u002Fcard-800x500.png",{"path":787,"title":788,"description":789,"published":790,"category":624,"image":791,"draft":580},"\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":793,"title":794,"description":795,"published":796,"category":624,"image":797,"draft":580},"\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":799,"title":800,"description":801,"published":802,"category":624,"image":803,"draft":580},"\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":805,"title":806,"description":807,"published":808,"category":624,"image":809,"draft":580},"\u002Fblog\u002Finfinite-recursion-in-policy-for-relation","\"Infinite recursion detected in policy\" sin desactivar RLS","\"Infinite recursion detected in policy for relation\" significa que tu policy preguntó a la tabla que protege. Así se rompe el círculo.","2026-10-08","\u002Fblog\u002Finfinite-recursion-in-policy-for-relation\u002Fcard-800x500.png",{"path":811,"title":812,"description":813,"published":814,"category":624,"image":815,"draft":580},"\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":817,"title":818,"description":819,"published":820,"category":624,"image":821,"draft":580},"\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":823,"title":824,"description":825,"published":826,"category":624,"image":827,"draft":580},"\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":829,"title":830,"description":831,"published":832,"category":624,"image":833,"draft":580},"\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":835,"title":836,"description":837,"published":838,"category":624,"image":839,"draft":580},"\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":841,"title":842,"description":843,"published":844,"category":624,"image":845,"draft":580},"\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":847,"title":848,"description":849,"published":850,"category":624,"image":851,"draft":580},"\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":853,"title":854,"description":855,"published":856,"category":624,"image":857,"draft":580},"\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":859,"title":860,"description":861,"published":862,"category":624,"image":863,"draft":580},"\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":865,"title":866,"description":867,"published":868,"category":576,"image":869,"draft":580},"\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","\u002Fblog\u002Ftest-your-supabase-backup\u002Fcard-800x500.png",{"path":871,"title":872,"description":873,"published":874,"category":576,"image":875,"draft":580},"\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":877,"title":878,"description":879,"published":880,"category":576,"image":881,"draft":580},"\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":883,"title":884,"description":885,"published":880,"category":624,"image":886,"draft":580},"\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":888,"title":889,"description":890,"published":891,"category":624,"image":892,"draft":580},"\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":894,"title":895,"description":896,"published":897,"category":624,"image":898,"draft":580},"\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":470,"title":900,"description":901,"published":902,"category":576,"image":903,"draft":580},"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":905,"title":906,"description":907,"published":908,"category":624,"image":909,"draft":580},"\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":911,"title":912,"description":913,"published":914,"category":624,"image":915,"draft":580},"\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":917,"title":918,"description":919,"published":920,"category":624,"image":921,"draft":580},"\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":923,"title":924,"description":925,"published":926,"category":624,"image":927,"draft":580},"\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":929,"title":930,"description":931,"published":932,"category":624,"image":933,"draft":580},"\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":935,"title":936,"description":937,"published":938,"category":624,"image":939,"draft":580},"\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":941,"title":942,"description":943,"published":944,"category":624,"image":945,"draft":580},"\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":947,"title":948,"description":949,"published":950,"category":624,"image":951,"draft":580},"\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":448,"title":953,"description":954,"published":955,"category":576,"image":956,"draft":580},"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":958,"title":959,"description":960,"published":961,"category":576,"image":962,"draft":580},"\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":964,"title":965,"description":966,"published":967,"category":576,"image":968,"draft":580},"\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":970,"title":971,"description":972,"published":973,"category":624,"image":974,"draft":580},"\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":976,"title":977,"description":978,"published":979,"category":624,"image":980,"draft":580},"\u002Fblog\u002Fenable-rls-on-every-supabase-table","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":982,"title":983,"description":984,"published":985,"category":624,"image":986,"draft":580},"\u002Fblog\u002Fsupabase-rls-disabled-in-public","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":988,"title":989,"description":990,"published":991,"category":624,"image":992,"draft":580},"\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":994,"title":995,"description":996,"published":997,"category":624,"image":998,"draft":580},"\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. Ocho herramientas, con precios.","2026-09-07","\u002Fblog\u002Fvibe-coding-security-scanners-compared\u002Fcard-800x500.png",{"path":1000,"title":1001,"description":1002,"published":1003,"category":624,"image":1004,"draft":580},"\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":1006,"title":1007,"description":1008,"published":1009,"category":624,"image":1010,"draft":580},"\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":1012,"title":1013,"description":1014,"published":1015,"category":624,"image":1016,"draft":580},"\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":1018,"title":1019,"description":1020,"published":1021,"category":624,"image":1022,"draft":580},"\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":1024,"title":1025,"description":1026,"published":1027,"category":624,"image":1028,"draft":580},"\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":1030,"title":1031,"description":1032,"published":1033,"category":576,"image":1034,"draft":580},"\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":1036,"title":1037,"description":1038,"published":1039,"category":624,"image":1040,"draft":580},"\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":1042,"title":1043,"description":1044,"published":1045,"category":576,"image":1046,"draft":580},"\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":1048,"title":1049,"description":1050,"published":1051,"category":624,"image":1052,"draft":580},"\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":1054,"title":1055,"description":1056,"published":1057,"category":624,"image":1058,"draft":580},"\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":1060,"title":1061,"description":1062,"published":1063,"category":624,"image":1064,"draft":580},"\u002Fblog\u002Fnew-row-violates-row-level-security-policy","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":1066,"title":1067,"description":1068,"published":1069,"category":624,"image":1070,"draft":580},"\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":1072,"title":1073,"description":1074,"published":1075,"category":576,"image":1076,"draft":580},"\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":1078,"title":1079,"description":1080,"published":1081,"category":576,"image":1082,"draft":580},"\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":1084,"title":1085,"description":1086,"published":1087,"category":624,"image":1088,"draft":580},"\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":1090,"title":1091,"description":1092,"published":1093,"category":624,"image":1094,"draft":580},"\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":559,"title":1096,"description":1097,"published":1098,"category":576,"image":1099,"draft":580},"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":1101,"title":1102,"description":1103,"published":1104,"category":624,"image":1105,"draft":580},"\u002Fblog\u002Fcan-anyone-read-your-supabase-database","¿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":1107,"title":1108,"description":1109,"published":1104,"category":624,"image":1110,"draft":580},"\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":1112,"title":1113,"description":1114,"published":1115,"category":624,"image":1116,"draft":580},"\u002Fblog\u002Fsupabase-new-api-keys","Supabase publishable key: ¿es la misma que la anon key?","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":1118,"title":1119,"description":1120,"published":1121,"category":576,"image":1122,"draft":580},"\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":1124,"title":1125,"description":1126,"published":1127,"category":624,"image":1128,"draft":580},"\u002Fblog\u002Fsupabase-rls-on-but-table-still-public","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":1130,"title":1131,"description":1132,"published":1127,"category":576,"image":1133,"draft":580},"\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":465,"title":1135,"description":1136,"published":1137,"category":576,"image":1138,"draft":580},"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":1140,"title":1141,"description":1142,"published":1137,"category":624,"image":1143,"draft":580},"\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",1794213644965]