[{"data":1,"prerenderedAt":1129},["ShallowReactive",2],{"blog-es-supabase-invalid-jwt":3,"blog-index-es":603},{"id":4,"title":5,"body":6,"category":561,"cover":562,"coverAlt":563,"description":564,"draft":565,"extension":566,"faq":567,"image":583,"keywords":584,"meta":592,"navigation":593,"ogTitle":5,"path":594,"published":595,"seo":596,"stem":597,"tldr":598,"updated":595,"__hash__":602},"blog_es\u002Fblog\u002Fsupabase-invalid-jwt.md","Supabase invalid JWT: ¿qué clave dejó de funcionar?",{"type":7,"value":8,"toc":548},"minimark",[9,13,24,27,39,44,47,50,53,56,62,66,73,87,167,172,179,200,207,211,221,233,236,262,267,270,282,293,297,300,312,328,332,335,367,371,374,396,399,406,410,413,429,432,440,443,447,453,462,476,486,503,507,536],[10,11,12],"p",{},"Ayer tu aplicación funcionaba. Esta mañana cada petición vuelve con un 401, en tu\ncódigo no ha cambiado nada, y la respuesta habla de un JWT, algo que tú no has\nescrito nunca en ningún sitio.",[14,15,20],"pre",{"className":16,"code":18,"language":19},[17],"language-text","{\n  \"code\": \"PGRST301\",\n  \"message\": \"JWT cryptographic operation failed\"\n}\n","text",[21,22,18],"code",{"__ignoreMap":23},"",[10,25,26],{},"Un error Supabase invalid JWT es el que aparece después de hacer lo correcto.\nRotaste una clave porque una guía te lo dijo, o pasaste tu proyecto a las claves\nnuevas, y la aplicación se quedó callada.",[10,28,29,30,34,35,38],{},"Y aquí es donde guía tras guía se equivoca: ",[31,32,33],"strong",{},"la clave de la que habla este\nmensaje a menudo no es la que acabas de cambiar."," Busca el error y te mandarán a\nrevisar tu clave ",[21,36,37],{},"anon",". En un proyecto que ya se pasó a las claves publishable y\nsecret, la clave de API no es un token firmado en absoluto, así que lo que no\npasó la verificación era otra cosa de la petición.",[40,41,43],"h2",{"id":42},"qué-significa-un-error-supabase-invalid-jwt","¿Qué significa un error Supabase invalid JWT?",[10,45,46],{},"Algo de tu petición era un token firmado, y la firma no coincidió.",[10,48,49],{},"Una contraseña es una palabra que alguien busca en una lista. Un JWT no es eso.\nEs una carta cerrada con lacre: la persona de la puerta no te busca en ningún\nsitio, sostiene un sello y comprueba si el lacre de tu carta se hizo con él. Si\nes así, pasas, y nadie lee ninguna lista.",[10,51,52],{},"Una carta cerrada con el sello equivocado se rechaza aunque cada palabra de\ndentro sea correcta. Y en cuanto la puerta cambia de sello, ninguna carta que ya\nesté en camino se abre, sin que nadie haya tocado las cartas.",[10,54,55],{},"Tu petición es la carta. Tu proyecto de Supabase sostiene el sello. Un 401 aquí\nsignifica que los dos no coincidieron.",[57,58],"diagram",{"alt":59,"caption":60,"src":61},"Dos carriles que llevan cada uno una carta lacrada al mismo sello, grabado con una marca de tres radios. Arriba el lacre de la carta lleva la misma marca, la carta pasa, la tabla del fondo muestra tres filas iluminadas y la respuesta es 200. Abajo el lacre lleva un punto dentro de un cuadrado, una barra ámbar detiene la carta, la tabla del fondo es un contorno punteado vacío y la respuesta es 401.","No se busca nada en ninguna lista. El lacre del token se compara con el sello que guarda tu proyecto, y una carta cuyo lacre lo hizo otra cosa se rechaza diga lo que diga por dentro.","\u002Fblog\u002Fsupabase-invalid-jwt\u002Fhow-the-check-works-1600x660.png",[40,63,65],{"id":64},"el-mensaje-cambió-en-postgrest-13-y-el-código-no","El mensaje cambió en PostgREST 13 y el código no",[10,67,68,69,72],{},"Comprueba ",[21,70,71],{},"PGRST301",". La frase que lo acompaña depende de la versión de PostgREST\nque ejecute tu proyecto, y las más nuevas te dan una lista corta de ellas.",[10,74,75,76,78,79,82,83,86],{},"La referencia de errores de PostgREST describe ",[21,77,71],{}," como un JWT recibido\nque no se pudo decodificar o que no es válido, con HTTP 401, tanto en la versión\n13 como en la 14. Esa frase es la descripción de la tabla de documentación. No es\nlo que aterriza en tu campo ",[21,80,81],{},"message",". Leyendo ",[21,84,85],{},"Error.hs"," en la rama main de\nPostgREST, ese campo lleva una de estas:",[88,89,90,103],"table",{},[91,92,93],"thead",{},[94,95,96,100],"tr",{},[97,98,99],"th",{},"Lo que dice el mensaje",[97,101,102],{},"Lo que te dice",[104,105,106,117,127,137,147,157],"tbody",{},[94,107,108,114],{},[109,110,111],"td",{},[21,112,113],{},"Empty JWT is sent in Authorization header",[109,115,116],{},"La cabecera sí se envió y venía vacía.",[94,118,119,124],{},[109,120,121],{},[21,122,123],{},"Expected 3 parts in JWT; got 1",[109,125,126],{},"Lo que llegó no es un JWT. Un JWT son tres bloques separados por puntos.",[94,128,129,134],{},[109,130,131],{},[21,132,133],{},"No suitable key or wrong key type",[109,135,136],{},"El servidor no tiene ninguna clave capaz de verificar un token de ese tipo.",[94,138,139,144],{},[109,140,141],{},[21,142,143],{},"Wrong or unsupported encoding algorithm",[109,145,146],{},"El token nombra un algoritmo de firma que el servidor no usa.",[94,148,149,154],{},[109,150,151],{},[21,152,153],{},"JWT cryptographic operation failed",[109,155,156],{},"El token está bien formado y la firma no coincide.",[94,158,159,164],{},[109,160,161],{},[21,162,163],{},"Unsupported token type",[109,165,166],{},"El token tiene una forma que PostgREST no lee.",[10,168,169,171],{},[21,170,153],{}," es el que significa clave caducada. El token\nse construyó bien y lo firmó algo que el proyecto ya no tiene, que es\nexactamente lo que deja atrás una rotación.",[10,173,174,175,178],{},"Los proyectos antiguos ven algo muy distinto, y de ahí viene la cadena que\naparece por todas partes en los resultados de búsqueda. Antes de la versión 13,\nPostgREST imprimía lo que había dicho su biblioteca de JWT, tal cual. De ahí sale\n",[21,176,177],{},"JWSError JWSInvalidSignature",": es un valor de biblioteca impreso en pantalla, no\nuna frase que alguien escribiera para ti. El changelog de PostgREST de la 13.0.0\ndescribe el cambio como mensajes de error diagnósticos en lugar de internos\nexpuestos.",[180,181,183],"callout",{"type":182},"warn",[10,184,185,188,189,192,193,195,196,199],{},[31,186,187],{},"Si tu propio código compara el texto del mensaje, compara el código en su\nlugar."," Una comprobación que mide ",[21,190,191],{},"error.message"," contra\n",[21,194,177],{}," funciona hoy y deja de funcionar el día en que tu\nproyecto pase a un PostgREST más nuevo, sin un despliegue tuyo de por medio.\nSupabase dice lo mismo en su propia documentación de errores: usa ",[21,197,198],{},"error.code"," y\nno compares los mensajes de error como texto.",[10,201,202,203,206],{},"Vale la pena conocer un código vecino mientras estás aquí. ",[21,204,205],{},"PGRST303",", también\n401, es lo que recibes cuando la firma se verificó y el contenido del token fue\nrechazado: caducado, todavía no válido, o con la audiencia equivocada. Si ese es\ntu código, deja de mirar las claves y ve a mirar el reloj y la sesión.",[40,208,210],{"id":209},"qué-clave-es-la-de-api-o-el-token-de-tu-usuario","¿Qué clave es: la de API o el token de tu usuario?",[10,212,213,214,216,217,220],{},"Depende de si tu proyecto todavía usa ",[21,215,37],{}," y ",[21,218,219],{},"service_role",".",[10,222,223,224,216,226,228,229,232],{},"La documentación de Supabase sobre claves de API es explícita en la diferencia.\nLas claves antiguas ",[21,225,37],{},[21,227,219],{}," son JWT de larga duración, y por eso\nempiezan por ",[21,230,231],{},"eyJ",". Las claves publishable y secret más nuevas son cadenas cortas\ny no son JWT en absoluto.",[10,234,235],{},"Ese único hecho parte el error en dos:",[237,238,239,250],"ul",{},[240,241,242,249],"li",{},[31,243,244,245,216,247,220],{},"Tu proyecto sigue usando ",[21,246,37],{},[21,248,219],{}," La clave de API de tu\npetición es en sí misma un token firmado, así que una clave de otro proyecto,\nuna clave de antes de la rotación o un pegado truncado fallan la verificación\nde firma igual que lo haría una sesión mala. Cualquiera de los dos tokens de la\npetición puede ser el que falló.",[240,251,252,261],{},[31,253,254,255,216,258,220],{},"Tu proyecto ya usa ",[21,256,257],{},"sb_publishable_",[21,259,260],{},"sb_secret_"," La clave de API ha\ndejado de ser un token, así que no hay ninguna firma en ella que algo pueda\nrechazar. Lo que queda por verificar es el token de acceso de tu usuario con\nsesión iniciada, que Supabase Auth emite aparte de la clave de API.",[57,263],{"alt":264,"caption":265,"src":266},"Dos filas que terminan cada una en un sello. En la fila de arriba, una carta lacrada con la etiqueta anon y una segunda carta lacrada junto a una persona van las dos hacia el sello. En la fila de abajo, una tarjeta lisa con la etiqueta sb_publishable_ no lleva lacre ni línea, y solo la carta lacrada que está junto a la persona va hacia el sello.","En un proyecto antiguo llegan dos cartas lacradas y cualquiera de las dos puede ser la rechazada. Tras pasar a las claves nuevas, la clave de API ya no lleva lacre, y la sesión de tu usuario es lo único que conserva una firma.","\u002Fblog\u002Fsupabase-invalid-jwt\u002Ftwo-things-called-your-key-1600x820.png",[10,268,269],{},"En la práctica, la primera pregunta no es qué clave está mal. Es si había alguien\ncon la sesión iniciada cuando falló la petición. Si una página sin sesión\nfunciona y una con sesión no, estás mirando sesiones. Si falla todo, incluida una\npágina que no pide nada personal, estás mirando la clave de API, y tu proyecto es\nde los antiguos.",[10,271,272,273,276,277,220],{},"Si son sesiones y tu log de Auth dice además ",[21,274,275],{},"Invalid Refresh Token",", a tus\nusuarios se les cierra la sesión porque\n",[278,279,281],"a",{"href":280},"\u002Fblog\u002Fsupabase-users-logged-out","terminó la sesión detrás de su token",[10,283,284,285,288,289,220],{},"Si la respuesta vino de Supabase Auth y dice ",[21,286,287],{},"invalid claim: missing sub claim",",\nel token pasó la comprobación de su firma y no nombraba a nadie. Es una clave\nenviada donde va el token de una persona, y\n",[278,290,292],{"href":291},"\u002Fblog\u002Finvalid-claim-missing-sub-claim","ese mensaje tiene su propio artículo",[40,294,296],{"id":295},"invalid-jwt-no-es-no-api-key-found-in-request","Invalid JWT no es \"No API key found in request\"",[10,298,299],{},"Son dos mensajes distintos con dos causas distintas, y las soluciones no se\nparecen en nada.",[10,301,302,303,307,308,311],{},"Un invalid JWT significa que llegó un token y la firma no coincidió. ",[278,304,306],{"href":305},"\u002Fblog\u002Fno-api-key-found-in-request","\"No API key\nfound in request\""," significa que no llegó\nninguna cabecera ",[21,309,310],{},"apikey",", así que la petición se rechazó en la pasarela antes de\npreguntar nada a tu base de datos.",[10,313,314,315,317,318,321,322,324,325,327],{},"La propia respuesta aclara cuál tienes. Una clave que falta la rechaza la\npasarela que hay delante de tu proyecto, así que el cuerpo lleva un ",[21,316,81],{}," y\nun ",[21,319,320],{},"hint"," y ningún ",[21,323,21],{},". Un invalid JWT llegó lo bastante lejos como para que\nlo leyera PostgREST, y por eso vuelve con ",[21,326,71],{}," escrito.",[40,329,331],{"id":330},"dónde-sigue-estando-la-clave-antigua-por-orden","Dónde sigue estando la clave antigua, por orden",[10,333,334],{},"Cuatro sitios, y conviene mirarlos en este orden porque es más o menos la\nfrecuencia con la que cada uno es la respuesta.",[336,337,338,344,355,361],"ol",{},[240,339,340,343],{},[31,341,342],{},"Los ajustes de proyecto de tu herramienta."," Lovable, Bolt, v0 y Replit\nguardan cada uno su propio juego de variables de entorno, y si construiste con\nCursor o Claude Code ese mismo juego está donde despliegues. Es la copia que\ncasi todo el mundo actualiza, y la única que casi todo el mundo actualiza.",[240,345,346,349,350,354],{},[31,347,348],{},"Una clave escrita directamente en el código."," Viaja dentro del JavaScript\nque descargan tus visitantes, así que cambiarla en el panel no cambia nada\nhasta que se edite el archivo y se reconstruya el sitio. ",[278,351,353],{"href":352},"\u002Fblog\u002Fwhere-to-find-supabase-api-keys","Dónde está esa\npágina y qué hace cada valor",", si no la\nhas abierto.",[240,356,357,360],{},[31,358,359],{},"Los secretos de Edge Function."," Una función guarda su propio juego,\nseparado del de tu frontend, así que sigue usando la clave antigua cuando todo\nlo demás ya ha cambiado. Supabase también advierte de que una función con el\najuste Verify JWT activado puede romperse al rotar una clave de firma.",[240,362,363,366],{},[31,364,365],{},"Un despliegue que nadie ha reconstruido."," La clave quedó incrustada en la\núltima construcción, así que la copia en producción la sigue enviando hasta\nque algo dispare una nueva. Tu panel enseña la clave nueva todo el rato, y eso\nes lo que hace difícil ver este caso: el sitio donde vas a comprobarlo es el\núnico que ya está bien.",[40,368,370],{"id":369},"qué-clave-envía-de-verdad-tu-aplicación-en-producción","Qué clave envía de verdad tu aplicación en producción",[10,372,373],{},"Abre el sitio publicado en lugar de la vista previa y lee qué envía la página.",[10,375,376,377,379,380,382,383,385,386,390,391,395],{},"Una clave antigua de Supabase es una cadena larga que empieza por ",[21,378,231],{},". Las\nnuevas se anuncian en sus primeros caracteres: ",[21,381,257],{}," va en tu\naplicación y ",[21,384,260],{}," nunca. Si encuentras la segunda en una página que puede\ncargar un visitante, deja este artículo y\n",[278,387,389],{"href":388},"\u002Fblog\u002Frotate-supabase-service-role-key","rótala"," antes de hacer nada más. Cuál de\nlas dos tienes, y\n",[278,392,394],{"href":393},"\u002Fblog\u002Fsupabase-new-api-keys","qué cambió cuando Supabase les puso nombres\nnuevos",", es lo que conviene resolver antes de\nempezar a sustituir valores.",[10,397,398],{},"Para ver qué emite tu proyecto ahora mismo: todas las claves viven ya en un solo\nsitio del panel, en Settings y luego API Keys, las antiguas incluidas.",[10,400,401,402,220],{},"Si prefieres no leer un bundle minificado, nuestro escaneo gratuito lee tu\naplicación en producción como lo haría un desconocido y te dice qué claves se ven\ndesde fuera. Tarda unos 20 segundos y no necesita cuenta:\n",[278,403,405],{"href":404},"\u002Fsecurity-scanner","escanea tu aplicación",[40,407,409],{"id":408},"rotar-una-clave-de-firma-sin-cerrar-la-sesión-de-todos","Rotar una clave de firma sin cerrar la sesión de todos",[10,411,412],{},"Cuál de los dos caminos de rotación tomes decide si tus usuarios lo notan.",[10,414,415,416,419,420,216,422,424,425,428],{},"La documentación de Supabase sobre claves de firma los pone uno al lado del otro.\nRotar el ",[31,417,418],{},"secreto JWT antiguo"," cierra la sesión de todos los usuarios activos\nde inmediato, y ",[21,421,37],{},[21,423,219],{}," hay que rotarlas a la vez. Las ",[31,426,427],{},"JWT\nsigning keys"," más nuevas funcionan de otra manera: una clave nueva empieza como\nstandby, al rotar pasa a ser la actual, y la que sustituye pasa a ser la anterior.\nMientras eso sea así, se siguen aceptando las firmas de las dos claves, y las\ncartas lacradas la semana pasada todavía se abren. Revocar la clave antigua es el\npaso aparte que pone fin a eso.",[10,430,431],{},"Dos avisos de esa misma documentación son los que pillan a la gente:",[237,433,434,437],{},[240,435,436],{},"Un componente que verifica los tokens por su cuenta contra el secreto JWT\nantiguo, con una biblioteca como jose o jsonwebtoken, se rompe al rotar.\nSostiene el sello viejo y nadie le ha dicho que lo cambie.",[240,438,439],{},"Una Edge Function con Verify JWT activado puede romperse por lo mismo.",[10,441,442],{},"Crear claves publishable y secret tampoco revoca tus claves antiguas. Siguen\nfuncionando hasta que las desactivas, algo que la documentación señala como un\npaso aparte. Así que una aplicación que solo ha movido la mitad de sus copias\nsigue funcionando durante semanas, y el día en que alguien desactiva el par\nantiguo es el día en que la otra mitad empieza a responder 401.",[40,444,446],{"id":445},"vigilar-una-aplicación-que-ha-empezado-a-responder-401","Vigilar una aplicación que ha empezado a responder 401",[10,448,449,452],{},[31,450,451],{},"Una aplicación que devuelve 401 a cada petición sigue en pie, sigue sirviendo\nuna página y sigue respondiendo a cualquier comprobación de salud."," En tu panel\nno se pone nada en rojo. Te enteras cuando alguien te escribe.",[10,454,455],{},[31,456,457,461],{},[278,458,460],{"href":459},"\u002Fwebsite-monitoring","Reeve Monitor"," se entera primero:",[237,463,464,467,470,473],{},[240,465,466],{},"las nueve comprobaciones cada hora, en hasta tres aplicaciones",[240,468,469],{},"un correo el día en que un nuevo escaneo puntúa tu aplicación peor que el anterior",[240,471,472],{},"si la aplicación responde, cada 60 segundos",[240,474,475],{},"un informe mensual de lo que vio",[10,477,478],{},[31,479,480,481,485],{},"Una clave que tuviste que rotar con prisa suele ser una clave que estaba donde\nno debía, y desde aquí no puedes ver qué se leyó o se cambió con ella mientras\nestuvo fuera. ",[278,482,484],{"href":483},"\u002Fsupabase-backups","Reeve Care"," guarda una copia de tu base de\ndatos de Supabase anterior a eso.",[237,487,488,491,494,497,500],{},[240,489,490],{},"una copia cifrada según un calendario, guardada en el almacenamiento propio de Reeve en la UE, fuera de tu cuenta de Supabase",[240,492,493],{},"cada copia verificada antes de contar, contando las filas de cada tabla",[240,495,496],{},"una restauración con un clic que guarda lo que hay ahora antes de reemplazarlo",[240,498,499],{},"también tus archivos subidos, en cuanto conectes un acceso de Storage",[240,501,502],{},"todo lo que hace Monitor",[40,504,506],{"id":505},"qué-hacer-hoy","Qué hacer hoy",[508,509,510],"key-takeaways",{},[237,511,512,524,527,530,533],{},[240,513,514,515,517,518,520,521,523],{},"Lee el campo ",[21,516,21],{},", no la frase. ",[21,519,71],{}," es una firma que no coincidió, ",[21,522,205],{}," es un token cuyo contenido fue rechazado, por ejemplo uno caducado.",[240,525,526],{},"Averigua qué token falló antes de cambiar ninguna clave. Si las páginas sin sesión funcionan y las que tienen sesión no, es una sesión y no tu clave de API.",[240,528,529],{},"Revisa los cuatro sitios donde se esconde la clave antigua: los ajustes de la herramienta, el propio código, los secretos de Edge Function y un despliegue que nadie reconstruyó.",[240,531,532],{},"Haz la rotación de una sentada. Cada copia que dejes atrás es una aplicación que funciona a medias, y el sitio en producción sigue enviando la clave antigua hasta que se reconstruya.",[240,534,535],{},"Con las claves de firma nuevas, revocar es el paso que acaba de verdad con las firmas antiguas. Rotar por sí solo las deja aceptadas.",[10,537,538,539,542,543,547],{},"Empieza preguntándote si una página sin sesión todavía carga, porque esa sola\nrespuesta decide qué mitad de este artículo te afecta. Si resulta que había una\nclave secreta a la vista, ",[278,540,541],{"href":388},"rotarla","\nexplica en qué orden proceder, y\n",[278,544,546],{"href":545},"\u002Fblog\u002Fwhich-api-keys-are-safe-in-your-frontend","qué claves fueron alguna vez seguras de\npublicar"," cuenta por qué las dos\nacabaron pareciéndose tanto.",{"title":23,"searchDepth":549,"depth":549,"links":550},3,[551,553,554,555,556,557,558,559,560],{"id":42,"depth":552,"text":43},2,{"id":64,"depth":552,"text":65},{"id":209,"depth":552,"text":210},{"id":295,"depth":552,"text":296},{"id":330,"depth":552,"text":331},{"id":369,"depth":552,"text":370},{"id":408,"depth":552,"text":409},{"id":445,"depth":552,"text":446},{"id":505,"depth":552,"text":506},"Fundamentos de seguridad","\u002Fblog\u002Fsupabase-invalid-jwt\u002Fcover-1200x630.png","Una carta cerrada con lacre delante de un sello cuyo grabado ya no coincide con el lacre, y más cartas detrás de una barra ámbar.","Un error Supabase invalid JWT es PGRST301: un token firmado no pasó la verificación. En un proyecto nuevo se trata de la sesión, no de la clave de API.",false,"md",[568,571,574,577,580],{"q":569,"a":570},"¿Qué significa PGRST301?","PGRST301 es PostgREST respondiendo HTTP 401 porque no pudo verificar un token firmado que iba en tu petición. PostgREST es el servidor que convierte tus tablas de Supabase en la API HTTP con la que habla tu aplicación. Su propia referencia de errores describe el código como un JWT recibido que no se pudo decodificar o que no es válido, y desde la versión 13 el campo message lleva una frase más concreta sobre qué parte de la comprobación falló.",{"q":572,"a":573},"¿Por qué recibo 401 después de rotar mi clave de Supabase?","Porque en algún sitio se sigue enviando una copia de la clave antigua. Los lugares habituales son los ajustes de proyecto de tu herramienta, una clave escrita directamente en el código, un secreto de Edge Function y un despliegue que se construyó antes del cambio y que nadie ha vuelto a construir. Un token firmado con una clave que el servidor ya no acepta no pasa la verificación, y eso te llega como un 401 en cada petición en vez de como un aviso sobre la clave.",{"q":575,"a":576},"¿En qué se diferencia de No API key found in request?","Son dos mensajes distintos con causas distintas. No API key found in request significa que la petición llegó sin ninguna cabecera apikey y la rechazó la pasarela antes de que se preguntara nada a tu base de datos. Un invalid JWT significa que el token sí estaba y su firma no coincidió. Lo primero es una carta sin dirección, lo segundo una carta con el lacre equivocado.",{"q":578,"a":579},"¿Tengo que actualizar también mis Edge Functions?","Sí, y es la copia que más se olvida, porque una Edge Function guarda sus propios secretos, separados de los que usa tu frontend. Supabase también advierte de que una función con el ajuste Verify JWT activado puede romperse al rotar una clave de firma, y de que cualquier componente que verifique los tokens por su cuenta contra el secreto JWT antiguo se romperá por el mismo motivo.",{"q":581,"a":582},"¿Cómo veo qué clave envía realmente mi aplicación en producción?","Abre tu sitio publicado, no la vista previa, y mira qué envía la página de verdad. Una clave antigua de Supabase es una cadena larga que empieza por eyJ, y las nuevas empiezan por sb_publishable_ o sb_secret_. Nuestro escaneo gratuito lee una aplicación en producción como lo haría un desconocido y te dice qué claves se ven desde fuera, lo que también revela si todavía se está enviando una antigua.","\u002Fblog\u002Fsupabase-invalid-jwt\u002Fcard-800x500.png",[585,586,587,588,589,590,591],"supabase invalid jwt","pgrst301","bad_jwt supabase","supabase 401 tras rotar la clave","jwsinvalidsignature supabase","clave antigua de supabase dejó de funcionar","provided jwt could not be decoded",{},true,"\u002Fblog\u002Fsupabase-invalid-jwt","2026-10-16",{"title":5,"description":564},"blog\u002Fsupabase-invalid-jwt",[599,600,601],"Un error Supabase invalid JWT llega como PGRST301 con HTTP 401, y significa que la firma del token que iba en tu petición no se pudo verificar. No se buscó nada en una lista para luego rechazarlo.","Las claves publishable y secret son cadenas cortas sin firma, así que en un proyecto que ya se pasó a ellas este error habla de tu usuario con sesión iniciada y nunca de la clave de API.","PostgREST 13 cambió una frase vaga por una lista corta de frases concretas. Lee el mensaje que te llegó de verdad: cada uno descarta una causa distinta.","NtmQA5wcy5YWj9cpX15Ak9UPmxoR5PYhj1OZXppVaII",[604,610,616,622,628,634,639,645,651,657,663,670,676,682,688,694,700,706,712,718,724,730,735,741,747,748,754,760,766,772,778,784,790,796,801,807,813,819,825,831,837,843,849,855,861,867,872,878,884,890,896,902,908,914,920,926,932,938,944,950,956,962,968,974,979,985,991,997,1003,1009,1015,1021,1027,1033,1039,1044,1050,1056,1062,1068,1074,1080,1086,1092,1097,1102,1108,1114,1119,1125],{"path":605,"title":606,"description":607,"published":608,"category":561,"image":609,"draft":565},"\u002Fblog\u002Fmax-client-connections-reached","Supabase \"max client connections reached\", explicado","Supabase \"max client connections reached\": el pooler no tiene sitio para un cliente más. El puerto de tu cadena de conexión decide el límite.","2026-11-09","\u002Fblog\u002Fmax-client-connections-reached\u002Fcard-800x500.png",{"path":611,"title":612,"description":613,"published":614,"category":561,"image":615,"draft":565},"\u002Fblog\u002Funsupported-provider-provider-is-not-enabled","Error de Supabase: \"Unsupported provider: provider is not enabled\"","\"Unsupported provider: provider is not enabled\": Google está desactivado en tu proyecto Supabase. Cómo activarlo y arreglar redirect_uri_mismatch.","2026-11-08","\u002Fblog\u002Funsupported-provider-provider-is-not-enabled\u002Fcard-800x500.png",{"path":617,"title":618,"description":619,"published":620,"category":561,"image":621,"draft":565},"\u002Fblog\u002Fviolates-foreign-key-constraint","Error de Supabase \"violates foreign key constraint\" al insertar","Supabase \"insert or update on table violates foreign key constraint\": tu fila apunta a una fila que no existe. Cuál, por qué, y cómo arreglarlo.","2026-11-07","\u002Fblog\u002Fviolates-foreign-key-constraint\u002Fcard-800x500.png",{"path":623,"title":624,"description":625,"published":626,"category":561,"image":627,"draft":565},"\u002Fblog\u002Fnull-value-violates-not-null-constraint","Error de Supabase \"null value in column violates not-null constraint\"","Supabase \"null value in column violates not-null constraint\": una columna obligatoria llegó vacía. De dónde vino el null, y cómo arreglarlo.","2026-11-06","\u002Fblog\u002Fnull-value-violates-not-null-constraint\u002Fcard-800x500.png",{"path":629,"title":630,"description":631,"published":632,"category":561,"image":633,"draft":565},"\u002Fblog\u002Fauth-session-missing","Supabase \"Auth session missing!\": adónde fue la sesión","\"Auth session missing!\" es supabase-js sin encontrar ninguna sesión iniciada y sin llegar a preguntar a Supabase. Adónde fue la sesión, y el arreglo.","2026-11-05","\u002Fblog\u002Fauth-session-missing\u002Fcard-800x500.png",{"path":291,"title":635,"description":636,"published":637,"category":561,"image":638,"draft":565},"Supabase \"invalid claim: missing sub claim\": no hay nadie conectado","\"invalid claim: missing sub claim\": Supabase Auth recibió un token auténtico sin ningún usuario, la clave del proyecto en lugar de una sesión.","2026-11-04","\u002Fblog\u002Finvalid-claim-missing-sub-claim\u002Fcard-800x500.png",{"path":640,"title":641,"description":642,"published":643,"category":561,"image":644,"draft":565},"\u002Fblog\u002Ffailed-to-send-a-request-to-the-edge-function","\"Failed to send a request to the Edge Function\": la causa real","\"Failed to send a request to the Edge Function\": el navegador no recibió una respuesta legible. Su consola dice por qué y si tu función se ejecutó.","2026-11-03","\u002Fblog\u002Ffailed-to-send-a-request-to-the-edge-function\u002Fcard-800x500.png",{"path":646,"title":647,"description":648,"published":649,"category":561,"image":650,"draft":565},"\u002Fblog\u002Fsupabase-auth-redirects-to-localhost","Por qué los enlaces de email de Supabase redirigen a localhost:3000","Si Supabase redirige a localhost:3000, la Site URL nunca se cambió o tu dirección falta en Redirect URLs. La solución, y el comodín que debes evitar.","2026-11-02","\u002Fblog\u002Fsupabase-auth-redirects-to-localhost\u002Fcard-800x500.png",{"path":652,"title":653,"description":654,"published":655,"category":561,"image":656,"draft":565},"\u002Fblog\u002Femail-link-is-invalid-or-has-expired","Supabase \"Email link is invalid or has expired\" con un enlace nuevo","Con un enlace enviado hace un minuto, \"Email link is invalid or has expired\" en Supabase suele indicar que un filtro de correo lo usó antes. Cómo arreglarlo.","2026-11-01","\u002Fblog\u002Femail-link-is-invalid-or-has-expired\u002Fcard-800x500.png",{"path":658,"title":659,"description":660,"published":661,"category":561,"image":662,"draft":565},"\u002Fblog\u002Fcanceling-statement-due-to-statement-timeout","Supabase \"canceling statement due to statement timeout\", explicado","Supabase \"canceling statement due to statement timeout\": una consulta superó el límite de su rol, 3 segundos para un visitante. Revisa antes la política RLS.","2026-10-31","\u002Fblog\u002Fcanceling-statement-due-to-statement-timeout\u002Fcard-800x500.png",{"path":664,"title":665,"description":666,"published":667,"category":668,"image":669,"draft":565},"\u002Fblog\u002Fdatabase-error-deleting-user","\"Database error deleting user\" en Supabase, y qué se va con él","\"Database error deleting user\" en Supabase: tu base de datos rechazó el borrado. La causa está en tus logs y el arreglo decide qué más se borra.","2026-10-30","Backups","\u002Fblog\u002Fdatabase-error-deleting-user\u002Fcard-800x500.png",{"path":671,"title":672,"description":673,"published":674,"category":561,"image":675,"draft":565},"\u002Fblog\u002Fstripe-webhook-no-signatures-found","\"No signatures found matching the expected signature\" en Supabase","¿Tu webhook de Stripe en una Edge Function de Supabase dice \"No signatures found matching the expected signature\"? Tiene dos causas. Sigue verificando.","2026-10-29","\u002Fblog\u002Fstripe-webhook-no-signatures-found\u002Fcard-800x500.png",{"path":677,"title":678,"description":679,"published":680,"category":561,"image":681,"draft":565},"\u002Fblog\u002Fsupabase-invalid-login","\"Invalid login credentials\" en Supabase: las tres causas","Supabase dice \"Invalid login credentials\" ante una contraseña errónea, un email desconocido y una cuenta de Google. Cómo saber cuál es.","2026-10-28","\u002Fblog\u002Fsupabase-invalid-login\u002Fcard-800x500.png",{"path":683,"title":684,"description":685,"published":686,"category":561,"image":687,"draft":565},"\u002Fblog\u002Fedge-function-returned-a-non-2xx-status-code","\"Edge Function returned a non-2xx status code\": el error real","\"Edge Function returned a non-2xx status code\" significa que tu función de Supabase respondió con un error. Aquí ves dónde leer el real.","2026-10-27","\u002Fblog\u002Fedge-function-returned-a-non-2xx-status-code\u002Fcard-800x500.png",{"path":689,"title":690,"description":691,"published":692,"category":561,"image":693,"draft":565},"\u002Fblog\u002Fcertificate-not-trusted","Certificado no confiable: tres causas aparte de la caducidad","¿Certificado no confiable aunque la fecha está bien? Nombra otra dirección, se firmó a sí mismo o a su cadena le falta un eslabón. Cómo saber cuál.","2026-10-26","\u002Fblog\u002Fcertificate-not-trusted\u002Fcard-800x500.png",{"path":695,"title":696,"description":697,"published":698,"category":561,"image":699,"draft":565},"\u002Fblog\u002Finvalid-input-syntax-for-type-uuid","Supabase \"invalid input syntax for type uuid\": lee las comillas","Supabase \"invalid input syntax for type uuid\" significa que tu app envió algo que no es un id. Lee las comillas, frena la consulta, conserva la columna.","2026-10-25","\u002Fblog\u002Finvalid-input-syntax-for-type-uuid\u002Fcard-800x500.png",{"path":701,"title":702,"description":703,"published":704,"category":561,"image":705,"draft":565},"\u002Fblog\u002Fduplicate-key-value-violates-unique-constraint","Supabase \"duplicate key value violates unique constraint\", explicado","Supabase \"duplicate key value violates unique constraint\": el valor ya está ocupado. Encuentra la columna, elige un arreglo y aparta el valor de la pantalla.","2026-10-24","\u002Fblog\u002Fduplicate-key-value-violates-unique-constraint\u002Fcard-800x500.png",{"path":707,"title":708,"description":709,"published":710,"category":561,"image":711,"draft":565},"\u002Fblog\u002Fsupabase-relation-does-not-exist","Supabase \"relation does not exist\" aunque la tabla está ahí","Supabase \"relation does not exist\" o PGRST205: ninguna tabla tiene ese nombre en ese esquema. Cuatro comprobaciones, y lo que abre exponer un esquema.","2026-10-23","\u002Fblog\u002Fsupabase-relation-does-not-exist\u002Fcard-800x500.png",{"path":713,"title":714,"description":715,"published":716,"category":561,"image":717,"draft":565},"\u002Fblog\u002Fsupabase-function-not-found","Supabase PGRST202: \"Could not find the function in the schema cache\"","Supabase \"Could not find the function in the schema cache\" muestra los nombres de argumentos que envió tu app. Compáralos con la función.","2026-10-22","\u002Fblog\u002Fsupabase-function-not-found\u002Fcard-800x500.png",{"path":719,"title":720,"description":721,"published":722,"category":561,"image":723,"draft":565},"\u002Fblog\u002Fsupabase-schema-cache-column-error","Supabase PGRST204: \"Could not find the column in the schema cache\"","Supabase \"Could not find the column in the schema cache\" suele ser un nombre que no coincide. Cuatro comprobaciones y la línea que recarga.","2026-10-21","\u002Fblog\u002Fsupabase-schema-cache-column-error\u002Fcard-800x500.png",{"path":725,"title":726,"description":727,"published":728,"category":561,"image":729,"draft":565},"\u002Fblog\u002Fsupabase-email-rate-limit-exceeded","Supabase \"Email Rate Limit Exceeded\": dos límites, un solo código","Supabase \"Email Rate Limit Exceeded\" limita cada email que envía tu proyecto: 2 por hora con el remitente integrado. Qué cuenta y cómo subirlo.","2026-10-20","\u002Fblog\u002Fsupabase-email-rate-limit-exceeded\u002Fcard-800x500.png",{"path":280,"title":731,"description":732,"published":733,"category":561,"image":734,"draft":565},"Supabase \"Invalid Refresh Token: Refresh Token Not Found\": 3 causas","Supabase \"Invalid Refresh Token: Refresh Token Not Found\" cierra la sesión de tus usuarios. Tres causas, y cómo las separa el log de Auth.","2026-10-19","\u002Fblog\u002Fsupabase-users-logged-out\u002Fcard-800x500.png",{"path":736,"title":737,"description":738,"published":739,"category":561,"image":740,"draft":565},"\u002Fblog\u002Fpermission-denied-for-schema-public","Supabase \"permission denied for schema public\": tres causas","Supabase \"permission denied for schema public\" es un grant que falta en el esquema. Qué lo quitó y la línea que lo devuelve.","2026-10-18","\u002Fblog\u002Fpermission-denied-for-schema-public\u002Fcard-800x500.png",{"path":742,"title":743,"description":744,"published":745,"category":561,"image":746,"draft":565},"\u002Fblog\u002Fsupabase-bucket-not-found","Supabase \"Bucket not found\": las tres cosas que significa","Supabase \"Bucket not found\" es una de tres cosas: un enlace público a un bucket privado, un nombre que no coincide exactamente o el proyecto equivocado.","2026-10-17","\u002Fblog\u002Fsupabase-bucket-not-found\u002Fcard-800x500.png",{"path":594,"title":5,"description":564,"published":595,"category":561,"image":583,"draft":565},{"path":749,"title":750,"description":751,"published":752,"category":561,"image":753,"draft":565},"\u002Fblog\u002Fsupabase-single-row-error","Supabase \"multiple (or no) rows returned\" casi siempre es ninguna","Supabase devuelve PGRST116, \"JSON object requested, multiple (or no) rows returned\", cuando tu consulta no encontró nada. Una fila oculta parece ausente.","2026-10-15","\u002Fblog\u002Fsupabase-single-row-error\u002Fcard-800x500.png",{"path":755,"title":756,"description":757,"published":758,"category":561,"image":759,"draft":565},"\u002Fblog\u002Fdatabase-error-saving-new-user","\"Database error saving new user\" en Supabase: dónde mirar","\"Database error saving new user\" significa que algo unido a tu registro de Supabase falló y se deshizo entero. El error real está en tus logs.","2026-10-14","\u002Fblog\u002Fdatabase-error-saving-new-user\u002Fcard-800x500.png",{"path":761,"title":762,"description":763,"published":764,"category":561,"image":765,"draft":565},"\u002Fblog\u002Fwhat-is-supabase","¿Qué es Supabase? La base de datos que eligió tu builder","Qué es Supabase: la base de datos, el login y el almacenamiento de archivos de tu app. Supabase lleva los servidores; las reglas sobre tus filas son tuyas.","2026-10-13","\u002Fblog\u002Fwhat-is-supabase\u002Fcard-800x500.png",{"path":767,"title":768,"description":769,"published":770,"category":561,"image":771,"draft":565},"\u002Fblog\u002Faws-access-key-exposed-in-frontend","Una clave AWS expuesta en tu frontend no es como una clave OpenAI","Una clave de acceso de AWS expuesta en tu frontend lee y escribe tu almacenamiento. La solución suele ser una presigned URL, no una clave nueva.","2026-10-12","\u002Fblog\u002Faws-access-key-exposed-in-frontend\u002Fcard-800x500.png",{"path":773,"title":774,"description":775,"published":776,"category":561,"image":777,"draft":565},"\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":779,"title":780,"description":781,"published":782,"category":561,"image":783,"draft":565},"\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":785,"title":786,"description":787,"published":788,"category":561,"image":789,"draft":565},"\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":791,"title":792,"description":793,"published":794,"category":561,"image":795,"draft":565},"\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":305,"title":797,"description":798,"published":799,"category":561,"image":800,"draft":565},"\"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":802,"title":803,"description":804,"published":805,"category":561,"image":806,"draft":565},"\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":808,"title":809,"description":810,"published":811,"category":561,"image":812,"draft":565},"\u002Fblog\u002Fmove-a-secret-to-an-edge-function","Ocultar una clave API: muévela a una Edge Function de Supabase","Ocultar una clave API significa sacarla del navegador, y una Edge Function de Supabase es el sitio más pequeño donde ponerla. Dos pasos importan más.","2026-10-05","\u002Fblog\u002Fmove-a-secret-to-an-edge-function\u002Fcard-800x500.png",{"path":814,"title":815,"description":816,"published":817,"category":561,"image":818,"draft":565},"\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":820,"title":821,"description":822,"published":823,"category":561,"image":824,"draft":565},"\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":826,"title":827,"description":828,"published":829,"category":561,"image":830,"draft":565},"\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":832,"title":833,"description":834,"published":835,"category":561,"image":836,"draft":565},"\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":838,"title":839,"description":840,"published":841,"category":561,"image":842,"draft":565},"\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":844,"title":845,"description":846,"published":847,"category":561,"image":848,"draft":565},"\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":850,"title":851,"description":852,"published":853,"category":668,"image":854,"draft":565},"\u002Fblog\u002Ftest-your-supabase-backup","Prueba tu backup de Supabase antes del día que lo necesites","Cómo probar tu backup de Supabase: restaurarlo en un proyecto de prueba, contar filas, iniciar sesión y buscar la línea que le falta a un archivo cortado.","2026-09-28","\u002Fblog\u002Ftest-your-supabase-backup\u002Fcard-800x500.png",{"path":856,"title":857,"description":858,"published":859,"category":668,"image":860,"draft":565},"\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":862,"title":863,"description":864,"published":865,"category":668,"image":866,"draft":565},"\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":868,"title":869,"description":870,"published":865,"category":561,"image":871,"draft":565},"\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":873,"title":874,"description":875,"published":876,"category":561,"image":877,"draft":565},"\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":879,"title":880,"description":881,"published":882,"category":561,"image":883,"draft":565},"\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":885,"title":886,"description":887,"published":888,"category":668,"image":889,"draft":565},"\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":891,"title":892,"description":893,"published":894,"category":561,"image":895,"draft":565},"\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":897,"title":898,"description":899,"published":900,"category":561,"image":901,"draft":565},"\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":903,"title":904,"description":905,"published":906,"category":561,"image":907,"draft":565},"\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":909,"title":910,"description":911,"published":912,"category":561,"image":913,"draft":565},"\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":915,"title":916,"description":917,"published":918,"category":561,"image":919,"draft":565},"\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":921,"title":922,"description":923,"published":924,"category":561,"image":925,"draft":565},"\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":927,"title":928,"description":929,"published":930,"category":561,"image":931,"draft":565},"\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":933,"title":934,"description":935,"published":936,"category":561,"image":937,"draft":565},"\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":939,"title":940,"description":941,"published":942,"category":668,"image":943,"draft":565},"\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":945,"title":946,"description":947,"published":948,"category":668,"image":949,"draft":565},"\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":951,"title":952,"description":953,"published":954,"category":668,"image":955,"draft":565},"\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":957,"title":958,"description":959,"published":960,"category":561,"image":961,"draft":565},"\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":963,"title":964,"description":965,"published":966,"category":561,"image":967,"draft":565},"\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":969,"title":970,"description":971,"published":972,"category":561,"image":973,"draft":565},"\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":388,"title":975,"description":976,"published":977,"category":561,"image":978,"draft":565},"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":980,"title":981,"description":982,"published":983,"category":561,"image":984,"draft":565},"\u002Fblog\u002Fvibe-coding-security-scanners-compared","Escáneres de seguridad vibe coding comparados, el nuestro incluido","El mejor escáner de seguridad para vibe coding depende de tres preguntas que ninguna lista de funciones responde. Ocho herramientas, con precios.","2026-09-07","\u002Fblog\u002Fvibe-coding-security-scanners-compared\u002Fcard-800x500.png",{"path":986,"title":987,"description":988,"published":989,"category":561,"image":990,"draft":565},"\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":992,"title":993,"description":994,"published":995,"category":561,"image":996,"draft":565},"\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":998,"title":999,"description":1000,"published":1001,"category":561,"image":1002,"draft":565},"\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":1004,"title":1005,"description":1006,"published":1007,"category":561,"image":1008,"draft":565},"\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":1010,"title":1011,"description":1012,"published":1013,"category":561,"image":1014,"draft":565},"\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":1016,"title":1017,"description":1018,"published":1019,"category":668,"image":1020,"draft":565},"\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":1022,"title":1023,"description":1024,"published":1025,"category":561,"image":1026,"draft":565},"\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":1028,"title":1029,"description":1030,"published":1031,"category":668,"image":1032,"draft":565},"\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":1034,"title":1035,"description":1036,"published":1037,"category":561,"image":1038,"draft":565},"\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":352,"title":1040,"description":1041,"published":1042,"category":561,"image":1043,"draft":565},"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":1045,"title":1046,"description":1047,"published":1048,"category":561,"image":1049,"draft":565},"\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":1051,"title":1052,"description":1053,"published":1054,"category":561,"image":1055,"draft":565},"\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":1057,"title":1058,"description":1059,"published":1060,"category":668,"image":1061,"draft":565},"\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":1063,"title":1064,"description":1065,"published":1066,"category":668,"image":1067,"draft":565},"\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":1069,"title":1070,"description":1071,"published":1072,"category":561,"image":1073,"draft":565},"\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":1075,"title":1076,"description":1077,"published":1078,"category":561,"image":1079,"draft":565},"\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":1081,"title":1082,"description":1083,"published":1084,"category":668,"image":1085,"draft":565},"\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":1087,"title":1088,"description":1089,"published":1090,"category":561,"image":1091,"draft":565},"\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":1093,"title":1094,"description":1095,"published":1090,"category":561,"image":1096,"draft":565},"\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":393,"title":1098,"description":1099,"published":1100,"category":561,"image":1101,"draft":565},"Supabase publishable key: ¿es la misma que la anon key?","Supabase sustituyó anon y service_role por claves publicables y secretas. ¿Cuál pertenece a tu app y cuál no debe estar nunca?","2026-08-12","\u002Fblog\u002Fsupabase-new-api-keys\u002Fcard-800x500.png",{"path":1103,"title":1104,"description":1105,"published":1106,"category":668,"image":1107,"draft":565},"\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":1109,"title":1110,"description":1111,"published":1112,"category":561,"image":1113,"draft":565},"\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":1115,"title":1116,"description":1117,"published":1112,"category":668,"image":1118,"draft":565},"\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":1120,"title":1121,"description":1122,"published":1123,"category":668,"image":1124,"draft":565},"\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":545,"title":1126,"description":1127,"published":1123,"category":561,"image":1128,"draft":565},"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",1794213645220]