[{"data":1,"prerenderedAt":1090},["ShallowReactive",2],{"blog-es-database-error-saving-new-user":3,"blog-index-es":564},{"id":4,"title":5,"body":6,"category":521,"cover":522,"coverAlt":523,"description":524,"draft":525,"extension":526,"faq":527,"image":543,"keywords":544,"meta":552,"navigation":553,"ogTitle":554,"path":555,"published":556,"seo":557,"stem":558,"tldr":559,"updated":556,"__hash__":563},"blog_es\u002Fblog\u002Fdatabase-error-saving-new-user.md","\"Database error saving new user\" en Supabase: dónde mirar",{"type":7,"value":8,"toc":508},"minimark",[9,13,24,27,35,40,43,57,60,63,76,82,86,89,92,95,120,132,138,142,149,159,162,165,169,175,182,196,201,207,211,217,220,226,237,244,257,261,264,273,285,310,316,326,337,348,366,370,373,379,399,402,406,412,421,436,446,463,467,495],[10,11,12],"p",{},"Alguien intenta crear una cuenta en tu app y recibe una respuesta que no va sobre\nél:",[14,15,20],"pre",{"className":16,"code":18,"language":19},[17],"language-text","Database error saving new user\n","text",[21,22,18],"code",{"__ignoreMap":23},"",[10,25,26],{},"Nadie puede registrarse. El formulario funcionaba cuando lo construiste, tu\nproyecto está arriba, y lo único que tienes es una frase que no nombra ninguna\ntabla, ninguna columna ni ninguna razón.",[10,28,29,30,34],{},"Aquí está la parte que guía tras guía se equivoca: ",[31,32,33],"strong",{},"este mensaje está hecho para\nno decirte nada, y tu error real ya está anotado en otro sitio."," Busca la cadena\ny te darán un bloque de SQL para pegar, normalmente la versión de alguien de una\nfunción que escribe en una tabla con los nombres de columna de esa persona.\nPegarlo o funciona, o te suma un segundo problema al primero. La línea que dice\nqué falló está en los logs de tu propio proyecto, y nombra la tabla.",[36,37,39],"h2",{"id":38},"qué-significa-database-error-saving-new-user","Qué significa \"Database error saving new user\"",[10,41,42],{},"Supabase aceptó la cuenta nueva y después la deshizo, porque algo unido al\nregistro falló.",[10,44,45,46,49,50,53,54,56],{},"Un registro escribe una fila, en una tabla llamada ",[21,47,48],{},"auth.users"," que Supabase\ngestiona por ti. La mayoría de los proyectos une algo a esa escritura: un trigger\nque copia a la persona nueva a una tabla ",[21,51,52],{},"profiles"," tuya, una constraint, una\nclave foránea. Cuando eso que está unido lanza un error, la base de datos deshace\nla operación entera, y eso incluye la fila en ",[21,55,48],{},". No queda ninguna\ncuenta a medio hacer. Tampoco hay nada en la respuesta que diga cuál de esas\ncosas unidas fue.",[10,58,59],{},"Piensa en un mostrador con una trastienda detrás. Alguien firma el libro en el\nmostrador, y un empleado se lleva el nombre nuevo atrás para copiarlo en un\nsegundo libro. Si el empleado no puede entrar en la sala donde se guarda ese\nlibro, la inscripción no vale: la línea del mostrador se tacha y a la persona se\nle dice que ha habido un problema. Lo que nadie le dice es que el problema era\nuna puerta cerrada a doce metros.",[10,61,62],{},"La vaguedad es deliberada, y es la decisión correcta. Tu formulario de registro\nestá abierto a internet. Un mensaje que nombrase tus tablas le estaría\ndescribiendo tu base de datos a cualquiera dispuesto a teclear un correo en él.",[10,64,65,66,69,70,75],{},"Si la respuesta dice ",[21,67,68],{},"email rate limit exceeded",", el problema no es tu base de\ndatos: Supabase se negó a enviar el correo de confirmación, y\n",[71,72,74],"a",{"href":73},"\u002Fblog\u002Fsupabase-email-rate-limit-exceeded","ese error tiene su propia página",".",[77,78],"diagram",{"alt":79,"caption":80,"src":81},"A la izquierda un libro con filas y la más reciente tachada, una flecha hacia la derecha que lleva a una figura, y el camino de esa figura hacia un segundo libro cortado por una barra ámbar. Un panel aparte al lado muestra tres líneas anotadas, una de ellas destacada.","La fila de auth.users se escribe y se vuelve a sacar, así que del fallo no sobrevive nada salvo una línea en un log que la respuesta nunca menciona.","\u002Fblog\u002Fdatabase-error-saving-new-user\u002Fthe-rollback-1600x660.png",[36,83,85],{"id":84},"dónde-está-el-error-real","¿Dónde está el error real?",[10,87,88],{},"En los logs de tu proyecto, en dos pasos, y la propia guía de resolución de\nSupabase da el orden.",[10,90,91],{},"Reprodúcelo una vez antes de ponerte a buscar, para tener una hora con la que\ncuadrar. Intenta un registro en tu app en producción, deja que falle y apunta el\nmomento.",[10,93,94],{},"Después, en el panel de Supabase:",[96,97,98,110],"ol",{},[99,100,101,102,105,106,109],"li",{},"Abre ",[31,103,104],{},"Logs"," y lee los logs de ",[31,107,108],{},"Auth",". Busca la entrada de esa hora. Lleva\nel error que Auth anotó por su cuenta, que es más concreto que la frase que\nrecibió tu app.",[99,111,112,113,115,116,119],{},"Si esa entrada apunta a la base de datos, vuelve a ",[31,114,104],{}," y abre los de\n",[31,117,118],{},"Postgres",". La línea del mismo momento es tu error real, escrito en el\nvocabulario corriente de Postgres: una columna, una relación, un tipo, un\npermiso.",[10,121,122,123,125,126,128,129,131],{},"La nota de resolución de Supabase para este mensaje enumera tres causas. Tienes\nun trigger o una función de trigger sobre la tabla ",[21,124,48],{}," que tiene un\nerror. Has añadido sobre ",[21,127,48],{}," una constraint que no se cumple. O estás\nusando Prisma y ha roto los permisos sobre ",[21,130,48],{},". De esa misma nota sale\nel recorrido de dos pasos por los logs de arriba.",[10,133,134,135,137],{},"En una app hecha con Lovable, Bolt, Replit o v0, la primera causa cubre la\nmayoría, porque copiar un registro nuevo a una tabla ",[21,136,52],{}," es el patrón al\nque recurre cualquier builder, y un trigger es como se hace.",[36,139,141],{"id":140},"causa-1-el-trigger-no-alcanza-tu-tabla","Causa 1: el trigger no alcanza tu tabla",[10,143,144,145,148],{},"El rol de base de datos que ejecuta tu trigger no puede salir del esquema ",[21,146,147],{},"auth",",\ny tu trigger escribe fuera de él.",[10,150,151,152,155,156,158],{},"Supabase lo explica en su propio foro de discusión, y la formulación conviene\ntenerla exacta. El rol de base de datos que gestiona la autenticación,\n",[21,153,154],{},"supabase_auth_admin",", solo tiene los permisos que necesita para sus propias\ntareas, así que cuando un trigger operado por ",[21,157,154],{}," interactúa\nfuera del esquema auth, eso provoca un error de permisos.",[10,160,161],{},"Esa es la credencial del empleado. Abre la oficina de delante, porque ahí está su\ntrabajo. El segundo libro está en una sala para la que esa credencial nunca se\nemitió, y una puerta cerrada no informa de por qué lo está.",[10,163,164],{},"En el log de Postgres esto llega como un error de permisos que nombra la tabla en\nla que tu trigger estaba escribiendo.",[36,166,168],{"id":167},"la-solución-y-lo-que-cede","La solución, y lo que cede",[10,170,171,174],{},[21,172,173],{},"security definer"," en la función, que la hace correr con los privilegios de quien\nla creó en lugar de los de quien la llamó.",[10,176,177,178,181],{},"Supabase lo dice en una línea: una función security definer conserva los\nprivilegios del usuario de base de datos que la creó, y mientras ese sea el rol\n",[21,179,180],{},"postgres",", tus triggers de auth deberían poder interactuar con tablas de fuera.\nEl empleado deja de llevar su propia credencial y lleva la del encargado.",[10,183,184,185,188,189,192,193,195],{},"Eso es una cesión real de autoridad, así que conviene saber qué has escrito antes\nde pegarlo. Cada sentencia dentro de esa función corre ahora como el propietario.\nEl propio ejemplo de Supabase fija el search_path a vacío, con\n",[21,186,187],{},"security definer set search_path = ''",", y por eso el insert que hay en él nombra\n",[21,190,191],{},"public.profiles"," entero en lugar de ",[21,194,52],{},". Con el search_path vacío un\nnombre de tabla a secas no resuelve a nada. A una función que lleva los\nprivilegios del propietario no se le puede cambiar entonces el destino en\nsilencio por otra tabla, según lo que diga el search_path de quien la llama.",[77,197],{"alt":198,"caption":199,"src":200},"El mismo camino dibujado dos veces. Arriba, una figura que lleva una credencial pequeña queda detenida por una puerta atrancada delante de un panel con un libro. Abajo, la misma figura lleva una credencial mayor, la puerta está abierta y el camino sigue hacia dentro del panel.","Qué credencial lleva la función es toda la diferencia. security definer es el carril de abajo, y es la credencial del propietario.","\u002Fblog\u002Fdatabase-error-saving-new-user\u002Ftwo-passes-1600x620.png",[10,202,203,204,206],{},"Lo que esto no autoriza es recurrir a ",[21,205,173],{}," la próxima vez que algo\nsea denegado. Deja la función haciendo una cosa y devolviendo, que es lo que hace\nla versión de Supabase.",[36,208,210],{"id":209},"causa-2-una-regla-en-la-tabla-en-la-que-escribes","Causa 2: una regla en la tabla en la que escribes",[10,212,213,214,216],{},"Row Level Security en tu tabla ",[21,215,52],{}," puede rechazar el insert, y entonces el\nregistro falla exactamente igual.",[10,218,219],{},"La guía de Supabase para este patrón te dice que actives Row Level Security en\nesa tabla, y esa indicación es la correcta. Para tu trigger significa que el\ninsert tiene que estar permitido, o por una policy que lo cubra, o por una\nfunción que corra como alguien a quien las policies no se le aplican.",[10,221,222,223,225],{},"Aquí es donde esto deja de ser un bug y pasa a ser una decisión que alguien toma\na las once de la noche. Nadie puede registrarse, el error no dice nada, y en el\npanel hay un interruptor, rotulado en lenguaje claro, que hace desaparecer el\nerror: apagar Row Level Security en ",[21,224,52],{},". El siguiente intento funciona. Lo\nque también pasa es que una tabla con tus usuarios, con todo lo que guardes en\nella junto a sus nombres, empieza a responderle a cualquiera que la pida.",[10,227,228,229,232,233,236],{},"Esa parte se ve desde fuera, que es como podemos contarla. Nuestro escáner lee\napps en producción como lo haría un desconocido. Encontramos un proyecto de\nSupabase detrás de 8.435 de las apps que ha calificado. En 3.680 de ellas la\npregunta \"¿puede alguien sin cuenta leer esta tabla?\" obtuvo una respuesta\nutilizable, y ",[31,230,231],{},"2.096 respondieron que sí",". En ",[31,234,235],{},"394"," de ellas la tabla abierta\nestaba nombrada como una tabla de personas.",[10,238,239,240,75],{},"Parte de eso es deliberado, porque una carta publicada y una página de anuncios\nviven en tablas que un desconocido debe leer, y nuestro escáner no puede\ndistinguirlas de una tabla de clientes que alguien abrió para quitarse un error\nde encima. Informa de lo que respondió. Si prefieres ver tu propia respuesta\nantes que razonar sobre ella, nuestro escaneo gratuito lee tu app en producción y\nte dice qué tablas contestaron. Tarda unos 20 segundos y no necesita cuenta:\n",[71,241,243],{"href":242},"\u002Fsecurity-scanner","escanea tu app",[10,245,246,247,251,252,256],{},"La policy que necesita un trigger es bastante más estrecha que apagar las reglas.\n",[71,248,250],{"href":249},"\u002Fblog\u002Fenable-rls-on-every-supabase-table","Activar Row Level Security en todas las tablas","\nexplica cómo escribir una, y\n",[71,253,255],{"href":254},"\u002Fblog\u002Fnew-row-violates-row-level-security-policy","\"new row violates row-level security policy\"","\nes el mensaje que recibes cuando una policy rechaza un insert que estás viendo\ncon tus propios ojos.",[36,258,260],{"id":259},"las-otras-causas-y-cómo-se-lee-cada-una-en-el-log","Las otras causas, y cómo se lee cada una en el log",[10,262,263],{},"Cuatro más, y la línea del log las distingue en unas pocas palabras.",[10,265,266,269,270,272],{},[31,267,268],{},"Una tabla que no está."," La nota de Supabase lo cubre: una función de trigger\nque apunta a una tabla que no existe o fue borrada. Una tabla ",[21,271,52],{}," borrada\ny reconstruida con otro nombre se lleva por delante cada registro, y el log\nnombra la relación que falta.",[10,274,275,280,281,75],{},[31,276,277,278,75],{},"Una constraint sobre ",[21,279,48],{}," Un check o una clave foránea añadida a esa\ntabla que la fila nueva no cumple. Vale la pena buscarla incluso si estás seguro\nde no haber creado ningún trigger, porque una clave foránea entre una tabla tuya\ny el usuario de auth es la vía más común por la que llega esta causa. Con su\ncomportamiento por defecto, esa misma clave foránea es la que rechaza el borrado\ncuando más tarde se elimina a esa persona, y\n",[71,282,284],{"href":283},"\u002Fblog\u002Fdatabase-error-deleting-user","ese mensaje tiene su propio artículo",[10,286,287,290,291,294,295,298,299,301,302,305,306,309],{},[31,288,289],{},"Un valor que no es del tipo correcto."," Esta aparece palabra por palabra. Un\ntrigger que copia campos del registro desde ",[21,292,293],{},"raw_user_meta_data"," los lee con\n",[21,296,297],{},"->>",", y ",[21,300,297],{}," devuelve texto independientemente del aspecto que tuviera el valor\nal entrar. Escribir ese texto en una columna ",[21,303,304],{},"date"," produjo, en un caso\nreportado, ",[21,307,308],{},"column \"birth_date\" is of type date but expression is of type text",".\nEl arreglo es un cast, con la extracción entre paréntesis primero:",[14,311,314],{"className":312,"code":313,"language":19},[17],"(new.raw_user_meta_data->>'birthDate')::date,\n(new.raw_user_meta_data->>'height')::integer,\n",[21,315,313],{"__ignoreMap":23},[10,317,318,321,322,325],{},[31,319,320],{},"Filas insertadas a mano."," La nota de Supabase cubre también las cuentas\ncreadas directamente con SQL en lugar de por la API de Auth, donde columnas\nobligatorias del esquema auth se quedan en NULL en lugar de en cadenas vacías. Si\nalguien sembró usuarios con una sentencia ",[21,323,324],{},"insert",", ese es un arreglo distinto de\ntodos los de arriba.",[10,327,328,329,332,333,75],{},"Una línea más que puedes encontrarte ahí:\n",[21,330,331],{},"duplicate key value violates unique constraint",". Significa que la fila que\nescribe tu trigger ya estaba, por ejemplo porque tu app inserta el mismo perfil\ndespués del registro, y\n",[71,334,336],{"href":335},"\u002Fblog\u002Fduplicate-key-value-violates-unique-constraint","ese error tiene su propio artículo",[10,338,339,340,343,344,75],{},"Y una más: ",[21,341,342],{},"null value in column … violates not-null constraint",". Tu trigger\nescribió un valor vacío en una columna que exige uno, casi siempre un campo de\nregistro que el formulario nunca envió, y\n",[71,345,347],{"href":346},"\u002Fblog\u002Fnull-value-violates-not-null-constraint","ese también tiene su propio artículo",[10,349,350,351,354,355,358,359,362,363,75],{},"La última que puedes encontrarte es\n",[21,352,353],{},"insert or update on table \"profiles\" violates foreign key constraint",". Casi\nsiempre significa que el trigger se creó con ",[21,356,357],{},"before insert",", así que la persona\npara la que escribe un perfil todavía no está guardada. El patrón de Supabase de\nmás abajo usa ",[21,360,361],{},"after insert",", y\n",[71,364,284],{"href":365},"\u002Fblog\u002Fviolates-foreign-key-constraint",[36,367,369],{"id":368},"la-versión-que-funciona-entera","La versión que funciona, entera",[10,371,372],{},"Parte del patrón que publica Supabase y no de un resultado de búsqueda, porque\ncuatro cosas de ahí sostienen el peso y una paráfrasis suele dejarse una.",[14,374,377],{"className":375,"code":376,"language":19},[17],"create function public.handle_new_user()\nreturns trigger\nlanguage plpgsql\nsecurity definer set search_path = ''\nas $$\nbegin\n  insert into public.profiles (id, first_name, last_name)\n  values (new.id, new.raw_user_meta_data ->> 'first_name', new.raw_user_meta_data ->> 'last_name');\n  return new;\nend;\n$$;\n\ncreate trigger on_auth_user_created\n  after insert on auth.users\n  for each row execute procedure public.handle_new_user();\n",[21,378,376],{"__ignoreMap":23},[10,380,381,382,384,385,388,389,391,392,395,396,398],{},"Las cuatro son ",[21,383,173],{},", el ",[21,386,387],{},"search_path"," vacío, el ",[21,390,191],{},"\nescrito entero y el ",[21,393,394],{},"return new"," que deja continuar el registro. Tus propios\nnombres de columna van en el insert, y cada columna que nombres ahí tiene que\nexistir y tiene que aceptar lo que ",[21,397,297],{}," le da.",[10,400,401],{},"La guía de Supabase añade la frase que conviene repetir: si el trigger falla,\npuede bloquear los registros, así que prueba tu código a fondo. Crea una cuenta\nreal en tu app en producción después de cada cambio que le hagas.",[36,403,405],{"id":404},"cómo-mantener-tu-registro-funcionando-después-de-tocarlo","Cómo mantener tu registro funcionando después de tocarlo",[10,407,408,411],{},[31,409,410],{},"Un registro es la parte de tu app que dejas de probar el día en que funciona.","\nTe registraste una vez, tienes cuenta, y a partir de ahí inicias sesión. El\nsiguiente prompt que toque tus tablas puede romper el registro para todo el mundo\nmientras tu propia sesión sigue comportándose con normalidad.",[10,413,414],{},[31,415,416,420],{},[71,417,419],{"href":418},"\u002Fwebsite-monitoring","Reeve Monitor"," mira por ti:",[422,423,424,427,430,433],"ul",{},[99,425,426],{},"las nueve comprobaciones cada hora, en hasta tres apps",[99,428,429],{},"un correo el día en que un nuevo escaneo califica tu app peor que el anterior",[99,431,432],{},"si la app está disponible, cada 60 segundos",[99,434,435],{},"un informe mensual de lo que vio",[10,437,438],{},[31,439,440,441,445],{},"Una regla apagada para quitarse un error de encima deja legible una tabla de\npersonas. ",[71,442,444],{"href":443},"\u002Fsupabase-backups","Reeve Care"," guarda una copia de tu base de datos de\nSupabase donde ningún cambio en tus reglas puede alcanzarla.",[422,447,448,451,454,457,460],{},[99,449,450],{},"una copia cifrada cada día, guardada fuera de tu cuenta de Supabase",[99,452,453],{},"cada copia verificada antes de contar, contando las filas de cada tabla",[99,455,456],{},"una restauración en un clic que guarda lo que hay ahora antes de reemplazarlo",[99,458,459],{},"también tus archivos subidos, en cuanto conectes una credencial de Storage",[99,461,462],{},"todo lo que hace Monitor",[36,464,466],{"id":465},"qué-hacer-hoy","Qué hacer hoy",[468,469,470],"key-takeaways",{},[422,471,472,475,478,489,492],{},[99,473,474],{},"Reproduce un registro fallido y apunta la hora antes de cambiar nada.",[99,476,477],{},"Lee la entrada del log de Auth de ese momento, y después la línea del log de Postgres de la misma hora si la entrada de Auth apunta a la base de datos. Esa línea nombra la columna, la relación o el permiso que falló.",[99,479,480,481,483,484,486,487,75],{},"Comprueba que tu función de trigger lleva ",[21,482,173],{}," y nombra su destino entero, como ",[21,485,191],{}," en lugar de ",[21,488,52],{},[99,490,491],{},"Si una policy de esa tabla está rechazando el insert, escribe una policy para él. Apagar Row Level Security quita el error y deja esa tabla legible para cualquiera que la pida.",[99,493,494],{},"Crea una cuenta real en tu app en producción después de cada cambio en un trigger de registro. Tu propia sesión te oculta por completo un registro roto.",[10,496,497,498,502,503,507],{},"Empieza por la tabla en la que escribe tu trigger, y lee después las policies de\ncada tabla que tocaste esa misma semana. La\n",[71,499,501],{"href":500},"\u002Fchecklist","lista de seguridad de 10 minutos"," cubre qué más suele quedarse\nabierto en una app recién lanzada, y la\n",[71,504,506],{"href":505},"\u002Fis-your-supabase-app-safe","guía de seguridad de Supabase"," repasa el resto de lo\nque un desconocido puede alcanzar.",{"title":23,"searchDepth":509,"depth":509,"links":510},3,[511,513,514,515,516,517,518,519,520],{"id":38,"depth":512,"text":39},2,{"id":84,"depth":512,"text":85},{"id":140,"depth":512,"text":141},{"id":167,"depth":512,"text":168},{"id":209,"depth":512,"text":210},{"id":259,"depth":512,"text":260},{"id":368,"depth":512,"text":369},{"id":404,"depth":512,"text":405},{"id":465,"depth":512,"text":466},"Fundamentos de seguridad","\u002Fblog\u002Fdatabase-error-saving-new-user\u002Fcover-1200x630.png","Un libro de registro en un mostrador con la línea más reciente ya tachada, y detrás un segundo libro en una sala con la puerta atrancada.","\"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.",false,"md",[528,531,534,537,540],{"q":529,"a":530},"¿Qué significa \"Database error saving new user\"?","Significa que Supabase escribió a la persona nueva en su propia tabla auth.users, que algo unido a esa escritura falló y que la base de datos deshizo la operación entera, incluida la fila nueva. Así que no se creó ninguna cuenta y no queda nada a medias que limpiar. El mensaje no nombra ninguna tabla ni ninguna razón a propósito, porque tu formulario de registro está abierto a internet y un error concreto le describiría tu base de datos a cualquiera que escribiese un correo en él.",{"q":532,"a":533},"¿Dónde encuentro el error real?","En los logs de tu proyecto, y la propia guía de resolución de Supabase da el orden. Prueba un registro, deja que falle y apunta la hora. Abre Logs y lee la entrada de Auth de ese momento, que lleva lo que Auth anotó por su cuenta. Si esa entrada apunta a la base de datos, abre los logs de Postgres a la misma hora: esa línea nombra la columna, la relación o el permiso que falló de verdad.",{"q":535,"a":536},"¿Row Level Security en mi tabla profiles rompe el registro?","Puede. Si tu trigger inserta en una tabla con Row Level Security y ninguna policy permite ese insert, el insert se rechaza y el registro falla con el mismo mensaje vago. La solución es una policy para él, o una función que corra con privilegios a los que las policies no se aplican. Apagar las reglas en esa tabla también quita el error, y deja una tabla con tus usuarios legible para cualquiera que la pida.",{"q":538,"a":539},"¿Qué es security definer y es seguro?","Hace que la función corra con los privilegios de quien la creó en lugar de los de quien la llama, que es lo que permite a un trigger ejecutado por el rol de auth escribir en una tabla tuya. Es el patrón que publica Supabase, y es una cesión real de autoridad, así que la función debería hacer una sola cosa concreta. Supabase además fija el search_path a vacío en su ejemplo, y por eso el insert nombra public.profiles entero y no solo profiles.",{"q":541,"a":542},"Nunca creé ningún trigger. ¿Qué más puede ser?","Otras tres cosas. Una constraint o una clave foránea añadida a auth.users que la fila nueva no cumple. Una función de trigger que apunta a una tabla renombrada o borrada. Y la nota de Supabase menciona Prisma expresamente, porque puede romper los permisos sobre auth.users. La línea del log de Postgres te dice cuál de ellas tienes.","\u002Fblog\u002Fdatabase-error-saving-new-user\u002Fcard-800x500.png",[545,546,547,548,549,550,551],"database error saving new user","supabase database error saving new user","error de registro supabase","error trigger auth.users","handle_new_user no funciona","supabase security definer trigger","supabase error 500 registro",{},true,"\"Database error saving new user\" en Supabase","\u002Fblog\u002Fdatabase-error-saving-new-user","2026-10-14",{"title":5,"description":524},"blog\u002Fdatabase-error-saving-new-user",[560,561,562],"\"Database error saving new user\" significa que Supabase aceptó el registro, algo unido a él falló y todo se deshizo. No se creó ninguna cuenta.","El mensaje es vago a propósito, y el de verdad ya está anotado. La propia guía de resolución de Supabase te manda primero a los logs de Auth y después a los de Postgres, a la línea que hay debajo.","Casi siempre es un trigger sobre auth.users que no alcanza la tabla en la que escribe. La solución es una función security definer, y lo que hay que acertar es cuánto se le permite hacer.","59A8GEPvIap-AGi9Sb_-QV7iptLVlRdB6B2u4qVvths",[565,571,577,582,587,593,599,605,611,617,623,629,635,641,647,653,659,664,670,676,682,687,693,699,705,711,717,718,724,730,736,742,748,754,760,766,772,778,784,790,796,802,808,814,820,826,831,837,843,849,855,861,867,873,879,885,891,897,903,909,915,921,926,932,938,944,950,956,962,968,974,980,986,992,998,1004,1009,1015,1021,1027,1033,1039,1045,1051,1056,1062,1068,1074,1079,1085],{"path":566,"title":567,"description":568,"published":569,"category":521,"image":570,"draft":525},"\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","\u002Fblog\u002Fmax-client-connections-reached\u002Fcard-800x500.png",{"path":572,"title":573,"description":574,"published":575,"category":521,"image":576,"draft":525},"\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":365,"title":578,"description":579,"published":580,"category":521,"image":581,"draft":525},"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":346,"title":583,"description":584,"published":585,"category":521,"image":586,"draft":525},"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":588,"title":589,"description":590,"published":591,"category":521,"image":592,"draft":525},"\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":594,"title":595,"description":596,"published":597,"category":521,"image":598,"draft":525},"\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":600,"title":601,"description":602,"published":603,"category":521,"image":604,"draft":525},"\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":606,"title":607,"description":608,"published":609,"category":521,"image":610,"draft":525},"\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":612,"title":613,"description":614,"published":615,"category":521,"image":616,"draft":525},"\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":618,"title":619,"description":620,"published":621,"category":521,"image":622,"draft":525},"\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":283,"title":624,"description":625,"published":626,"category":627,"image":628,"draft":525},"\"Database error deleting user\" en Supabase, y qué se va con él","\"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.","2026-10-30","Backups","\u002Fblog\u002Fdatabase-error-deleting-user\u002Fcard-800x500.png",{"path":630,"title":631,"description":632,"published":633,"category":521,"image":634,"draft":525},"\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":636,"title":637,"description":638,"published":639,"category":521,"image":640,"draft":525},"\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":642,"title":643,"description":644,"published":645,"category":521,"image":646,"draft":525},"\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":648,"title":649,"description":650,"published":651,"category":521,"image":652,"draft":525},"\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":654,"title":655,"description":656,"published":657,"category":521,"image":658,"draft":525},"\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":335,"title":660,"description":661,"published":662,"category":521,"image":663,"draft":525},"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":665,"title":666,"description":667,"published":668,"category":521,"image":669,"draft":525},"\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":671,"title":672,"description":673,"published":674,"category":521,"image":675,"draft":525},"\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":677,"title":678,"description":679,"published":680,"category":521,"image":681,"draft":525},"\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":73,"title":683,"description":684,"published":685,"category":521,"image":686,"draft":525},"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":688,"title":689,"description":690,"published":691,"category":521,"image":692,"draft":525},"\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":694,"title":695,"description":696,"published":697,"category":521,"image":698,"draft":525},"\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":700,"title":701,"description":702,"published":703,"category":521,"image":704,"draft":525},"\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":706,"title":707,"description":708,"published":709,"category":521,"image":710,"draft":525},"\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":712,"title":713,"description":714,"published":715,"category":521,"image":716,"draft":525},"\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":555,"title":5,"description":524,"published":556,"category":521,"image":543,"draft":525},{"path":719,"title":720,"description":721,"published":722,"category":521,"image":723,"draft":525},"\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":725,"title":726,"description":727,"published":728,"category":521,"image":729,"draft":525},"\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":731,"title":732,"description":733,"published":734,"category":521,"image":735,"draft":525},"\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":737,"title":738,"description":739,"published":740,"category":521,"image":741,"draft":525},"\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":743,"title":744,"description":745,"published":746,"category":521,"image":747,"draft":525},"\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":749,"title":750,"description":751,"published":752,"category":521,"image":753,"draft":525},"\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":755,"title":756,"description":757,"published":758,"category":521,"image":759,"draft":525},"\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":761,"title":762,"description":763,"published":764,"category":521,"image":765,"draft":525},"\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":767,"title":768,"description":769,"published":770,"category":521,"image":771,"draft":525},"\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":773,"title":774,"description":775,"published":776,"category":521,"image":777,"draft":525},"\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":779,"title":780,"description":781,"published":782,"category":521,"image":783,"draft":525},"\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":785,"title":786,"description":787,"published":788,"category":521,"image":789,"draft":525},"\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":791,"title":792,"description":793,"published":794,"category":521,"image":795,"draft":525},"\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":797,"title":798,"description":799,"published":800,"category":521,"image":801,"draft":525},"\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":803,"title":804,"description":805,"published":806,"category":521,"image":807,"draft":525},"\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":809,"title":810,"description":811,"published":812,"category":627,"image":813,"draft":525},"\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":815,"title":816,"description":817,"published":818,"category":627,"image":819,"draft":525},"\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":821,"title":822,"description":823,"published":824,"category":627,"image":825,"draft":525},"\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":827,"title":828,"description":829,"published":824,"category":521,"image":830,"draft":525},"\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":832,"title":833,"description":834,"published":835,"category":521,"image":836,"draft":525},"\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":838,"title":839,"description":840,"published":841,"category":521,"image":842,"draft":525},"\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":844,"title":845,"description":846,"published":847,"category":627,"image":848,"draft":525},"\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":850,"title":851,"description":852,"published":853,"category":521,"image":854,"draft":525},"\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":856,"title":857,"description":858,"published":859,"category":521,"image":860,"draft":525},"\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":862,"title":863,"description":864,"published":865,"category":521,"image":866,"draft":525},"\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":868,"title":869,"description":870,"published":871,"category":521,"image":872,"draft":525},"\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":874,"title":875,"description":876,"published":877,"category":521,"image":878,"draft":525},"\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":880,"title":881,"description":882,"published":883,"category":521,"image":884,"draft":525},"\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":886,"title":887,"description":888,"published":889,"category":521,"image":890,"draft":525},"\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":892,"title":893,"description":894,"published":895,"category":521,"image":896,"draft":525},"\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":898,"title":899,"description":900,"published":901,"category":627,"image":902,"draft":525},"\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":904,"title":905,"description":906,"published":907,"category":627,"image":908,"draft":525},"\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":910,"title":911,"description":912,"published":913,"category":627,"image":914,"draft":525},"\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":916,"title":917,"description":918,"published":919,"category":521,"image":920,"draft":525},"\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":249,"title":922,"description":923,"published":924,"category":521,"image":925,"draft":525},"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":927,"title":928,"description":929,"published":930,"category":521,"image":931,"draft":525},"\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":933,"title":934,"description":935,"published":936,"category":521,"image":937,"draft":525},"\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":939,"title":940,"description":941,"published":942,"category":521,"image":943,"draft":525},"\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":945,"title":946,"description":947,"published":948,"category":521,"image":949,"draft":525},"\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":951,"title":952,"description":953,"published":954,"category":521,"image":955,"draft":525},"\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":957,"title":958,"description":959,"published":960,"category":521,"image":961,"draft":525},"\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":963,"title":964,"description":965,"published":966,"category":521,"image":967,"draft":525},"\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":969,"title":970,"description":971,"published":972,"category":521,"image":973,"draft":525},"\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":975,"title":976,"description":977,"published":978,"category":627,"image":979,"draft":525},"\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":981,"title":982,"description":983,"published":984,"category":521,"image":985,"draft":525},"\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":987,"title":988,"description":989,"published":990,"category":627,"image":991,"draft":525},"\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":993,"title":994,"description":995,"published":996,"category":521,"image":997,"draft":525},"\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":999,"title":1000,"description":1001,"published":1002,"category":521,"image":1003,"draft":525},"\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":254,"title":1005,"description":1006,"published":1007,"category":521,"image":1008,"draft":525},"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":1010,"title":1011,"description":1012,"published":1013,"category":521,"image":1014,"draft":525},"\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":1016,"title":1017,"description":1018,"published":1019,"category":627,"image":1020,"draft":525},"\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":1022,"title":1023,"description":1024,"published":1025,"category":627,"image":1026,"draft":525},"\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":1028,"title":1029,"description":1030,"published":1031,"category":521,"image":1032,"draft":525},"\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":1034,"title":1035,"description":1036,"published":1037,"category":521,"image":1038,"draft":525},"\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":1040,"title":1041,"description":1042,"published":1043,"category":627,"image":1044,"draft":525},"\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":1046,"title":1047,"description":1048,"published":1049,"category":521,"image":1050,"draft":525},"\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":1052,"title":1053,"description":1054,"published":1049,"category":521,"image":1055,"draft":525},"\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":1057,"title":1058,"description":1059,"published":1060,"category":521,"image":1061,"draft":525},"\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":1063,"title":1064,"description":1065,"published":1066,"category":627,"image":1067,"draft":525},"\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":1069,"title":1070,"description":1071,"published":1072,"category":521,"image":1073,"draft":525},"\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":1075,"title":1076,"description":1077,"published":1072,"category":627,"image":1078,"draft":525},"\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":1080,"title":1081,"description":1082,"published":1083,"category":627,"image":1084,"draft":525},"\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":1086,"title":1087,"description":1088,"published":1083,"category":521,"image":1089,"draft":525},"\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",1794213645231]