[{"data":1,"prerenderedAt":800},["ShallowReactive",2],{"blog-es-enable-rls-on-every-supabase-table":3,"blog-index-es":561},{"id":4,"title":5,"body":6,"category":517,"cover":518,"coverAlt":519,"description":520,"draft":521,"extension":522,"faq":523,"image":539,"keywords":540,"meta":549,"navigation":550,"ogTitle":551,"path":552,"published":553,"seo":554,"stem":555,"tldr":556,"updated":553,"__hash__":560},"blog_es\u002Fblog\u002Fenable-rls-on-every-supabase-table.md","Activa Row Level Security en cada tabla de Supabase y compruébalo",{"type":7,"value":8,"toc":505},"minimark",[9,13,21,26,29,32,39,45,48,52,55,58,69,80,86,89,95,98,114,118,121,127,133,159,165,171,174,180,186,193,275,287,291,294,297,302,305,308,314,318,321,324,327,333,342,345,349,352,358,365,370,391,404,411,415,418,421,433,436,439,451,455,492],[10,11,12],"p",{},"Te han dicho que actives Row Level Security, o tu constructor de IA lo mencionó\nde pasada mientras arreglaba otra cosa. Tu proyecto de Supabase tiene entre\ncuatro y cuarenta tablas, y no sabes cuáles están cubiertas.",[10,14,15,16,20],{},"La instrucción que encuentras en todas partes es una línea de SQL por tabla, y\nhasta ahí es correcta. Donde casi todas las guías se paran es en el paso\nsiguiente. ",[17,18,19],"strong",{},"Una política que existe no es una política que funciona",", y nada\nen tu dashboard te enseña la diferencia. Así que activar Row Level Security en\nSupabase son tres trabajos y no uno: ponerlo en todas las tablas, escribir las\ndos o tres políticas que vuelven a montar tu app, y luego preguntarle a tu\npropia base de datos lo que le preguntaría un desconocido.",[22,23,25],"h2",{"id":24},"qué-hace-realmente-activar-row-level-security","¿Qué hace realmente activar Row Level Security?",[10,27,28],{},"Hace que Postgres consulte tus reglas antes de entregar una fila. Con el\ninterruptor apagado no hay reglas que consultar, así que la respuesta a\ncualquier petición es todo.",[10,30,31],{},"Imagina a un bibliotecario que va a buscar lo que le pidas. Row Level Security\nes la instrucción de leer una nota sobre ti antes de cargar el carrito. La nota\nes tu política, y puede decir que esta persona puede llevarse los libros que ha\nescrito, o puede decir que cualquiera puede llevarse cualquier cosa. Con la\ninstrucción puesta y ninguna nota escrita todavía, el bibliotecario vuelve con\nel carrito vacío y no explica por qué.",[10,33,34,35,38],{},"Esa última parte es donde tropieza la gente, y decide qué aspecto tendrá una\nprueba más adelante. ",[17,36,37],{},"Row Level Security filtra filas. No rechaza peticiones.","\nUna tabla que no tienes permiso para leer responde con una lista vacía y un\ncódigo de éxito, no con un error ni con una pantalla de inicio de sesión. A tu\napp nunca se le dice que la han rechazado. No recibe nada y pinta una pantalla\nen blanco.",[40,41],"diagram",{"alt":42,"caption":43,"src":44},"La misma petición dibujada tres veces. Con el interruptor de seguridad apagado vuelven cuatro filas encendidas. Con el interruptor puesto y sin política vuelve un hueco discontinuo vacío. Con el interruptor puesto y una política de propiedad vuelve una fila encendida. Las tres respuestas están marcadas con 200.","Una petición, tres configuraciones y el mismo código de éxito cada vez. Row Level Security cambia lo que vuelve, nunca si la petición salió bien.","\u002Fblog\u002Fenable-rls-on-every-supabase-table\u002Fsame-request-three-answers-1600x780.png",[10,46,47],{},"Así que poner el interruptor en un proyecto que todavía no tiene políticas no\ndeja fuera a los desconocidos. Deja fuera a todo el mundo, tu propia app\nincluida, hasta que digas quién puede ver qué.",[22,49,51],{"id":50},"cómo-activo-rls-en-todas-las-tablas-de-supabase-a-la-vez","¿Cómo activo RLS en todas las tablas de Supabase a la vez?",[10,53,54],{},"Un bucle, ejecutado una vez en el SQL Editor. Recorre todas las tablas de tu\nesquema public y pone el interruptor allí donde esté apagado.",[10,56,57],{},"Empieza por ver dónde estás. Esto lista tus tablas y dice si cada una lo tiene\nahora mismo:",[59,60,65],"pre",{"className":61,"code":63,"language":64},[62],"language-text","select tablename, rowsecurity\nfrom pg_tables\nwhere schemaname = 'public'\norder by tablename;\n","text",[66,67,63],"code",{"__ignoreMap":68},"",[10,70,71,72,75,76,79],{},"Cada fila en la que ",[66,73,74],{},"rowsecurity"," pone ",[66,77,78],{},"false"," es una tabla que entrega su\ncontenido a cualquiera que tenga la clave publicable que va en tu app. Si esa\nlista es corta, hazlas de una en una y mira qué se rompe:",[59,81,84],{"className":82,"code":83,"language":64},[62],"alter table public.orders enable row level security;\n",[66,85,83],{"__ignoreMap":68},[10,87,88],{},"Si no es corta, esto se ocupa de todas:",[59,90,93],{"className":91,"code":92,"language":64},[62],"do $$\ndeclare t record;\nbegin\n  for t in\n    select tablename from pg_tables where schemaname = 'public'\n  loop\n    execute format(\n      'alter table public.%I enable row level security', t.tablename\n    );\n  end loop;\nend $$;\n",[66,94,92],{"__ignoreMap":68},[10,96,97],{},"Ejecuta eso y tu app se queda en blanco. Supabase lo dice con todas las letras\nen su propia documentación: los datos dejan de estar accesibles a través de la\nAPI con una clave publicable hasta que se definen políticas. Es el interruptor\nhaciendo su trabajo, y por eso la siguiente sección es la que conviene tener\nabierta antes de pulsar ejecutar.",[99,100,102],"callout",{"type":101},"warn",[10,103,104,107,108,113],{},[17,105,106],{},"Las tablas que hizo tu constructor son las que estaban abiertas."," Una tabla\ncreada a clics desde el Table Editor de Supabase viene con Row Level Security\nactivada. Una tabla creada ejecutando SQL no, y ejecutar SQL es como Lovable,\nBolt, Cursor y v0 crean tablas por ti. Las tablas que nunca viste nacer son\njusto aquellas para las que existe este bucle, y\n",[109,110,112],"a",{"href":111},"\u002Fblog\u002Fsupabase-rls-disabled-in-public","qué pilla y qué no pilla el aviso \"RLS disabled in public\"","\nexplica por qué tu dashboard pudo quedarse callado sobre ellas.",[22,115,117],{"id":116},"las-tres-políticas-que-de-verdad-necesitas","Las tres políticas que de verdad necesitas",[10,119,120],{},"Casi todas las tablas de una app como la tuya tienen una de tres formas: filas\nque pertenecen a una persona, filas que cualquiera puede leer y filas que tu app\nescribe en nombre de un visitante. Aquí tienes cada una, lista para pegar y\nrenombrar.",[10,122,123,126],{},[17,124,125],{},"Filas que pertenecen a una persona."," Pedidos, mensajes, elementos guardados,\ncualquier cosa con un propietario.",[59,128,131],{"className":129,"code":130,"language":64},[62],"create policy \"read own orders\"\non public.orders for select\nto authenticated\nusing ( (select auth.uid()) = user_id );\n",[66,132,130],{"__ignoreMap":68},[10,134,135,138,139,142,143,146,147,150,151,154,155,158],{},[66,136,137],{},"auth.uid()"," es el id de quien tenga la sesión iniciada en esa petición.\n",[66,140,141],{},"user_id"," es la columna de tu tabla que guarda al propietario, así que comprueba\nel nombre antes de ejecutarlo: los constructores también escriben ",[66,144,145],{},"owner_id",",\n",[66,148,149],{},"profile_id"," y ",[66,152,153],{},"created_by",". La línea ",[66,156,157],{},"to authenticated"," significa que la\npolítica ni siquiera se considera para un visitante sin sesión, y eso es lo que\nmantiene la tabla cerrada al público.",[10,160,161,164],{},[17,162,163],{},"Filas que cualquiera puede leer."," Un catálogo de productos, artículos\npublicados, un mapa de locales.",[59,166,169],{"className":167,"code":168,"language":64},[62],"create policy \"anyone may read products\"\non public.products for select\nto anon, authenticated\nusing ( true );\n",[66,170,168],{"__ignoreMap":68},[10,172,173],{},"Escribe esta a propósito o no la escribas, porque también es la política a la\nque echa mano un constructor de IA en cuanto le pides que arregle una pantalla\nvacía. La pregunta que hay que resolver antes: ¿podría esta tabla ser una página\nde tu sitio, tal cual está, sin quitarle nada? Un no significa que quiere la\npolítica de propiedad de arriba.",[10,175,176,179],{},[17,177,178],{},"Filas que escribe tu app."," Leer y escribir son permisos separados en\nPostgres, así que una tabla en la que tu app guarda cosas necesita una segunda\npolítica, y esta comprueba la fila a la entrada y no a la salida.",[59,181,184],{"className":182,"code":183,"language":64},[62],"create policy \"insert own orders\"\non public.orders for insert\nto authenticated\nwith check ( (select auth.uid()) = user_id );\n",[66,185,183],{"__ignoreMap":68},[10,187,188,189,192],{},"Qué cláusula va dónde es la parte que atrapa a la gente, y la fila ",[66,190,191],{},"update","\ncarga con un requisito que no tiene nada de evidente:",[194,195,196,212],"table",{},[197,198,199],"thead",{},[200,201,202,206,209],"tr",{},[203,204,205],"th",{},"Operación",[203,207,208],{},"Cláusula",[203,210,211],{},"Además necesita",[213,214,215,230,244,262],"tbody",{},[200,216,217,223,228],{},[218,219,220],"td",{},[66,221,222],{},"select",[218,224,225],{},[66,226,227],{},"using",[218,229],{},[200,231,232,237,242],{},[218,233,234],{},[66,235,236],{},"insert",[218,238,239],{},[66,240,241],{},"with check",[218,243],{},[200,245,246,250,256],{},[218,247,248],{},[66,249,191],{},[218,251,252,150,254],{},[66,253,227],{},[66,255,241],{},[218,257,258,259,261],{},"una política ",[66,260,222],{}," en la misma tabla",[200,263,264,269,273],{},[218,265,266],{},[66,267,268],{},"delete",[218,270,271],{},[66,272,227],{},[218,274],{},[10,276,277,278,281,282,286],{},"La documentación de Supabase es explícita sobre esa última: sin una política de\nselect correspondiente, un update no funciona como esperas. Y si el mensaje\n",[66,279,280],{},"new row violates row-level security policy"," es lo que te trajo hasta aquí, la\ndistancia entre esas dos cláusulas es\n",[109,283,285],{"href":284},"\u002Fblog\u002Fnew-row-violates-row-level-security-policy","todo ese error",".",[22,288,290],{"id":289},"por-qué-todos-los-ejemplos-escriben-select-authuid-y-no-authuid","Por qué todos los ejemplos escriben (select auth.uid()) y no auth.uid()",[10,292,293],{},"Porque los paréntesis hacen que Postgres calcule el valor una vez para toda la\nconsulta en lugar de una vez por cada fila que mira.",[10,295,296],{},"La versión envuelta se convierte en lo que Postgres llama un initPlan, que\nejecuta una sola vez y luego reutiliza durante el resto de la sentencia. Sin los\nparéntesis, la función se vuelve a llamar en la fila uno, en la fila dos, en la\nfila tres, y así hacia abajo por una tabla que puede tener cien mil. El propio\nPerformance Advisor de Supabase señala la versión sin envolver bajo una regla\nllamada Auth RLS Initialization Plan, y es una de las entradas más habituales\nque la gente se encuentra ahí dentro.",[40,298],{"alt":299,"caption":300,"src":301},"Dos paneles sobre las mismas ocho filas de una tabla. En el panel izquierdo hay una marca de función junto a cada fila con su propio conector, ocho en total. En el panel derecho una sola marca de función arriba se abre en abanico hacia las ocho filas a la vez.","Los paréntesis son toda la diferencia. A la izquierda la función se ejecuta una vez por fila; a la derecha se ejecuta una vez y la respuesta se reutiliza.","\u002Fblog\u002Fenable-rls-on-every-supabase-table\u002Fonce-per-query-1600x620.png",[10,303,304],{},"Una advertencia, y es de la propia Supabase: esto funciona porque la respuesta\nno cambia de una fila a otra. Una función cuyo resultado sí depende de la fila\nque tiene delante no se puede sacar del bucle, así que esa déjala sin envolver.",[10,306,307],{},"Ya que estás, añade un índice a la columna por la que filtran tus políticas. La\npolítica se convierte en una condición en cada lectura de esa tabla, así que en\nuna tabla con muchas filas una columna sin índice se nota en tus tiempos de\nrespuesta:",[59,309,312],{"className":310,"code":311,"language":64},[62],"create index orders_user_id_idx on public.orders (user_id);\n",[66,313,311],{"__ignoreMap":68},[22,315,317],{"id":316},"probar-una-política-sin-crear-una-cuenta-falsa","Probar una política sin crear una cuenta falsa",[10,319,320],{},"El SQL Editor de Supabase puede ejecutar una consulta como si la hubiera enviado\nun visitante concreto, y eso cubre el caso anónimo por completo.",[10,322,323],{},"El editor lleva un control para el rol con el que debe ejecutarse una consulta.\nPonlo en el rol anónimo y luego lanza un select normal contra la tabla que\nacabas de cambiar. Lo que vuelve es lo que recibe un desconocido sin sesión.\nPara un visitante con sesión, coge el id de un usuario que ya tengas en vez de\ncrear uno nuevo; cualquier fila de tu propia tabla sirve para probar.",[10,325,326],{},"Si prefieres teclearlo a hacer clic, lo mismo en SQL:",[59,328,331],{"className":329,"code":330,"language":64},[62],"begin;\nset local role anon;\nselect * from public.orders;\nrollback;\n",[66,332,330],{"__ignoreMap":68},[10,334,335,150,338,341],{},[66,336,337],{},"begin",[66,339,340],{},"rollback"," están ahí para que el cambio de rol dure ese bloque y nada\nmás, algo que importa si vas repasando varias tablas de una sentada.",[10,343,344],{},"Lo que esto te dice es lo que hacen tus políticas dentro de la base de datos. Lo\nque no te puede decir es lo que tu proyecto entrega a internet, porque la\npetición que hacen de verdad tus visitantes no empieza en el SQL Editor. Empieza\nen un navegador, lleva una clave publicable y llega por la dirección pública de\ntu proyecto.",[22,346,348],{"id":347},"cómo-pruebo-el-rls-de-supabase-desde-fuera-de-mi-app","¿Cómo pruebo el RLS de Supabase desde fuera de mi app?",[10,350,351],{},"Manda la petición que mandaría un desconocido. Necesitas dos valores y los dos\nestán ya en el código que tu sitio sirve a cada visitante: la URL de tu proyecto\ny tu clave publicable.",[59,353,356],{"className":354,"code":355,"language":64},[62],"curl \"https:\u002F\u002FYOUR-PROJECT.supabase.co\u002Frest\u002Fv1\u002Forders?select=*&limit=1\" \\\n  -H \"apikey: YOUR_PUBLISHABLE_KEY\" \\\n  -H \"Authorization: Bearer YOUR_PUBLISHABLE_KEY\"\n",[66,357,355],{"__ignoreMap":68},[10,359,360,361,364],{},"Ve cambiando el nombre de la tabla de uno en uno y lee lo que vuelve. Una lista\nvacía, ",[66,362,363],{},"[]",", significa que la política aguantó para un visitante sin sesión. Una\nfila significa que cualquiera con una clave que va en tu app puede leer esa\ntabla. Un error que menciona la propia clave significa que copiaste el valor\nequivocado, y conviene descartarlo antes de concluir nada.",[40,366],{"alt":367,"caption":368,"src":369},"Un límite discontinuo alrededor de una tabla de base de datos. Dentro, una lupa junto a la tabla con una flecha corta. Fuera, una persona lanza una flecha que lleva una llave por un hueco del límite, con la ruta barra rest barra v1 escrita al lado del hueco, y llega a la misma tabla.","La prueba del editor no sale nunca del edificio. La petición desde fuera llega por la misma dirección pública que usan tus visitantes, con la misma clave que va en tu app.","\u002Fblog\u002Fenable-rls-on-every-supabase-table\u002Ftwo-tests-one-table-1600x640.png",[10,371,372,373,376,377,380,381,385,386,390],{},"En un proyecto de Supabase reciente esa clave empieza por ",[66,374,375],{},"sb_publishable_",", y\nen uno más antiguo es la clave ",[66,378,379],{},"anon",". Las dos se pueden usar así con seguridad\ny estar en tu app con seguridad, para eso están;\n",[109,382,384],{"href":383},"\u002Fblog\u002Fwhich-api-keys-are-safe-in-your-frontend","qué claves de API van en un frontend","\ncubre el par que no, y\n",[109,387,389],{"href":388},"\u002Fblog\u002Fwhere-to-find-supabase-api-keys","dónde encontrarlas en el dashboard"," son\nlos cuatro valores de esa página de ajustes.",[10,392,393,394,398,399,403],{},"Esta es la prueba que se parece a la realidad, y pasarla a escala es como\nsabemos lo común que es el hueco. Entre el 12 y el 14 de agosto de 2026 pasamos\nnueve comprobaciones externas a 30.998 apps en producción publicadas desde\nLovable, Base44, Replit, v0 y Bolt. De las 3.680 apps con Supabase en las que la\ncomprobación pudo completarse, 2.096 respondieron a una petición anónima con\nfilas de al menos una tabla. Eso es un 57 %, y es una proporción de las apps que\nnos dieron una respuesta clara, no de todo lo que escaneamos. El\n",[109,395,397],{"href":396},"\u002Fresearch\u002Fvibe-coded-app-security-2026","conjunto de datos completo está publicado",",\ny ",[109,400,402],{"href":401},"\u002Fblog\u002Fcan-anyone-read-your-supabase-database","de qué es proporción ese 57 %","\nrepasa el recuento.",[10,405,406,407,286],{},"Hacerlo a mano está bien con cuatro tablas y es pesado con cuarenta. Nuestro\nescaneo gratuito manda esa petición por ti, averigua qué tablas existen sin que\ntú se las nombres y te dice cuáles respondieron, junto a otras ocho\ncomprobaciones que hace desde fuera. Lee tu sitio en producción igual que puede\nhacerlo cualquier visitante, tarda unos 20 segundos y no necesita cuenta:\n",[109,408,410],{"href":409},"\u002Fsecurity-scanner","escanea tu app",[22,412,414],{"id":413},"antes-de-reescribir-políticas-en-una-tabla-en-producción","Antes de reescribir políticas en una tabla en producción",[10,416,417],{},"Saca primero una copia de tu base de datos. Estás a punto de cambiar permisos en\ntodas las tablas que tienes, con las mismas herramientas que produjeron el\nproblema.",[10,419,420],{},"Merece la pena separar dos riesgos distintos, porque solo uno de ellos tiene que\nver con el trabajo de hoy. Si una tabla era legible y escribible por cualquiera,\ncerrarla ahora no cambia nada de lo que ya pasó, y esa versión suele aparecer\ncomo un mensaje de soporte sobre datos que cambiaron solos. El otro riesgo es la\nmigración en sí. Una política que se borra y se vuelve a crear un poco mal es un\nmartes cualquiera, y el camino de vuelta es una copia de cómo estaban las cosas\nhace una hora.",[10,422,423,424,428,429,286],{},"En un plan de pago de Supabase tienes la copia de anoche esperando en la\nconsola. En el plan gratuito no hay nada a lo que volver, porque\n",[109,425,427],{"href":426},"\u002Fblog\u002Fdoes-supabase-back-up-my-database","Supabase no hace ninguna copia automática en el plan gratuito",".\nSi ese es tu caso,\n",[109,430,432],{"href":431},"\u002Fblog\u002Fback-up-supabase-free-tier","saca una antes de empezar",[10,434,435],{},"Reeve Care es la versión de eso que no tienes que recordar. Tu base de datos de\nSupabase se copia según un calendario, se guarda fuera de tu cuenta de Supabase,\ncifrada, y se vuelve a leer para confirmar que restaura antes de que la fecha de\ntu panel se mueva. Los archivos que suben tus usuarios viajan con ella en cuanto\nconectas una clave de Storage, y esa clave se pide aparte porque Supabase no\nemite una clave de solo lectura para archivos: la que copia tus subidas también\npuede escribir, y la que copia tu base de datos no. Conectarla es opcional y tu\nbase de datos se respalda igualmente. Las copias son solo de Supabase, así que\nsi tus datos viven en otro sitio lo decimos en vez de venderte una suscripción\nque vigila una caja vacía.",[10,437,438],{},"Restaurar es la parte que importa en un día como este. Care copia el estado\nactual de tu base de datos antes de reproducir la versión que elegiste, así que\npulsar el botón tiene su propio deshacer.",[10,440,441,442,446,447,286],{},"La otra mitad es la comprobación que acabas de hacer a mano. Una política que se\nafloja en alguna migración posterior no es algo que nadie encuentre mirando, así\nque Care vuelve a lanzar la misma petición anónima según un calendario y te avisa\ncuando una tabla empieza a responder y la semana pasada estaba callada. La\nmonitorización de uptime y un informe mensual están en la misma suscripción.\nNada de eso escribe tus políticas por ti, y ninguna copia cierra una tabla\nabierta. Lo que cambia es cuánto te cuesta una migración que sale mal.\n",[109,443,445],{"href":444},"\u002Fsupabase-backups","Qué respalda Reeve en Supabase, con qué frecuencia y qué hace una restauración","\nrecorre el ciclo entero, y los planes están en la\n",[109,448,450],{"href":449},"\u002Fpricing","página de precios",[22,452,454],{"id":453},"el-orden-en-el-que-trabajar","El orden en el que trabajar",[456,457,458],"key-takeaways",{},[459,460,461,469,472,479,489],"ul",{},[462,463,464,465,468],"li",{},"Lista tus tablas con ",[66,466,467],{},"select tablename, rowsecurity from pg_tables where schemaname = 'public'"," y mira cuántas están abiertas antes de cambiar nada.",[462,470,471],{},"Saca una copia de seguridad y luego activa Row Level Security en todas las tablas del esquema public. Cuenta con que tu app se quede en blanco; es el interruptor funcionando.",[462,473,474,475,478],{},"Dale a cada tabla una de las tres políticas. La de propiedad es la opción por defecto; ",[66,476,477],{},"USING (true)"," es una decisión que tomas tabla por tabla, no una forma de recuperar las pantallas.",[462,480,481,482,485,486,488],{},"Escribe ",[66,483,484],{},"(select auth.uid())"," y no ",[66,487,137],{},", y añade un índice a la columna por la que filtran tus políticas.",[462,490,491],{},"Prueba cada tabla desde fuera y sin sesión. Una lista vacía es un aprobado. Una fila es una tabla que cualquiera puede leer, diga lo que diga tu dashboard.",[10,493,494,495,499,500,504],{},"Empieza por la tabla que más vergüenza te daría como página pública y sigue\nhacia abajo desde ahí. La\n",[109,496,498],{"href":497},"\u002Fchecklist","lista de seguridad de 10 minutos"," cubre esto junto al resto de lo\nque conviene confirmar en una app recién lanzada, y la\n",[109,501,503],{"href":502},"\u002Fis-your-supabase-app-safe","guía de seguridad de Supabase"," repasa qué más suele\nquedarse abierto.",{"title":68,"searchDepth":506,"depth":506,"links":507},3,[508,510,511,512,513,514,515,516],{"id":24,"depth":509,"text":25},2,{"id":50,"depth":509,"text":51},{"id":116,"depth":509,"text":117},{"id":289,"depth":509,"text":290},{"id":316,"depth":509,"text":317},{"id":347,"depth":509,"text":348},{"id":413,"depth":509,"text":414},{"id":453,"depth":509,"text":454},"Fundamentos de seguridad","\u002Fblog\u002Fenable-rls-on-every-supabase-table\u002Fcover-1200x630.png","Una lista de tablas de base de datos en un panel, cada una con un interruptor de seguridad al lado, y la mayoría todavía apagados.","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.",false,"md",[524,527,530,533,536],{"q":525,"a":526},"¿Qué pasa si activo RLS y no escribo ninguna política?","La tabla deja de responder, también a tu propia app. Con el interruptor puesto, Postgres consulta tus políticas antes de entregar una fila, y sin políticas no hay nada que pueda decir que sí, así que las peticiones vuelven vacías. Supabase lo documenta directamente: los datos dejan de estar accesibles a través de la API con una clave publicable hasta que se definen políticas. No se borra nada ni se rompe nada. Escribe una política para las filas que tu app debe mostrar y las pantallas vuelven.",{"q":528,"a":529},"¿Row Level Security ralentiza mis consultas?","Puede hacerlo, y los dos arreglos son pequeños. Escribe `(select auth.uid())` en la condición de la política, con los paréntesis, para que Postgres calcule el valor una vez para toda la consulta y luego lo reutilice en cada fila. Supabase señala la versión sin paréntesis en su propio Performance Advisor, bajo una regla llamada Auth RLS Initialization Plan. Después añade un índice a la columna por la que filtra la política, normalmente `user_id`. En una tabla de unos pocos miles de filas es poco probable que notes la diferencia. En una grande, las dos cosas cuentan.",{"q":531,"a":532},"¿Necesito RLS si mi tabla no tiene datos personales?","Aun así lo quieres activado, y la política puede ser la permisiva. Una tabla con Row Level Security apagada la puede leer cualquiera que tenga la clave publicable que va en tu app, y eso está bien para un catálogo de productos y bastante menos bien para cualquier cosa que no publicarías como una página. Activarlo y escribir una política de lectura con `USING (true)` te da ese mismo acceso público a propósito, y hace que la tabla se lea como decidida y no como olvidada la próxima vez que alguien repase la lista.",{"q":534,"a":535},"¿Por qué dejó de funcionar mi suscripción de realtime al activar RLS?","Porque Realtime consulta las mismas políticas antes de enviar un cambio a quien está suscrito. Una tabla con Row Level Security activada y sin política de select para ese visitante no entrega filas a una consulta ni cambios a una suscripción, exactamente por el mismo motivo. Añade la política de select que necesita esa suscripción y el flujo se reanuda. Si usas Realtime broadcast o presence en lugar de cambios de base de datos, esos se autorizan aparte, con políticas escritas sobre la tabla `realtime.messages`.",{"q":537,"a":538},"¿Cómo pruebo una política sin crear un usuario falso?","De dos maneras, y ninguna necesita una cuenta nueva. El SQL Editor de Supabase puede ejecutar una consulta como si la hubiera enviado un rol concreto, y eso cubre el caso anónimo por completo: lo que vuelve es lo que recibe un desconocido. Para el caso con sesión iniciada necesitas un id de usuario que haga de sustituto, y sirve cualquier fila que ya tengas en tu propia tabla. La segunda manera es una petición desde fuera con tu clave publicable, que prueba el camino entero y no solo la política.","\u002Fblog\u002Fenable-rls-on-every-supabase-table\u002Fcard-800x500.png",[541,542,543,544,545,546,547,548],"activar row level security supabase","supabase activar rls","row level security policies supabase","ejemplo de política rls supabase","supabase rls select auth.uid","probar rls supabase","supabase rls best practices","auth.uid() policy",{},true,"Activa Row Level Security en cada tabla de Supabase","\u002Fblog\u002Fenable-rls-on-every-supabase-table","2026-09-10",{"title":5,"description":520},"blog\u002Fenable-rls-on-every-supabase-table",[557,558,559],"Activar Row Level Security en Supabase sin política cierra una tabla del todo, y escribir una política sin activar el interruptor no hace absolutamente nada. Cada tabla necesita las dos cosas.","Tres formas de política cubren casi todo lo que crea un constructor de IA: filas que pertenecen a una persona, filas que cualquiera puede leer y filas que tu app escribe en nombre de un visitante.","Después comprueba desde fuera de tu app y sin iniciar sesión, porque esa es la petición que hace un desconocido, y es la única que te dice lo que hacen tus políticas en lugar de lo que dicen.","jGbcbjMCBxFR99KSmQQbEgnfWnRr6hcSiomkNVccWAs",[562,569,575,581,587,593,599,605,611,617,623,629,635,641,642,647,653,659,665,671,677,683,689,695,701,706,712,717,722,728,734,740,746,752,758,763,768,774,779,785,790,796],{"path":563,"title":564,"description":565,"published":566,"category":567,"image":568,"draft":521},"\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","Backups","\u002Fblog\u002Fsupabase-backup-auth-users\u002Fcard-800x500.png",{"path":570,"title":571,"description":572,"published":573,"category":517,"image":574,"draft":521},"\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":576,"title":577,"description":578,"published":579,"category":517,"image":580,"draft":521},"\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":582,"title":583,"description":584,"published":585,"category":517,"image":586,"draft":521},"\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":588,"title":589,"description":590,"published":591,"category":517,"image":592,"draft":521},"\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":594,"title":595,"description":596,"published":597,"category":517,"image":598,"draft":521},"\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":600,"title":601,"description":602,"published":603,"category":517,"image":604,"draft":521},"\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":606,"title":607,"description":608,"published":609,"category":517,"image":610,"draft":521},"\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":612,"title":613,"description":614,"published":615,"category":517,"image":616,"draft":521},"\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":618,"title":619,"description":620,"published":621,"category":567,"image":622,"draft":521},"\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":624,"title":625,"description":626,"published":627,"category":567,"image":628,"draft":521},"\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":630,"title":631,"description":632,"published":633,"category":567,"image":634,"draft":521},"\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":636,"title":637,"description":638,"published":639,"category":517,"image":640,"draft":521},"\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":552,"title":5,"description":520,"published":553,"category":517,"image":539,"draft":521},{"path":111,"title":643,"description":644,"published":645,"category":517,"image":646,"draft":521},"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":648,"title":649,"description":650,"published":651,"category":517,"image":652,"draft":521},"\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":654,"title":655,"description":656,"published":657,"category":517,"image":658,"draft":521},"\u002Fblog\u002Fvibe-coding-security-scanners-compared","Escáneres de seguridad vibe coding comparados, el nuestro incluido","El mejor escáner de seguridad para vibe coding depende de tres preguntas que ninguna lista de funciones responde. Diez herramientas, con precios.","2026-09-07","\u002Fblog\u002Fvibe-coding-security-scanners-compared\u002Fcard-800x500.png",{"path":660,"title":661,"description":662,"published":663,"category":517,"image":664,"draft":521},"\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":666,"title":667,"description":668,"published":669,"category":517,"image":670,"draft":521},"\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":672,"title":673,"description":674,"published":675,"category":517,"image":676,"draft":521},"\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":678,"title":679,"description":680,"published":681,"category":517,"image":682,"draft":521},"\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":684,"title":685,"description":686,"published":687,"category":517,"image":688,"draft":521},"\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":690,"title":691,"description":692,"published":693,"category":567,"image":694,"draft":521},"\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":696,"title":697,"description":698,"published":699,"category":517,"image":700,"draft":521},"\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":431,"title":702,"description":703,"published":704,"category":567,"image":705,"draft":521},"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":707,"title":708,"description":709,"published":710,"category":517,"image":711,"draft":521},"\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":388,"title":713,"description":714,"published":715,"category":517,"image":716,"draft":521},"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":284,"title":718,"description":719,"published":720,"category":517,"image":721,"draft":521},"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":723,"title":724,"description":725,"published":726,"category":517,"image":727,"draft":521},"\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":729,"title":730,"description":731,"published":732,"category":567,"image":733,"draft":521},"\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":735,"title":736,"description":737,"published":738,"category":567,"image":739,"draft":521},"\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":741,"title":742,"description":743,"published":744,"category":517,"image":745,"draft":521},"\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":747,"title":748,"description":749,"published":750,"category":517,"image":751,"draft":521},"\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":753,"title":754,"description":755,"published":756,"category":567,"image":757,"draft":521},"\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":401,"title":759,"description":760,"published":761,"category":517,"image":762,"draft":521},"¿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":764,"title":765,"description":766,"published":761,"category":517,"image":767,"draft":521},"\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":769,"title":770,"description":771,"published":772,"category":517,"image":773,"draft":521},"\u002Fblog\u002Fsupabase-new-api-keys","Las nuevas claves de API de Supabase: ¿cuál va en tu app?","Supabase sustituyó anon y service_role por claves publicables y secretas. ¿Cuál pertenece a tu app y cuál no debe estar nunca?","2026-08-12","\u002Fblog\u002Fsupabase-new-api-keys\u002Fcard-800x500.png",{"path":426,"title":775,"description":776,"published":777,"category":567,"image":778,"draft":521},"¿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":780,"title":781,"description":782,"published":783,"category":517,"image":784,"draft":521},"\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":786,"title":787,"description":788,"published":783,"category":567,"image":789,"draft":521},"\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":791,"title":792,"description":793,"published":794,"category":567,"image":795,"draft":521},"\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":383,"title":797,"description":798,"published":794,"category":517,"image":799,"draft":521},"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",1790150954091]