[{"data":1,"prerenderedAt":1184},["ShallowReactive",2],{"blog-es-permission-denied-for-schema-public":3,"blog-index-es":653},{"id":4,"title":5,"body":6,"category":612,"cover":613,"coverAlt":614,"description":615,"draft":616,"extension":617,"faq":618,"image":634,"keywords":635,"meta":642,"navigation":643,"ogTitle":5,"path":644,"published":645,"seo":646,"stem":647,"tldr":648,"updated":645,"__hash__":652},"blog_es\u002Fblog\u002Fpermission-denied-for-schema-public.md","Supabase \"permission denied for schema public\": tres causas",{"type":7,"value":8,"toc":598},"minimark",[9,18,21,31,38,46,51,57,87,102,116,125,131,134,138,154,163,166,172,183,187,192,199,205,214,217,221,224,230,240,243,250,253,269,274,277,281,287,299,302,306,316,344,357,361,364,367,373,399,405,425,431,447,451,454,486,493,497,500,509,527,536,553,557,592],[10,11,12,13,17],"p",{},"Ayer tu app funcionaba. Hoy cada pantalla que lee de Supabase sale vacía o\nmuestra un error, y la consola del navegador tiene la misma línea para todas:\n",[14,15,16],"code",{},"permission denied for schema public",". Abres el panel de Supabase y todas las\ntablas siguen ahí. El SQL Editor las lee sin ninguna queja.",[10,19,20],{},"La respuesta que hay detrás de esa línea suele llegar como un 401 o un 403.\nRecortada a los dos campos que importan, dice esto:",[22,23,28],"pre",{"className":24,"code":26,"language":27},[25],"language-text","{\n  \"code\": \"42501\",\n  \"message\": \"permission denied for schema public\"\n}\n","text",[14,29,26],{"__ignoreMap":30},"",[10,32,33,34,37],{},"Supabase \"permission denied for schema public\" viene de Postgres, la base de\ndatos sobre la que funciona Supabase, y su lista de causas es corta. Todo\nproyecto de Supabase se crea con un grant que deja entrar a tu app en el esquema\n",[14,35,36],{},"public",". Alguien, o algo, lo ha quitado, y devolverlo es una línea de SQL.",[10,39,40,41,45],{},"Esta es la parte que es fácil entender al revés: ",[42,43,44],"strong",{},"ese grant es la puerta\nprincipal del edificio, y las cerraduras de tus datos están más adentro."," Si\nalguien lo quitó para dejar fuera a los extraños, dejó fuera a tu app con ellos.\nY que reabrirla sea seguro depende de esas cerraduras interiores, así que son lo\nque hay que revisar antes de darlo por arreglado.",[47,48,50],"h2",{"id":49},"qué-significa-permission-denied-for-schema-public","¿Qué significa \"permission denied for schema public\"?",[10,52,53,54,56],{},"Significa que el rol que hace la petición no tiene derecho a entrar en el\nesquema ",[14,55,36],{},", así que Postgres la rechazó antes de mirar una sola tabla.",[10,58,59,60,63,64,66,67,70,71,74,75,78,79,82,83,86],{},"Un ",[42,61,62],{},"esquema"," es una carpeta de tablas con nombre dentro de tu base de datos.\n",[14,65,36],{}," es donde Lovable, Bolt, v0, Cursor, Claude Code y Replit ponen tus\ntablas si nadie les dice otra cosa. Un ",[42,68,69],{},"rol"," es el tipo de visitante del que\nviene una petición: ",[14,72,73],{},"anon"," es alguien que no ha iniciado sesión,\n",[14,76,77],{},"authenticated"," es alguien que sí, y ",[14,80,81],{},"service_role"," es tu propio código de\nservidor usando la clave secreta. Un ",[42,84,85],{},"grant"," da a un rol un permiso sobre una\ncosa.",[10,88,89,90,93,94,101],{},"El permiso del que habla este error se llama ",[14,91,92],{},"USAGE",", y la\n",[95,96,100],"a",{"href":97,"rel":98},"https:\u002F\u002Fwww.postgresql.org\u002Fdocs\u002Fcurrent\u002Fddl-priv.html",[99],"nofollow","documentación de Postgres","\nlo describe como el derecho a buscar (\"look up\") objetos dentro del esquema. Un\nrol sin él no llega a ninguna tabla de ese esquema, digan lo que digan los\najustes de la propia tabla.",[10,103,104,105,109,110,112,113,115],{},"Si ",[95,106,108],{"href":107},"\u002Fblog\u002Fsupabase-permission-denied-for-table","la versión de tabla de este error","\nte enseñó a pensar en cada tabla como una habitación con una puerta para cada\nrol, el esquema es el edificio donde están esas habitaciones, y ",[14,111,92],{}," es su\npuerta principal. ",[14,114,16],{}," significa que tu\npetición está de pie fuera de ella. Da igual qué habitaciones tengan la puerta\nabierta, porque la petición nunca llega a ninguna habitación.",[10,117,118,119,124],{},"Supabase da esa puerta principal a los tres roles cuando se crea un proyecto. La\n",[95,120,123],{"href":121,"rel":122},"https:\u002F\u002Fgithub.com\u002Fsupabase\u002Fpostgres\u002Fblob\u002Fdevelop\u002Fmigrations\u002Fdb\u002Finit-scripts\u002F00000000000000-initial-schema.sql",[99],"imagen de base de datos de Supabase","\nejecuta esta línea en su primer script de configuración:",[22,126,129],{"className":127,"code":128,"language":27},[25],"grant usage on schema public to postgres, anon, authenticated, service_role;\n",[14,130,128],{"__ignoreMap":30},[10,132,133],{},"Así que si tu app recibe este error, el grant estuvo ahí y ya no está.",[47,135,137],{"id":136},"por-qué-el-sql-editor-sigue-funcionando","¿Por qué el SQL Editor sigue funcionando?",[10,139,140,141,146,147,150,151,153],{},"Porque el SQL Editor no usa ninguno de los roles de tu app. Una\n",[95,142,145],{"href":143,"rel":144},"https:\u002F\u002Fsupabase.com\u002Fdocs\u002Fguides\u002Ftroubleshooting\u002Ftracking-postgres-role-activity-to-specific-dashboard-users-8d3715",[99],"guía de resolución de problemas de Supabase","\ndice que una consulta lanzada desde ahí es \"routed through the ",[14,148,149],{},"postgres"," role\nat the database level\", es decir, que pasa por el rol ",[14,152,149],{}," a nivel de base\nde datos.",[10,155,156,158,159,162],{},[14,157,149],{}," es el rol dueño de tus tablas, y si el propio esquema se volvió a\ncrear desde el panel o lo hizo tu builder, también es dueño de él. Un dueño no\nnecesita ningún grant para entrar en su propio edificio. Así que el panel\nmuestra todas las tablas, un ",[14,160,161],{},"select"," en el SQL Editor devuelve todas las filas,\ny la misma petición desde tu app se queda en la puerta principal. Nada en esas\npantallas te dice que los roles de tu app han perdido la forma de entrar.",[10,164,165],{},"Para ver lo que ve tu app, pregunta a Postgres directamente por sus roles. Pega\nesto en el SQL Editor:",[22,167,170],{"className":168,"code":169,"language":27},[25],"select rolname,\n       has_schema_privilege(rolname, 'public', 'USAGE') as can_enter\nfrom pg_roles\nwhere rolname in ('anon', 'authenticated', 'service_role');\n",[14,171,169],{"__ignoreMap":30},[10,173,174,175,178,179,182],{},"Cada fila debería decir ",[14,176,177],{},"true",". Una fila que dice ",[14,180,181],{},"false"," es un rol que usa tu\napp y que ya no puede entrar.",[47,184,186],{"id":185},"el-grant-abre-el-edificio-y-las-cerraduras-están-dentro","El grant abre el edificio, y las cerraduras están dentro",[10,188,189,191],{},[14,190,92],{}," deja a un rol llegar a las tablas de un esquema. No dice nada sobre\ncuáles de esas tablas puede usar el rol, ni qué filas recibe.",[10,193,194,195,198],{},"Detrás de la puerta principal hay dos comprobaciones más, y las dos pertenecen a\ncada tabla. El grant propio de la tabla decide si un rol puede usar esa tabla, y\ncuando falta es lo que informa ",[14,196,197],{},"permission denied for table",". Después, Row Level\nSecurity, las reglas por fila que quizá ya conozcas, decide qué filas recibe ese\nrol. Postgres pregunta las tres en ese orden y se detiene en el primer rechazo.",[200,201],"diagram",{"alt":202,"caption":203,"src":204},"Tres carriles, cada uno una petición de anon hacia la misma tabla. En el primero, una verja con barrotes marcada public está cerrada y la flecha se detiene en ella con el código 42501, antes de la puerta propia de la tabla. En el segundo, la verja está abierta y la flecha se detiene en la puerta cerrada de la tabla, también 42501. En el tercero, la verja y la puerta están abiertas, la flecha se detiene en una barrera de Row Level Security y se iluminan dos filas de la tabla.","Postgres comprueba el esquema, luego la tabla, luego las filas, y se detiene en el primer rechazo. Una verja cerrada rechaza la petición antes de probar siquiera la puerta de ninguna tabla.","\u002Fblog\u002Fpermission-denied-for-schema-public\u002Fthree-checks-in-order-1600x760.png",[10,206,207,208,210,211,213],{},"Ese orden es lo que convierte el grant del esquema en una mala cerradura. Para un\nrol es todo o nada: quítaselo a ",[14,209,73],{}," y cada pantalla que ve un visitante sin\nsesión deja de funcionar a la vez, incluida una página de inicio de sesión que\nlea cualquier cosa de tus tablas. Quítaselo a ",[14,212,77],{}," y pasa lo mismo a\ntodos los que han iniciado sesión. La documentación de Postgres añade que\nrevocarlo \"is not a completely secure way to prevent object access\", es decir,\nque no es una forma completamente segura de impedir el acceso, y que un rol sin\nél todavía puede leer los nombres de tus tablas en los catálogos del sistema.",[10,215,216],{},"Si estás viendo este error, tu app necesita el rol que fue rechazado.",[47,218,220],{"id":219},"causa-1-el-esquema-se-borró-y-se-volvió-a-crear","Causa 1: el esquema se borró y se volvió a crear",[10,222,223],{},"Esta causa es la que más se lleva por delante. Alguien ejecutó estas dos líneas,\no algo que hace lo mismo:",[22,225,228],{"className":226,"code":227,"language":27},[25],"drop schema public cascade;\ncreate schema public;\n",[14,229,227],{"__ignoreMap":30},[10,231,232,233,236,237,239],{},"La primera línea elimina el esquema y, por el ",[14,234,235],{},"cascade",", cada tabla, vista y\nfunción que había dentro. La segunda crea un ",[14,238,36],{}," nuevo y vacío. Un esquema\nnuevo pertenece al rol que lo creó, y la documentación de Postgres dice con\nclaridad que nadie más recibe ningún privilegio sobre un esquema por defecto. El\ngrant que Supabase ejecutó al crear tu proyecto pertenecía al esquema viejo y se\nfue con él, y Supabase no vuelve a ejecutar su script de configuración.",[10,241,242],{},"Esas dos líneas son la forma más corta de vaciar una base de datos Postgres, así\nque es un paso que un asistente de IA puede dar cuando le pides empezar la base\nde datos de cero o hacer que una migración que falla pase limpia.",[10,244,245,246,249],{},"Después tus tablas vuelven si las migraciones de la app se ejecutan otra vez. Una\n",[42,247,248],{},"migración"," es uno de los archivos de cambios de base de datos que escribe tu\nbuilder, y al ejecutarlas todas se reconstruyen las tablas que describen. Lo que\nno vuelve es todo lo que nunca mencionan, y el grant de la puerta principal lo\nhizo Supabase antes de que existiera tu primera migración. Puede que las\nmigraciones que escribió tu builder no lo contengan en absoluto.",[10,251,252],{},"Otras dos cosas se van con el esquema viejo:",[254,255,256,263],"ul",{},[257,258,259,262],"li",{},[42,260,261],{},"Los grants automáticos para tablas nuevas."," Postgres los guarda como\nprivilegios por defecto (default privileges) del esquema, y se borran junto con\nél. Las tablas creadas después empiezan sin grant para ninguno de los roles de\ntu app.",[257,264,265,268],{},[42,266,267],{},"Las filas."," Las migraciones reconstruyen tablas vacías. Lo que guardaban las\nviejas solo vuelve si alguien restauró una copia hecha antes del reinicio.",[200,270],{"alt":271,"caption":272,"src":273},"Dos patios amurallados, uno al lado del otro, ambos marcados public. A la izquierda, el esquema original: la verja abierta, tres tablas con las puertas abiertas y las filas iluminadas, y encima etiquetas para postgres, anon, authenticated y service_role. A la derecha, el esquema recreado: las mismas tres tablas con las puertas cerradas, la verja cerrada, solo la etiqueta postgres iluminada y contornos discontinuos donde estaban las otras tres.","Antes del reinicio, los cuatro roles podían entrar en el esquema. Después, las tablas se pueden reconstruir, pero la verja y sus puertas solo se abren para postgres, que es quien las construyó.","\u002Fblog\u002Fpermission-denied-for-schema-public\u002Fdrop-and-create-1600x700.png",[10,275,276],{},"Si tus tablas han vuelto vacías, encontrar esa copia va antes que cualquier otra\ncosa de esta página.",[47,278,280],{"id":279},"causa-2-un-revoke-pensado-para-proteger","Causa 2: un revoke pensado para proteger",[10,282,283,284,286],{},"La segunda forma es deliberada. Alguien leyó que cada tabla de ",[14,285,36],{}," es\naccesible a través de la Data API de Supabase, la dirección web por la que tu\napp lee y escribe sus datos, y revocó el grant del esquema para cerrarla.",[10,288,289,290,295,296,298],{},"La preocupación de fondo es razonable, y la herramienta elegida es la\nequivocada. La guía de Supabase para\n",[95,291,294],{"href":292,"rel":293},"https:\u002F\u002Fsupabase.com\u002Fdocs\u002Fguides\u002Fapi\u002Fsecuring-your-api",[99],"proteger la Data API","\nnunca toca el grant del esquema. Lo que revoca es el acceso automático que\nreciben las tablas nuevas de ",[14,297,36],{},", de modo que cada tabla nueva sigue cerrada\nhasta que alguien le da acceso a propósito. Supabase lo activa para todos los\nproyectos existentes el 30 de octubre de 2026.",[10,300,301],{},"Si un script que ejecutaste, o un asistente al que pediste reforzar tu base de\ndatos, quitó el grant del esquema, la consulta de comprobación de arriba muestra\nqué roles lo perdieron. Devuélveselo a los que usa tu app y luego protege las\ncosas como describen las secciones de abajo.",[47,303,305],{"id":304},"causa-3-el-error-viene-de-una-migración","Causa 3: el error viene de una migración",[10,307,308,309,312,313,315],{},"Cuando el mensaje aparece mientras algo está creando tablas, como una migración,\nun despliegue, o Prisma o Drizzle preparando tu base de datos, el permiso que\nfalta es otro. Crear cualquier cosa en un esquema necesita ",[14,310,311],{},"CREATE"," sobre él, y\nel texto del error cuando falta ",[14,314,311],{}," es la misma línea.",[10,317,318,319,321,322,324,325,329,330,335,336,339,340,343],{},"Desde Postgres 15, los roles normales ya no reciben ",[14,320,311],{}," sobre ",[14,323,36],{}," por\ndefecto. La\n",[95,326,100],{"href":327,"rel":328},"https:\u002F\u002Fwww.postgresql.org\u002Fdocs\u002Fcurrent\u002Fddl-schemas.html",[99],"\ndice que solo las bases de datos actualizadas desde la versión 14 o anteriores\nse lo siguen dando a todo el mundo. Así que un usuario de base de datos aparte,\ncreado para una herramienta de migraciones, recibe el rechazo en su primerísima\ntabla. La ",[95,331,334],{"href":332,"rel":333},"https:\u002F\u002Fsupabase.com\u002Fdocs\u002Fguides\u002Fdatabase\u002Fprisma",[99],"guía de Prisma"," de\nSupabase crea un usuario ",[14,337,338],{},"prisma",", y su configuración incluye\n",[14,341,342],{},"grant create on schema public to prisma",".",[10,345,346,347,350,351,353,354,356],{},"La solución es un grant de ",[14,348,349],{},"create"," para ese único usuario de migraciones. Nadie\nque visite tu app necesita crear tablas, así que ",[14,352,73],{}," y ",[14,355,77],{}," nunca\nreciben uno.",[47,358,360],{"id":359},"cómo-devolver-el-grant-sin-riesgo","Cómo devolver el grant sin riesgo",[10,362,363],{},"Ejecuta una línea, confirma que los tres roles pueden entrar, recarga tu app y\nlee lo que dice a continuación.",[10,365,366],{},"La línea, en el SQL Editor:",[22,368,371],{"className":369,"code":370,"language":27},[25],"grant usage on schema public to anon, authenticated, service_role;\n",[14,372,370],{"__ignoreMap":30},[10,374,375,376,379,380,383,384,387,388,390,391,393,394,398],{},"Quédate en ",[14,377,378],{},"usage",". ",[14,381,382],{},"grant all on schema public"," también quita el error, y en\nun esquema ",[14,385,386],{},"all"," significa ",[14,389,378],{}," más ",[14,392,349],{},", el derecho a añadir tablas\npropias. Supabase aconseja dar a cada rol\n",[95,395,397],{"href":292,"rel":396},[99],"lo mínimo que necesita",",\ny los roles de tus visitantes solo necesitan entrar.",[10,400,401,402,404],{},"La consulta de comprobación de antes debería decir ahora ",[14,403,177],{}," para los tres\nroles. Recarga tu app y pasará una de dos cosas:",[254,406,407,413],{},[257,408,409,412],{},[42,410,411],{},"Funciona."," La puerta principal era lo único que faltaba.",[257,414,415,418,419,421,422,343],{},[42,416,417],{},"Un mensaje nuevo nombra una tabla."," ",[14,420,197],{}," significa\nque también falta el grant propio de esa tabla, que es lo esperable después de\nla primera causa, porque las tablas reconstruidas nunca recibieron los grants\nautomáticos. Da a cada tabla\n",[95,423,424],{"href":107},"su grant, Row Level Security y una regla en un solo cambio",[10,426,427,428,430],{},"Resiste la tentación de dar a cada rol todo sobre cada tabla para que desaparezca\nel segundo mensaje. Una tabla con un grant para ",[14,429,73],{}," y Row Level Security\ndesactivado entrega todas sus filas a cualquiera que las pida, y en tu app nada\nse verá distinto.",[10,432,433,434,437,438,440,441,446],{},"Si el mensaje nombra otro esquema, como ",[14,435,436],{},"permission denied for schema api",", ese\nesquema lo creó alguien, y nunca recibió los grants con los que empieza\n",[14,439,36],{},". La guía de Supabase para\n",[95,442,445],{"href":443,"rel":444},"https:\u002F\u002Fsupabase.com\u002Fdocs\u002Fguides\u002Fapi\u002Fusing-custom-schemas",[99],"usar esquemas propios","\nenumera las líneas que necesita.",[47,448,450],{"id":449},"qué-proteger-en-su-lugar","Qué proteger en su lugar",[10,452,453],{},"Los controles que mantienen a los extraños lejos de tus datos pertenecen a cada\ntabla, y la guía de seguridad de Supabase los nombra:",[455,456,457,465,474,480],"ol",{},[257,458,459,462,463,343],{},[42,460,461],{},"Un grant por tabla, para los roles que lo necesitan."," Una tabla de pedidos\nno necesita ningún grant para ",[14,464,73],{},[257,466,467,470,343],{},[42,468,469],{},"Row Level Security en cada tabla, con una regla.",[95,471,473],{"href":472},"\u002Fblog\u002Fenable-rls-on-every-supabase-table","Cómo activarlo y comprobar que aguanta",[257,475,476,479],{},[42,477,478],{},"Tablas nuevas cerradas por defecto."," Revocar los grants automáticos lo\nconsigue hoy, y el 30 de octubre lo hace por ti.",[257,481,482,485],{},[42,483,484],{},"Las tablas internas fuera de la Data API."," La guía de Supabase describe\nponer lo que lee tu app en un esquema dedicado, de modo que todo lo demás\nquede en esquemas que la Data API no sirve.",[10,487,488,489,343],{},"Después mira el resultado desde fuera, porque ninguna de esas pantallas muestra\nlo que recibe un extraño. Nuestro escaneo gratuito pregunta a cada tabla que tu\napp nombra cuántas filas recibiría un visitante sin sesión, lee la cifra sin\ndescargar ni una fila y tarda unos 20 segundos, sin cuenta:\n",[95,490,492],{"href":491},"\u002Fsecurity-scanner","escanea tu app",[47,494,496],{"id":495},"conservar-tus-tablas-y-lo-que-hay-en-ellas","Conservar tus tablas, y lo que hay en ellas",[10,498,499],{},"Después de hoy aún pueden salir mal dos cosas: un cambio posterior abre una tabla\na los extraños, y un reinicio como el de la primera causa se lleva tus filas.",[10,501,502],{},[42,503,504,508],{},[95,505,507],{"href":506},"\u002Fwebsite-monitoring","Reeve Monitor"," sigue comprobando desde fuera:",[254,510,511,514,521,524],{},[257,512,513],{},"la comprobación de seguridad completa cada hora, en hasta tres apps",[257,515,516,517,520],{},"un mensaje cuando un resultado ",[42,518,519],{},"cambia",", para que una tabla que se abrió tras una migración no espere a que lo mires",[257,522,523],{},"si tu app está en línea",[257,525,526],{},"un informe mensual, en lenguaje claro, de lo que ha visto",[10,528,529],{},[42,530,531,535],{},[95,532,534],{"href":533},"\u002Fsupabase-backups","Reeve Care"," guarda una copia de lo que hay en tus tablas:",[254,537,538,541,544,547,550],{},[257,539,540],{},"una copia cifrada de tu base de datos de Supabase según un calendario, en el almacenamiento propio de Reeve en la UE, fuera de tu cuenta de Supabase",[257,542,543],{},"cada copia verificada antes de contar, contando las filas de cada tabla",[257,545,546],{},"también tus archivos subidos, en cuanto conectas una credencial de Storage",[257,548,549],{},"una restauración con un clic, con una copia de seguridad del estado actual hecha antes",[257,551,552],{},"todo lo que hace Monitor",[47,554,556],{"id":555},"qué-hacer-ahora","Qué hacer ahora",[558,559,560],"key-takeaways",{},[254,561,562,568,577,580,583,586],{},[257,563,564,565,567],{},"Ejecuta la consulta de comprobación en el SQL Editor. Cada rol que diga ",[14,566,181],{}," es uno que tu app ha perdido.",[257,569,570,571,574,575,343],{},"Devuelve el grant con ",[14,572,573],{},"grant usage on schema public to anon, authenticated, service_role;"," y quédate en ",[14,576,378],{},[257,578,579],{},"Si el esquema se borró, mira si tus tablas están vacías y busca una copia hecha antes del reinicio.",[257,581,582],{},"Si después aparece un error de tabla, da a esa tabla su grant, Row Level Security y una regla a la vez.",[257,584,585],{},"Protege tabla por tabla, donde se puede ajustar, y deja en paz el grant del esquema.",[257,587,588,589,591],{},"Si el error vino de una migración, da ",[14,590,349],{}," al usuario de migraciones y a nadie más.",[10,593,594,595,597],{},"Empieza por la consulta de comprobación, porque un solo resultado te dice qué\nroles de tu app perdieron la forma de entrar. Luego, con la puerta abierta otra\nvez, ",[95,596,492],{"href":491}," para ver lo que le entrega a un\nextraño.",{"title":30,"searchDepth":599,"depth":599,"links":600},3,[601,603,604,605,606,607,608,609,610,611],{"id":49,"depth":602,"text":50},2,{"id":136,"depth":602,"text":137},{"id":185,"depth":602,"text":186},{"id":219,"depth":602,"text":220},{"id":279,"depth":602,"text":280},{"id":304,"depth":602,"text":305},{"id":359,"depth":602,"text":360},{"id":449,"depth":602,"text":450},{"id":495,"depth":602,"text":496},{"id":555,"depth":602,"text":556},"Fundamentos de seguridad","\u002Fblog\u002Fpermission-denied-for-schema-public\u002Fcover-1200x630.png","Un patio amurallado de tablas de base de datos, cada puerta abierta e iluminada, tras una verja de entrada cerrada con los barrotes bajados.","Supabase \"permission denied for schema public\" es un grant que falta en el esquema. Qué lo quitó y la línea que lo devuelve.",false,"md",[619,622,625,628,631],{"q":620,"a":621},"¿Qué significa \"permission denied for schema public\" en Supabase?","Significa que el rol que hace la petición, normalmente anon o authenticated, no tiene derecho a entrar en el esquema public, así que Postgres la rechazó antes de mirar ninguna tabla. Lleva el código 42501 y suele llegar a tu app como un 401 o un 403. Todo proyecto de Supabase empieza con ese derecho concedido, así que el error significa que alguien o algo lo quitó.",{"q":623,"a":624},"¿Es seguro conceder usage sobre el esquema public?","Sí. Todo proyecto de Supabase empieza exactamente con ese grant. Solo deja a un rol llegar a las tablas del esquema: cada tabla sigue necesitando su propio grant, y Row Level Security sigue decidiendo qué filas recibe cada visitante. Lo que merece una mirada son las tablas de detrás, porque una con un grant para anon y Row Level Security desactivado entrega todas sus filas a cualquiera que las pida.",{"q":626,"a":627},"¿En qué se diferencia de \"permission denied for table\"?","El error del esquema detiene la petición antes de que llegue a ninguna tabla, así que todas las tablas del esquema fallan a la vez. El error de tabla afecta a una sola tabla que no tiene grant para ese rol, y el resto de tu app sigue funcionando. Los dos llevan el código 42501, así que lo que los distingue son las palabras del mensaje.",{"q":629,"a":630},"¿Por qué mis tablas funcionan en el SQL Editor de Supabase y no en mi app?","Porque el SQL Editor ejecuta tus consultas con el rol postgres, que es el dueño de tus tablas y no necesita el grant que han perdido los roles de tu app. Tu app usa anon para los visitantes sin sesión y authenticated para los que han iniciado sesión. Pregunta a Postgres por esos dos roles directamente, con has_schema_privilege, para ver lo que ve tu app.",{"q":632,"a":633},"¿Qué debería proteger en lugar del grant del esquema?","Las tablas. Da a cada tabla un grant solo para los roles que lo necesitan, activa Row Level Security con una regla en cada tabla y deja que las tablas nuevas empiecen sin ningún acceso. Supabase convierte esa última parte en la opción por defecto para los proyectos existentes el 30 de octubre de 2026.","\u002Fblog\u002Fpermission-denied-for-schema-public\u002Fcard-800x500.png",[16,636,637,638,639,640,641],"supabase permission denied for schema public","supabase permission denied for schema","grant usage on schema public","postgres 42501 schema public","drop schema public cascade supabase","supabase permisos rol anon",{},true,"\u002Fblog\u002Fpermission-denied-for-schema-public","2026-10-18",{"title":5,"description":615},"blog\u002Fpermission-denied-for-schema-public",[649,650,651],"Supabase \"permission denied for schema public\" significa que el rol que hace la petición no tiene derecho a entrar en el esquema public, así que Postgres rechazó la petición antes de mirar una sola tabla.","Todo proyecto de Supabase empieza con ese derecho concedido a anon, authenticated y service_role. Si tu app recibe este error, algo lo quitó: el esquema se borró y se volvió a crear, o un script revocó el grant.","Devolverlo es una línea de SQL. El grant solo deja a un rol llegar a tus tablas, y Row Level Security sigue decidiendo qué filas recibe cada visitante, así que revisa esas reglas antes de darlo por arreglado.","3rs0KEkqS3lrViZTERJyRUUfmsFInvZN0Ljt7qWFHVY",[654,660,666,672,678,684,690,696,702,708,714,721,727,733,739,745,751,757,763,769,775,781,787,788,794,800,806,812,818,824,830,836,842,848,854,860,866,872,878,884,890,896,902,908,914,920,924,930,936,942,948,954,960,966,972,978,984,990,996,1002,1008,1014,1019,1025,1031,1037,1043,1049,1055,1061,1067,1073,1079,1085,1091,1097,1103,1109,1115,1121,1127,1133,1139,1145,1150,1156,1162,1168,1173,1179],{"path":655,"title":656,"description":657,"published":658,"category":612,"image":659,"draft":616},"\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":661,"title":662,"description":663,"published":664,"category":612,"image":665,"draft":616},"\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":667,"title":668,"description":669,"published":670,"category":612,"image":671,"draft":616},"\u002Fblog\u002Fviolates-foreign-key-constraint","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":673,"title":674,"description":675,"published":676,"category":612,"image":677,"draft":616},"\u002Fblog\u002Fnull-value-violates-not-null-constraint","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":679,"title":680,"description":681,"published":682,"category":612,"image":683,"draft":616},"\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":685,"title":686,"description":687,"published":688,"category":612,"image":689,"draft":616},"\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":691,"title":692,"description":693,"published":694,"category":612,"image":695,"draft":616},"\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":697,"title":698,"description":699,"published":700,"category":612,"image":701,"draft":616},"\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":703,"title":704,"description":705,"published":706,"category":612,"image":707,"draft":616},"\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":709,"title":710,"description":711,"published":712,"category":612,"image":713,"draft":616},"\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":715,"title":716,"description":717,"published":718,"category":719,"image":720,"draft":616},"\u002Fblog\u002Fdatabase-error-deleting-user","\"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":722,"title":723,"description":724,"published":725,"category":612,"image":726,"draft":616},"\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":728,"title":729,"description":730,"published":731,"category":612,"image":732,"draft":616},"\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":734,"title":735,"description":736,"published":737,"category":612,"image":738,"draft":616},"\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":740,"title":741,"description":742,"published":743,"category":612,"image":744,"draft":616},"\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":746,"title":747,"description":748,"published":749,"category":612,"image":750,"draft":616},"\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":752,"title":753,"description":754,"published":755,"category":612,"image":756,"draft":616},"\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":758,"title":759,"description":760,"published":761,"category":612,"image":762,"draft":616},"\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":764,"title":765,"description":766,"published":767,"category":612,"image":768,"draft":616},"\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":770,"title":771,"description":772,"published":773,"category":612,"image":774,"draft":616},"\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":776,"title":777,"description":778,"published":779,"category":612,"image":780,"draft":616},"\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":782,"title":783,"description":784,"published":785,"category":612,"image":786,"draft":616},"\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":644,"title":5,"description":615,"published":645,"category":612,"image":634,"draft":616},{"path":789,"title":790,"description":791,"published":792,"category":612,"image":793,"draft":616},"\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":795,"title":796,"description":797,"published":798,"category":612,"image":799,"draft":616},"\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":801,"title":802,"description":803,"published":804,"category":612,"image":805,"draft":616},"\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":807,"title":808,"description":809,"published":810,"category":612,"image":811,"draft":616},"\u002Fblog\u002Fdatabase-error-saving-new-user","\"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":813,"title":814,"description":815,"published":816,"category":612,"image":817,"draft":616},"\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":819,"title":820,"description":821,"published":822,"category":612,"image":823,"draft":616},"\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":825,"title":826,"description":827,"published":828,"category":612,"image":829,"draft":616},"\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":831,"title":832,"description":833,"published":834,"category":612,"image":835,"draft":616},"\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":837,"title":838,"description":839,"published":840,"category":612,"image":841,"draft":616},"\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":843,"title":844,"description":845,"published":846,"category":612,"image":847,"draft":616},"\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":849,"title":850,"description":851,"published":852,"category":612,"image":853,"draft":616},"\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":855,"title":856,"description":857,"published":858,"category":612,"image":859,"draft":616},"\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":861,"title":862,"description":863,"published":864,"category":612,"image":865,"draft":616},"\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":867,"title":868,"description":869,"published":870,"category":612,"image":871,"draft":616},"\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":873,"title":874,"description":875,"published":876,"category":612,"image":877,"draft":616},"\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":879,"title":880,"description":881,"published":882,"category":612,"image":883,"draft":616},"\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":885,"title":886,"description":887,"published":888,"category":612,"image":889,"draft":616},"\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":891,"title":892,"description":893,"published":894,"category":612,"image":895,"draft":616},"\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":897,"title":898,"description":899,"published":900,"category":612,"image":901,"draft":616},"\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":903,"title":904,"description":905,"published":906,"category":719,"image":907,"draft":616},"\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":909,"title":910,"description":911,"published":912,"category":719,"image":913,"draft":616},"\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":915,"title":916,"description":917,"published":918,"category":719,"image":919,"draft":616},"\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":107,"title":921,"description":922,"published":918,"category":612,"image":923,"draft":616},"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":925,"title":926,"description":927,"published":928,"category":612,"image":929,"draft":616},"\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":931,"title":932,"description":933,"published":934,"category":612,"image":935,"draft":616},"\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":937,"title":938,"description":939,"published":940,"category":719,"image":941,"draft":616},"\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":943,"title":944,"description":945,"published":946,"category":612,"image":947,"draft":616},"\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":949,"title":950,"description":951,"published":952,"category":612,"image":953,"draft":616},"\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":955,"title":956,"description":957,"published":958,"category":612,"image":959,"draft":616},"\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":961,"title":962,"description":963,"published":964,"category":612,"image":965,"draft":616},"\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":967,"title":968,"description":969,"published":970,"category":612,"image":971,"draft":616},"\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":973,"title":974,"description":975,"published":976,"category":612,"image":977,"draft":616},"\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":979,"title":980,"description":981,"published":982,"category":612,"image":983,"draft":616},"\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":985,"title":986,"description":987,"published":988,"category":612,"image":989,"draft":616},"\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":991,"title":992,"description":993,"published":994,"category":719,"image":995,"draft":616},"\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":997,"title":998,"description":999,"published":1000,"category":719,"image":1001,"draft":616},"\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":1003,"title":1004,"description":1005,"published":1006,"category":719,"image":1007,"draft":616},"\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":1009,"title":1010,"description":1011,"published":1012,"category":612,"image":1013,"draft":616},"\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":472,"title":1015,"description":1016,"published":1017,"category":612,"image":1018,"draft":616},"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":1020,"title":1021,"description":1022,"published":1023,"category":612,"image":1024,"draft":616},"\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":1026,"title":1027,"description":1028,"published":1029,"category":612,"image":1030,"draft":616},"\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":1032,"title":1033,"description":1034,"published":1035,"category":612,"image":1036,"draft":616},"\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":1038,"title":1039,"description":1040,"published":1041,"category":612,"image":1042,"draft":616},"\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":1044,"title":1045,"description":1046,"published":1047,"category":612,"image":1048,"draft":616},"\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":1050,"title":1051,"description":1052,"published":1053,"category":612,"image":1054,"draft":616},"\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":1056,"title":1057,"description":1058,"published":1059,"category":612,"image":1060,"draft":616},"\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":1062,"title":1063,"description":1064,"published":1065,"category":612,"image":1066,"draft":616},"\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":1068,"title":1069,"description":1070,"published":1071,"category":719,"image":1072,"draft":616},"\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":1074,"title":1075,"description":1076,"published":1077,"category":612,"image":1078,"draft":616},"\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":1080,"title":1081,"description":1082,"published":1083,"category":719,"image":1084,"draft":616},"\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":1086,"title":1087,"description":1088,"published":1089,"category":612,"image":1090,"draft":616},"\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":1092,"title":1093,"description":1094,"published":1095,"category":612,"image":1096,"draft":616},"\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":1098,"title":1099,"description":1100,"published":1101,"category":612,"image":1102,"draft":616},"\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":1104,"title":1105,"description":1106,"published":1107,"category":612,"image":1108,"draft":616},"\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":1110,"title":1111,"description":1112,"published":1113,"category":719,"image":1114,"draft":616},"\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":1116,"title":1117,"description":1118,"published":1119,"category":719,"image":1120,"draft":616},"\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":1122,"title":1123,"description":1124,"published":1125,"category":612,"image":1126,"draft":616},"\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":1128,"title":1129,"description":1130,"published":1131,"category":612,"image":1132,"draft":616},"\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":1134,"title":1135,"description":1136,"published":1137,"category":719,"image":1138,"draft":616},"\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":1140,"title":1141,"description":1142,"published":1143,"category":612,"image":1144,"draft":616},"\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":1146,"title":1147,"description":1148,"published":1143,"category":612,"image":1149,"draft":616},"\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":1151,"title":1152,"description":1153,"published":1154,"category":612,"image":1155,"draft":616},"\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":1157,"title":1158,"description":1159,"published":1160,"category":719,"image":1161,"draft":616},"\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":1163,"title":1164,"description":1165,"published":1166,"category":612,"image":1167,"draft":616},"\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":1169,"title":1170,"description":1171,"published":1166,"category":719,"image":1172,"draft":616},"\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":1174,"title":1175,"description":1176,"published":1177,"category":719,"image":1178,"draft":616},"\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":1180,"title":1181,"description":1182,"published":1177,"category":612,"image":1183,"draft":616},"\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",1794213645212]