[{"data":1,"prerenderedAt":923},["ShallowReactive",2],{"blog-es-move-a-secret-to-an-edge-function":3,"blog-index-es":571},{"id":4,"title":5,"body":6,"category":526,"cover":527,"coverAlt":528,"description":529,"draft":530,"extension":531,"faq":532,"image":549,"keywords":550,"meta":559,"navigation":560,"ogTitle":561,"path":562,"published":563,"seo":564,"stem":565,"tldr":566,"updated":563,"__hash__":570},"blog_es\u002Fblog\u002Fmove-a-secret-to-an-edge-function.md","Ocultar una clave API: muévela a una Edge Function de Supabase",{"type":7,"value":8,"toc":512},"minimark",[9,13,29,32,37,40,53,56,62,66,69,72,80,84,87,162,171,201,221,225,228,244,247,257,263,268,272,275,282,292,313,316,320,323,346,349,353,356,359,377,380,387,391,394,407,410,418,427,431,434,452,458,471,475,504],[10,11,12],"p",{},"Todas las guías sobre una clave API filtrada acaban en el mismo sitio: muévela a\nun servidor. Y ahí se paran. Te quedas con una app que construiste en Lovable,\nBolt o Cursor, ningún servidor a la vista, y una frase que da por hecho que ya\nsabes qué hacer a continuación.",[10,14,15,16,20,21,28],{},"Esto es lo que guía tras guía deja fuera. ",[17,18,19],"strong",{},"Mover la clave a una Edge Function de\nSupabase la saca de tu página, y por sí solo eso no impide que un desconocido la\nuse."," La propia documentación de Supabase dice por qué: la comprobación que una\nfunction desplegada ejecuta por defecto\n",[22,23,27],"a",{"href":24,"rel":25},"https:\u002F\u002Fsupabase.com\u002Fdocs\u002Fguides\u002Ffunctions\u002Fauth-headers",[26],"nofollow","acepta también tu clave pública",",\ny tu clave pública está en tu frontend, al alcance de cualquiera que quiera\ncopiarla. Ocultar la clave es la mitad sencilla. De la otra mitad no escribe\nnadie: decidir a quién responde tu function nueva.",[10,30,31],{},"Piensa en la clave como la llave maestra de un almacén. Ahora mismo está pegada\npor dentro del escaparate, donde la lee cualquiera que se pare. Una Edge Function\nes una trastienda con ventanilla: la llave entra ahí, y los visitantes preguntan\npor la ventanilla en vez de pasar y servirse. Que eso sea una mejora depende de a\nquién se le abre la ventanilla.",[33,34,36],"h2",{"id":35},"dónde-pongo-mi-clave-api-si-no-es-en-el-frontend","¿Dónde pongo mi clave API si no es en el frontend?",[10,38,39],{},"En cualquier sitio que no sea el navegador. Una Edge Function es la versión más\npequeña de eso.",[10,41,42,43,47,48,52],{},"Un navegador no sabe guardar un secreto. Todo lo que tu página necesita para\nfuncionar lo descarga cada visitante, y un visitante puede leerlo todo: el\ncódigo, las imágenes, los valores compilados dentro del código. Eso no es un\nfallo de tu herramienta. Es lo que es una página web.\n",[22,44,46],{"href":45},"\u002Fblog\u002Fwhich-api-keys-are-safe-in-your-frontend","Qué claves API son seguras en tu frontend","\nes la versión larga; la corta es que una clave ",[49,50,51],"code",{},"sk_",", una clave de OpenAI o una\nclave secreta de Supabase nunca iba a sobrevivir a un viaje al navegador.",[10,54,55],{},"Una Edge Function de Supabase es un trozo pequeño de código que se ejecuta en las\nmáquinas de Supabase. Puede leer los secretos que hayas puesto en tu proyecto,\nresponde en una dirección web propia, y tu app la llama por su nombre. Escribes\nun archivo, Supabase lo ejecuta, y no hay servidor que alquilar, parchear ni\nmantener despierto.",[57,58],"diagram",{"alt":59,"caption":60,"src":61},"Dos disposiciones. Arriba, la clave está dentro del bundle que descarga cada visitante y el navegador llama al proveedor directamente. Abajo, el bundle no lleva clave, el navegador llama a la Edge Function, y la function guarda la clave y llama al proveedor.","Arriba: la clave está en el archivo que descarga cada visitante, así que la tiene cada visitante. Abajo: el navegador le pregunta a la function, y la clave nunca sale de Supabase.","\u002Fblog\u002Fmove-a-secret-to-an-edge-function\u002Fwhere-the-key-lives-1600x800.png",[33,63,65],{"id":64},"sustituye-la-clave-que-ya-publicaste-antes-de-mover-nada","Sustituye la clave que ya publicaste, antes de mover nada",[10,67,68],{},"La clave que hoy está en tu frontend ya es pública, y lo sigue siendo después de\nque la saques del código.",[10,70,71],{},"Cada visitante que cargó tu sitio la descargó. Cada rastreador también, y los hay\nautomáticos recorriendo toda la web en busca exactamente de estas cadenas. Tu\nbundle antiguo sigue además en tu historial de versiones y en cualquier caché que\ntenga una copia de tu página. Borrar una línea de un archivo que controlas no\ncambia nada sobre un valor que ya se ha repartido.",[10,73,74,75,79],{},"Así que el orden es: crear una clave nueva en el proveedor, guardarla un momento,\ny revocar la antigua en cuanto la function de abajo esté en marcha. Lee después\ntu página de facturación y tus registros de uso de todo el periodo en que la\nclave antigua estuvo fuera. Sustituirla detiene lo que viene; no tiene ningún\nefecto sobre lo que ya pasó. Si la clave era una clave secreta de Supabase, ",[22,76,78],{"href":77},"\u002Fblog\u002Frotate-supabase-service-role-key","el\norden en que trabajar"," tiene un matiz que\nconviene leer antes.",[33,81,83],{"id":82},"los-cuatro-pasos","Los cuatro pasos",[10,85,86],{},"Guardar el secreto, escribir la function, desplegarla, y cambiar tu app para que\nllame a la function en vez de al proveedor.",[88,89,90,106],"table",{},[91,92,93],"thead",{},[94,95,96,100,103],"tr",{},[97,98,99],"th",{},"Paso",[97,101,102],{},"En la línea de comandos",[97,104,105],{},"En el panel",[107,108,109,123,136,149],"tbody",{},[94,110,111,115,120],{},[112,113,114],"td",{},"1. Guardar el secreto",[112,116,117],{},[49,118,119],{},"supabase secrets set MY_API_KEY=…",[112,121,122],{},"Edge Functions → Secrets, luego Key y Value",[94,124,125,128,133],{},[112,126,127],{},"2. Escribir la function",[112,129,130],{},[49,131,132],{},"supabase functions new forward-request",[112,134,135],{},"Edge Functions → Deploy a new function → Via Editor",[94,137,138,141,146],{},[112,139,140],{},"3. Desplegarla",[112,142,143],{},[49,144,145],{},"supabase functions deploy",[112,147,148],{},"El botón Deploy bajo el editor",[94,150,151,154,159],{},[112,152,153],{},"4. Llamarla desde tu app",[112,155,156],{},[49,157,158],{},"supabase.functions.invoke('…')",[112,160,161],{},"Igual, en el código de tu app",[10,163,164,165,170],{},"Dentro de la function el secreto llega como variable de entorno, es decir, un\nvalor con nombre que el código puede leer y nadie de fuera. La documentación de\nSupabase sobre\n",[22,166,169],{"href":167,"rel":168},"https:\u002F\u002Fsupabase.com\u002Fdocs\u002Fguides\u002Ffunctions\u002Fsecrets",[26],"secretos de Edge Functions","\ntiene la referencia completa, y tres detalles de ahí ahorran una hora de\nconfusión:",[172,173,174,185,191],"ul",{},[175,176,177,184],"li",{},[17,178,179,180,183],{},"El nombre de un secreto no puede empezar por ",[49,181,182],{},"SUPABASE_","."," Ese prefijo está\nreservado para los valores que Supabase pone por ti, y lo rechazan tanto el\npanel como la API.",[175,186,187,190],{},[17,188,189],{},"Un secreto nuevo se lee de inmediato."," No hay que volver a desplegar la\nfunction después de cambiar uno.",[175,192,193,196,197,200],{},[17,194,195],{},"Local y producción van por separado."," La pila local lee\n",[49,198,199],{},"supabase\u002Ffunctions\u002F.env",", que es un sitio distinto de los secretos de tu\nproyecto en marcha, así que pon el valor en ambos o la function funcionará en\ntu máquina y fallará una vez desplegada.",[10,202,203,204,207,208,211,212,215,216,220],{},"Borra después la variable antigua de tu frontend. Si se llamaba ",[49,205,206],{},"VITE_",",\n",[49,209,210],{},"NEXT_PUBLIC_"," o ",[49,213,214],{},"EXPO_PUBLIC_",", ese prefijo era la instrucción de compilar el\nvalor dentro del bundle, y ",[22,217,219],{"href":218},"\u002Fblog\u002Fvite-and-next-public-env-vars","el prefijo es toda la\nhistoria"," de cómo llegó ahí.",[33,222,224],{"id":223},"exigir-un-token-significa-que-solo-llaman-mis-usuarios","¿Exigir un token significa que solo llaman mis usuarios?",[10,226,227],{},"No, y este es el punto del artículo que merece dos lecturas.",[10,229,230,231,234,235,238,239,243],{},"Supabase activa por defecto una comprobación llamada ",[49,232,233],{},"verify_jwt",". Mira la\ncabecera ",[49,236,237],{},"Authorization"," antes de que corra tu código y rechaza la petición si\nahí no hay nada válido. Suena a puerta que solo abren tus usuarios. No lo es,\nporque Supabase documenta además que la misma comprobación\n",[22,240,242],{"href":24,"rel":241},[26],"acepta una clave pública o secreta","\nen cualquiera de las dos cabeceras, por compatibilidad con proyectos antiguos, y\nañade con claridad que la comprobación por sí sola no autentica a quien llama\nenviando únicamente una clave API.",[10,245,246],{},"Tu clave pública está en tu frontend. Eso es correcto y está pensado así. También\nsignifica que quien abra tu sitio, lea la clave y se la envíe a tu function nueva\npasa la comprobación de la plataforma y entra en tu código.",[248,249,251],"callout",{"type":250},"warn",[10,252,253,256],{},[17,254,255],{},"Así es como una clave movida se convierte en un proxy abierto."," La clave ya no\nestá en tu página, así que un escáner que busca cadenas con forma de clave no\nencuentra nada, y la factura de OpenAI o de Stripe sigue creciendo. Lo que ha\ncambiado es que ahora las peticiones pasan por ti. Antes un desconocido tenía tu\nclave; ahora un desconocido tiene una dirección web tuya que usa tu clave en su\nnombre, sin límite de veces.",[10,258,259,260,262],{},"En el almacén, ",[49,261,233],{}," es un portero que comprueba que llevas una\nacreditación de visitante. La lleva cada visitante, porque las repartes tú en la\npuerta. Quien está en la ventanilla hace otro trabajo, y es quien pregunta qué\nvisitante eres.",[57,264],{"alt":265,"caption":266,"src":267},"Dos llamantes llegan a la misma Edge Function. Un desconocido que envía solo la clave pública pasa la comprobación de plataforma verify_jwt y luego lo rechaza la comprobación de dentro de la function. Un usuario con sesión iniciada que envía un token de sesión pasa las dos y llega a la API del proveedor.","Los dos pasan la comprobación de la plataforma. Solo el segundo pasa una comprobación de quién llama, y esa segunda es la que tienes que añadir tú.","\u002Fblog\u002Fmove-a-secret-to-an-edge-function\u002Ftwo-checks-1600x860.png",[33,269,271],{"id":270},"cerrar-la-function-para-no-haber-montado-un-proxy-abierto","Cerrar la function, para no haber montado un proxy abierto",[10,273,274],{},"Decide a qué llamantes acepta tu function, y escribe esa decisión dentro de la\nfunction.",[10,276,277,278,281],{},"Supabase trae un envoltorio para esto, así que es una línea y no un proyecto. Pon\n",[49,279,280],{},"auth: 'user'"," y la function acepta el token de un usuario con sesión iniciada y\nle da a tu código un cliente de base de datos ya limitado a las reglas de Row\nLevel Security de ese usuario:",[283,284,290],"pre",{"className":285,"code":287,"language":288,"meta":289},[286],"language-ts","import { withSupabase } from 'npm:@supabase\u002Fserver@1'\n\nexport default {\n  fetch: withSupabase({ auth: 'user' }, async (_req, ctx) => {\n    \u002F\u002F ctx.userClaims dice quién llama. Rechaza aquí lo que no quieras,\n    \u002F\u002F y luego llama al proveedor con el secreto del entorno.\n    return Response.json({ ok: true })\n  }),\n}\n","ts","",[49,291,287],{"__ignoreMap":289},[10,293,294,295,300,301,304,305,308,309,312],{},"La página ",[22,296,299],{"href":297,"rel":298},"https:\u002F\u002Fsupabase.com\u002Fdocs\u002Fguides\u002Ffunctions\u002Fauth",[26],"Securing Edge\nFunctions"," enumera los demás\nmodos, y dos de ellos importan en una app corriente. Una function a la que llama\notra máquina y no un navegador usa ",[49,302,303],{},"auth: 'secret'",", con la clave secreta enviada\nen la cabecera ",[49,306,307],{},"apikey",". Una function que recibe webhooks de Stripe o de GitHub\nno puede usar ninguno de los dos, porque esos proveedores no tienen ningún token\ntuyo; pone ",[49,310,311],{},"verify_jwt = false"," y comprueba la firma del proveedor dentro del\nhandler.",[10,314,315],{},"Elijas lo que elijas, decide a conciencia qué pasa cuando aparece un llamante con\nel que no contabas. El ejemplo que da Supabase de una function que puede aceptar\na cualquiera es una comprobación de estado, y una comprobación de estado no\ncuesta nada cuando la llama un desconocido. Una function que reenvía una llamada\nde API de pago te factura cada una.",[33,317,319],{"id":318},"puedo-usar-service_role-en-una-edge-function","¿Puedo usar service_role en una Edge Function?",[10,321,322],{},"Sí. Una function es el único sitio donde una clave secreta de Supabase tiene\ncabida.",[10,324,325,326,329,330,333,334,329,337,340,341,345],{},"Tampoco tienes que pegarla. Supabase pone las claves del proyecto en el entorno\nde la function, así que el código las lee de ahí y no de un secreto que hayas\npuesto tú. Los proyectos nuevos reciben ",[49,327,328],{},"SUPABASE_SECRET_KEYS"," y\n",[49,331,332],{},"SUPABASE_PUBLISHABLE_KEYS",", cada uno un pequeño diccionario de claves con\nnombre; los proyectos más antiguos reciben ",[49,335,336],{},"SUPABASE_SERVICE_ROLE_KEY",[49,338,339],{},"SUPABASE_ANON_KEY"," con sus nombres de siempre. ",[22,342,344],{"href":343},"\u002Fblog\u002Fsupabase-new-api-keys","Qué cambió cuando Supabase\nrenombró sus claves"," te dice qué par tienes.",[10,347,348],{},"Usa la clave secreta para el trabajo que de verdad tiene que ver cada fila, como\nescribir un registro de auditoría que el usuario no pueda editar. Para todo lo\nque un usuario lee sobre sí mismo, usa su propio token y deja que filtren tus\nreglas de Row Level Security, que para eso están.",[33,350,352],{"id":351},"comprobarlo-desde-el-navegador-la-única-prueba-real","Comprobarlo desde el navegador, la única prueba real",[10,354,355],{},"Carga tu sitio en marcha, abre la pestaña de red, y mira qué envía de verdad tu\nnavegador.",[10,357,358],{},"Las dos cosas a comprobar:",[172,360,361,371],{},[175,362,363,366,367,370],{},[17,364,365],{},"Ninguna petición lleva la clave."," Recorre la parte de tu app que antes\nllamaba al proveedor. La petición saliente debe ir a\n",[49,368,369],{},"…supabase.co\u002Ffunctions\u002Fv1\u002Ftu-function",", y la única credencial que haya dentro\ndebe ser tu clave pública o el token de tu usuario.",[175,372,373,376],{},[17,374,375],{},"La clave no está en el código descargado."," Usa la búsqueda de tu navegador\nen todos los archivos cargados y pega la primera docena de caracteres de la\nclave antigua. Ningún resultado es la respuesta que quieres.",[10,378,379],{},"Solo una reconstrucción y un nuevo despliegue sacan un valor del bundle. Una\nclave que sigue apareciendo en la búsqueda suele significar que el frontend salió\nantes de que saliera la variable.",[10,381,382,383,183],{},"Nuestro escaneo gratuito hace la segunda comprobación desde fuera y nombra lo que\npuede leer en tu bundle en marcha, incluida qué clave de Supabase has publicado.\nTarda unos 20 segundos y no necesita cuenta:\n",[22,384,386],{"href":385},"\u002Fsecurity-scanner","escanea tu app",[33,388,390],{"id":389},"cómo-hacerlo-desde-lovable-bolt-o-replit","Cómo hacerlo desde Lovable, Bolt o Replit",[10,392,393],{},"Usa el panel de Supabase. Ahí no hay ningún paso de terminal.",[10,395,396,397,400,401,406],{},"Abre tu proyecto, elige Edge Functions en la barra lateral y luego ",[17,398,399],{},"Deploy a new\nfunction → Via Editor",". La ",[22,402,405],{"href":403,"rel":404},"https:\u002F\u002Fsupabase.com\u002Fdocs\u002Fguides\u002Ffunctions\u002Fquickstart-dashboard",[26],"guía rápida del\npanel"," de\nSupabase lo recorre con capturas, y entre las plantillas que ofrece hay una para\nreenviar a un proveedor de IA, que es exactamente la forma de este trabajo. El\ndespliegue tarda entre diez y treinta segundos, y la function queda en marcha en\nuna dirección propia. Tu secreto va en la página Edge Function Secrets, en la\nmisma sección.",[10,408,409],{},"Una advertencia que conviene conocer antes de confiarte: Supabase escribe que el\neditor del panel no tiene control de versiones, ni versiones, ni vuelta atrás, y\nlo recomienda para trabajo rápido y no para código que vaya a quedarse. Pulsar\nDeploy sobrescribe lo que había. Si la function acaba haciendo algo que te\nimporta, descárgala desde esa página y guarda el archivo.",[10,411,412,413,417],{},"Hay además una versión de esta pregunta con forma de Replit, porque Replit tiene\nsu propio almacén de secretos y tu app ejecuta allí su propio servidor. ",[22,414,416],{"href":415},"\u002Fblog\u002Freplit-secrets-explained","Qué\nhacen de verdad los Replit Secrets"," es esa\nversión.",[248,419,421],{"type":420},"note",[10,422,423,426],{},[17,424,425],{},"Si la llamada sale de un navegador, la function necesita cabeceras CORS."," Un\nnavegador no deja que tu página lea una respuesta de otra dirección salvo que esa\ndirección lo permita. Las plantillas del panel de Supabase traen las cabeceras;\nuna function escrita desde cero no, y el síntoma es una petición que se ve bien\nen los registros de la function y falla en tu app.",[33,428,430],{"id":429},"cuándo-una-edge-function-es-la-respuesta-equivocada","Cuándo una Edge Function es la respuesta equivocada",[10,432,433],{},"Dos casos, y en los dos la mudanza te cuesta trabajo y no aporta nada.",[10,435,436,439,440,443,444,447,448,451],{},[17,437,438],{},"La clave era pública por diseño."," Una clave ",[49,441,442],{},"pk_live_"," de Stripe, una clave\npública de Supabase o una clave ",[49,445,446],{},"anon",", un token público de Mapbox: están hechas\npara vivir en un navegador, y la protección está en otro sitio. Poner una detrás\nde una function añade un rodeo y no quita ningún riesgo. ",[22,449,450],{"href":45},"Cuáles son esas\nclaves"," es la lista.",[10,453,454,457],{},[17,455,456],{},"La clave se puede restringir a tu propio sitio."," Las claves de navegador de\nGoogle son el ejemplo habitual: en la consola de Google puedes limitar una a tus\ndominios, y esa es justo la solución que Google prevé para este caso. La clave\nsigue siendo legible y deja de servirle a quien la copie.",[10,459,460,461,465,466,470],{},"Todo lo demás va en un servidor. Una clave de OpenAI o de Anthropic no tiene\nvariante pública, y por eso ",[22,462,464],{"href":463},"\u002Fblog\u002Fopenai-api-key-exposed-in-frontend","una clave de proveedor de IA en tu\nfrontend"," no tiene ningún ajuste que la\nsalve, y ninguna minificación esconde una ",[22,467,469],{"href":468},"\u002Fblog\u002Fstripe-secret-key-in-frontend","clave secreta de\nStripe"," de quien lea tu página.",[33,472,474],{"id":473},"qué-hacer-ahora-mismo","Qué hacer ahora mismo",[476,477,478],"key-takeaways",{},[172,479,480,483,489,492,498,501],{},[175,481,482],{},"Sustituye la clave primero. Crea una nueva en el proveedor, ponla en el secreto de la function, y revoca la antigua en cuanto la function esté en marcha.",[175,484,485,486,488],{},"Guarda el secreto en tu proyecto de Supabase, no en tu repositorio y no en una variable ",[49,487,206],{},". Ponlo por separado para desarrollo local y para producción.",[175,490,491],{},"Despliega una function que use el secreto, y cambia tu app para que llame a la function por su nombre en vez de al proveedor.",[175,493,494,495,497],{},"Añade una comprobación de quién llama. ",[49,496,233],{}," por sí solo acepta la clave pública de tu propio frontend, así que no deja fuera a un desconocido.",[175,499,500],{},"Borra la variable antigua, reconstruye, y confirma en la pestaña de red de tu navegador que nada de lo que sale lleva la clave.",[175,502,503],{},"Lee la facturación y el uso de todo el periodo en que la clave antigua fue pública. Sustituirla detiene el próximo cargo y no el último.",[10,505,506,507,511],{},"Si prefieres repasar tu app entera en vez de una sola clave, la\n",[22,508,510],{"href":509},"\u002Fchecklist","checklist de seguridad en 10 minutos"," cubre esto junto a las otras\ncosas que conviene cerrar en una app recién lanzada.",{"title":289,"searchDepth":513,"depth":513,"links":514},3,[515,517,518,519,520,521,522,523,524,525],{"id":35,"depth":516,"text":36},2,{"id":64,"depth":516,"text":65},{"id":82,"depth":516,"text":83},{"id":223,"depth":516,"text":224},{"id":270,"depth":516,"text":271},{"id":318,"depth":516,"text":319},{"id":351,"depth":516,"text":352},{"id":389,"depth":516,"text":390},{"id":429,"depth":516,"text":430},{"id":473,"depth":516,"text":474},"Fundamentos de seguridad","\u002Fblog\u002Fmove-a-secret-to-an-edge-function\u002Fcover-1200x630.png","Una hoja con un agujero en forma de llave, y detrás una caja sellada con un candado cerrado y la llave visible tras una ventanilla.","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.",false,"md",[533,535,538,541,544,546],{"q":36,"a":534},"En cualquier sitio que no sea el navegador. Una Edge Function de Supabase es la opción más pequeña: guardas la clave como secreto en tu proyecto, escribes un archivo pequeño que la usa, y tu app llama a esa function por su nombre en lugar de llamar al proveedor directamente. Una ruta serverless en tu hosting o un servidor tuyo funcionan igual. Lo que tienen en común es que la clave se lee donde tu visitante no la ve.",{"q":536,"a":537},"¿Qué es una Edge Function de Supabase?","Un trozo pequeño de código que se ejecuta en las máquinas de Supabase y no en el navegador de tu visitante. Responde en una dirección web propia, puede leer los secretos que hayas puesto en tu proyecto, y tu app lo llama con supabase.functions.invoke. Escribes un archivo, Supabase lo ejecuta, y no hay ningún servidor que alquilar ni mantener.",{"q":539,"a":540},"¿Tengo que sustituir mi clave después de moverla?","Sí, y primero. La clave que estaba en tu frontend la ha descargado cada visitante y cada rastreador que leyó tu sitio, y sacarla del código no cambia nada sobre esas copias. Crea una clave nueva en el proveedor, ponla en el secreto de tu Edge Function y revoca la antigua. Después revisa la facturación y el uso de todo el periodo en que la clave antigua estuvo activa.",{"q":542,"a":543},"¿Puede cualquiera llamar a mi Edge Function?","Por defecto una function rechaza una petición sin ningún token, pero esa comprobación es más débil de lo que suena. Supabase documenta que la comprobación de la plataforma acepta también tu clave pública o secreta, y tu clave pública está en tu frontend, donde cualquiera puede leerla. Así que la comprobación detiene una petición vacía y no una decidida. Para aceptar solo a tus usuarios con sesión iniciada, verifica a quien llama dentro de la function.",{"q":319,"a":545},"Sí. Una Edge Function es el único sitio donde una clave secreta de Supabase tiene cabida, y Supabase pone las claves del proyecto en el entorno de la function por ti, así que nunca pegas ninguna. Úsala para el trabajo que tiene que ver cada fila, y usa el token de quien llama para todo lo que deba respetar tus reglas de Row Level Security.",{"q":547,"a":548},"¿Puedo hacer esto sin terminal?","Sí. El panel de Supabase tiene una sección Edge Functions donde escribes una function en el navegador, pulsas Deploy y pones tu secreto en la página Edge Function Secrets. Supabase advierte de que el editor del panel no guarda historial de versiones, así que lo recomienda para trabajo rápido y deja la línea de comandos para lo que pienses conservar.","\u002Fblog\u002Fmove-a-secret-to-an-edge-function\u002Fcard-800x500.png",[551,552,553,554,555,556,557,558],"ocultar clave api edge function supabase","supabase edge function secrets","supabase secrets set","mover clave api al backend","llamar a openai desde una edge function","mantener la clave api fuera del frontend","variables de entorno edge function supabase","dónde poner una clave secreta",{},true,"Ocultar una clave API en una Edge Function de Supabase","\u002Fblog\u002Fmove-a-secret-to-an-edge-function","2026-10-05",{"title":5,"description":529},"blog\u002Fmove-a-secret-to-an-edge-function",[567,568,569],"Nada en un navegador sabe guardar un secreto, así que ocultar una clave API significa moverla a algo que sí sepa. Una Edge Function de Supabase es el servidor más pequeño que puedes tener.","La mudanza son cuatro pasos. Sustituye antes la clave que ya publicaste, porque la copia que hay en tu bundle antiguo sigue siendo legible después de que la quites.","Una function desplegada exige un token por defecto, y la clave pública de tu propio frontend cumple esa exigencia. Comprobar quién llama es un paso aparte, y es el que impide que un desconocido gaste tu cuota.","uK40z1Gdthjxurr4zWG9Adzisa652Ef0Bxg1IbCxAYo",[572,578,584,590,596,602,608,609,615,621,627,633,639,645,652,658,664,669,675,681,687,693,699,705,710,715,721,727,733,739,745,751,757,763,769,774,780,786,792,798,803,809,815,820,826,832,838,844,850,856,862,868,874,880,886,891,896,902,908,913,919],{"path":573,"title":574,"description":575,"published":576,"category":526,"image":577,"draft":530},"\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":579,"title":580,"description":581,"published":582,"category":526,"image":583,"draft":530},"\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":585,"title":586,"description":587,"published":588,"category":526,"image":589,"draft":530},"\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":591,"title":592,"description":593,"published":594,"category":526,"image":595,"draft":530},"\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":597,"title":598,"description":599,"published":600,"category":526,"image":601,"draft":530},"\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":603,"title":604,"description":605,"published":606,"category":526,"image":607,"draft":530},"\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":562,"title":5,"description":529,"published":563,"category":526,"image":549,"draft":530},{"path":610,"title":611,"description":612,"published":613,"category":526,"image":614,"draft":530},"\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":616,"title":617,"description":618,"published":619,"category":526,"image":620,"draft":530},"\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":622,"title":623,"description":624,"published":625,"category":526,"image":626,"draft":530},"\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":628,"title":629,"description":630,"published":631,"category":526,"image":632,"draft":530},"\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":634,"title":635,"description":636,"published":637,"category":526,"image":638,"draft":530},"\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":640,"title":641,"description":642,"published":643,"category":526,"image":644,"draft":530},"\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":646,"title":647,"description":648,"published":649,"category":650,"image":651,"draft":530},"\u002Fblog\u002Ftest-your-supabase-backup","Prueba tu backup de Supabase antes del día que lo necesites","Cómo probar tu backup de Supabase: restaurarlo en un proyecto de prueba, contar filas, iniciar sesión y buscar la línea que le falta a un archivo cortado.","2026-09-28","Backups","\u002Fblog\u002Ftest-your-supabase-backup\u002Fcard-800x500.png",{"path":653,"title":654,"description":655,"published":656,"category":650,"image":657,"draft":530},"\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":659,"title":660,"description":661,"published":662,"category":650,"image":663,"draft":530},"\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":665,"title":666,"description":667,"published":662,"category":526,"image":668,"draft":530},"\u002Fblog\u002Fsupabase-permission-denied-for-table","Supabase \"permission denied for table\": el grant que falta","Desde el 30 de octubre, una tabla nueva de Supabase responde \"permission denied for table\" hasta que le das acceso. El grant del email es la mitad del arreglo.","\u002Fblog\u002Fsupabase-permission-denied-for-table\u002Fcard-800x500.png",{"path":670,"title":671,"description":672,"published":673,"category":526,"image":674,"draft":530},"\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":676,"title":677,"description":678,"published":679,"category":526,"image":680,"draft":530},"\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":682,"title":683,"description":684,"published":685,"category":650,"image":686,"draft":530},"\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":688,"title":689,"description":690,"published":691,"category":526,"image":692,"draft":530},"\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":694,"title":695,"description":696,"published":697,"category":526,"image":698,"draft":530},"\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":700,"title":701,"description":702,"published":703,"category":526,"image":704,"draft":530},"\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":468,"title":706,"description":707,"published":708,"category":526,"image":709,"draft":530},"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":218,"title":711,"description":712,"published":713,"category":526,"image":714,"draft":530},"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":716,"title":717,"description":718,"published":719,"category":526,"image":720,"draft":530},"\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":722,"title":723,"description":724,"published":725,"category":526,"image":726,"draft":530},"\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":728,"title":729,"description":730,"published":731,"category":526,"image":732,"draft":530},"\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":734,"title":735,"description":736,"published":737,"category":650,"image":738,"draft":530},"\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":740,"title":741,"description":742,"published":743,"category":650,"image":744,"draft":530},"\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":746,"title":747,"description":748,"published":749,"category":650,"image":750,"draft":530},"\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":752,"title":753,"description":754,"published":755,"category":526,"image":756,"draft":530},"\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":758,"title":759,"description":760,"published":761,"category":526,"image":762,"draft":530},"\u002Fblog\u002Fenable-rls-on-every-supabase-table","Activa Row Level Security en cada tabla de Supabase y compruébalo","Activar Row Level Security en Supabase sin política cierra la tabla del todo. Una política sin el interruptor no hace nada. Aquí tienes el SQL y la prueba.","2026-09-10","\u002Fblog\u002Fenable-rls-on-every-supabase-table\u002Fcard-800x500.png",{"path":764,"title":765,"description":766,"published":767,"category":526,"image":768,"draft":530},"\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":77,"title":770,"description":771,"published":772,"category":526,"image":773,"draft":530},"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":775,"title":776,"description":777,"published":778,"category":526,"image":779,"draft":530},"\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":781,"title":782,"description":783,"published":784,"category":526,"image":785,"draft":530},"\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":787,"title":788,"description":789,"published":790,"category":526,"image":791,"draft":530},"\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":793,"title":794,"description":795,"published":796,"category":526,"image":797,"draft":530},"\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":463,"title":799,"description":800,"published":801,"category":526,"image":802,"draft":530},"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":804,"title":805,"description":806,"published":807,"category":526,"image":808,"draft":530},"\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":810,"title":811,"description":812,"published":813,"category":650,"image":814,"draft":530},"\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":415,"title":816,"description":817,"published":818,"category":526,"image":819,"draft":530},"Cómo usar secrets en Replit, y qué se publica de todos modos","Cómo usar secrets en Replit: añadir uno, leerlo y arreglar las dos causas del undefined. Y las claves que la herramienta Secrets no puede mantener privadas.","2026-08-31","\u002Fblog\u002Freplit-secrets-explained\u002Fcard-800x500.png",{"path":821,"title":822,"description":823,"published":824,"category":650,"image":825,"draft":530},"\u002Fblog\u002Fback-up-supabase-free-tier","Backups del plan gratuito de Supabase: cómo hacer uno sin terminal","No hay backups en el plan gratuito de Supabase: la copia tienes que hacerla tú. Cómo sacarla desde el panel y qué deja fuera la vía CSV.","2026-08-30","\u002Fblog\u002Fback-up-supabase-free-tier\u002Fcard-800x500.png",{"path":827,"title":828,"description":829,"published":830,"category":526,"image":831,"draft":530},"\u002Fblog\u002Fis-supabase-secure","¿Supabase es seguro? Sí. Tu proyecto es otra pregunta","¿Supabase es seguro? La plataforma está auditada, cifrada y sometida a pentests. Su propia documentación dice dónde termina eso y empiezan tus ajustes.","2026-08-29","\u002Fblog\u002Fis-supabase-secure\u002Fcard-800x500.png",{"path":833,"title":834,"description":835,"published":836,"category":526,"image":837,"draft":530},"\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":839,"title":840,"description":841,"published":842,"category":526,"image":843,"draft":530},"\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":845,"title":846,"description":847,"published":848,"category":526,"image":849,"draft":530},"\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":851,"title":852,"description":853,"published":854,"category":650,"image":855,"draft":530},"\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":857,"title":858,"description":859,"published":860,"category":650,"image":861,"draft":530},"\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":863,"title":864,"description":865,"published":866,"category":526,"image":867,"draft":530},"\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":869,"title":870,"description":871,"published":872,"category":526,"image":873,"draft":530},"\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":875,"title":876,"description":877,"published":878,"category":650,"image":879,"draft":530},"\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":881,"title":882,"description":883,"published":884,"category":526,"image":885,"draft":530},"\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":887,"title":888,"description":889,"published":884,"category":526,"image":890,"draft":530},"\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":343,"title":892,"description":893,"published":894,"category":526,"image":895,"draft":530},"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":897,"title":898,"description":899,"published":900,"category":650,"image":901,"draft":530},"\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":903,"title":904,"description":905,"published":906,"category":526,"image":907,"draft":530},"\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":909,"title":910,"description":911,"published":906,"category":650,"image":912,"draft":530},"\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":914,"title":915,"description":916,"published":917,"category":650,"image":918,"draft":530},"\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":45,"title":920,"description":921,"published":917,"category":526,"image":922,"draft":530},"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",1791705962971]