[{"data":1,"prerenderedAt":1255},["ShallowReactive",2],{"blog-es-edge-function-returned-a-non-2xx-status-code":3,"blog-index-es":729},{"id":4,"title":5,"body":6,"category":687,"cover":688,"coverAlt":689,"description":690,"draft":691,"extension":692,"faq":693,"image":708,"keywords":709,"meta":717,"navigation":718,"ogTitle":719,"path":720,"published":721,"seo":722,"stem":723,"tldr":724,"updated":721,"__hash__":728},"blog_es\u002Fblog\u002Fedge-function-returned-a-non-2xx-status-code.md","\"Edge Function returned a non-2xx status code\": el error real",{"type":7,"value":8,"toc":674},"minimark",[9,13,24,27,35,40,43,62,65,82,88,92,95,120,133,139,171,175,178,301,316,320,323,339,345,364,381,385,393,410,439,442,463,468,472,475,510,516,520,523,543,558,566,573,577,583,592,606,616,633,637,662],[10,11,12],"p",{},"Pulsas en tu app el botón que llama a una Edge Function y, en lugar de un\nresultado, tu app muestra esto:",[14,15,20],"pre",{"className":16,"code":18,"language":19},[17],"language-text","Edge Function returned a non-2xx status code\n","text",[21,22,18],"code",{"__ignoreMap":23},"",[10,25,26],{},"Es la misma frase pase lo que pase. Una clave que falta, una función que se\ncayó, una petición rechazada antes de que tu código corriera: todas dicen\n\"Edge Function returned a non-2xx status code\", así que el mensaje por sí solo\ncasi no te dice qué arreglar.",[10,28,29,30,34],{},"Lo que no te dice es que la razón ya llegó. ",[31,32,33],"strong",{},"El error volvió en la misma\nrespuesta que esa frase, y supabase-js no te lo lee."," Buscar la frase te da las\nsuposiciones de otros. Leer la respuesta te da la tuya.",[36,37,39],"h2",{"id":38},"qué-significa-edge-function-returned-a-non-2xx-status-code","¿Qué significa \"Edge Function returned a non-2xx status code\"?",[10,41,42],{},"Tu función respondió, y la respuesta fue un error. Eso es todo lo que dice la\nfrase.",[10,44,45,46,49,50,57,58,61],{},"Cada respuesta en la web lleva un código de estado, un número de tres cifras que\ndice cómo fue la petición. Cualquier cosa en los 200 significa que funcionó.\nCuando tu app llama a una función con ",[21,47,48],{},"supabase.functions.invoke",", supabase-js\nmira ese número y, si está fuera de los 200, le entrega a tu app una frase fija\nen lugar de los datos. La frase está escrita palabra por palabra en\n",[51,52,56],"a",{"href":53,"rel":54},"https:\u002F\u002Fgithub.com\u002Fsupabase\u002Fsupabase-js\u002Fblob\u002Fmaster\u002Fpackages\u002Fcore\u002Ffunctions-js\u002Fsrc\u002Ftypes.ts",[55],"nofollow","el propio supabase-js",",\ny el error que la lleva se llama ",[21,59,60],{},"FunctionsHttpError",".",[10,63,64],{},"Piensa en la llamada como una carta que envías a una oficina interna. La oficina\ncontesta lo que pediste o te devuelve la carta. Cuando vuelve, supabase-js lee\nel sello del sobre y te dice que fue devuelta. No abre el sobre. La nota de\ndentro dice por qué, y la oficina llevó un registro de todo lo que pasó mientras\ntenía tu carta.",[10,66,67,68,71,72,76,77,81],{},"Dos parientes cercanos se parecen y significan otra cosa. ",[21,69,70],{},"Failed to send a request to the Edge Function"," significa que no volvió ninguna respuesta legible,\ny en un navegador eso suele ser una cabecera CORS que falta, algo que cubre\n",[51,73,75],{"href":74},"\u002Fblog\u002Ffailed-to-send-a-request-to-the-edge-function","el artículo sobre ese mensaje",".\nY si varias partes sin relación de tu app se rompieron a la vez, comprueba\n",[51,78,80],{"href":79},"\u002Fblog\u002Fis-supabase-down-or-your-app","si es Supabase o tu app"," antes de abrir\nningún código.",[83,84],"diagram",{"alt":85,"caption":86,"src":87},"Una petición sale del navegador hacia una Edge Function. La respuesta que vuelve lleva un estado 500 y un contenido lleno de líneas. supabase-js la reduce a una sola barra gris para la app, mientras una línea discontinua lleva de la barra de vuelta a la respuesta abierta y después al log de la función.","La respuesta lleva el estado y la razón. supabase-js le pasa a tu app una frase, y la razón sigue pegada al error y en el log de la función.","\u002Fblog\u002Fedge-function-returned-a-non-2xx-status-code\u002Fthe-reason-rides-back-1600x740.png",[36,89,91],{"id":90},"dónde-está-el-error-real","¿Dónde está el error real?",[10,93,94],{},"En dos sitios, y puedes leer los dos sin cambiar una línea de código.",[10,96,97,100,101,104,105,108,109,112,113,116,117,61],{},[31,98,99],{},"La respuesta misma."," Abre tu app publicada en Chrome, Edge o Firefox, pulsa\nF12, elige la pestaña ",[31,102,103],{},"Network"," y repite lo que falló. Haz clic en la petición\ncuya dirección contiene ",[21,106,107],{},"\u002Ffunctions\u002Fv1\u002F"," seguido del nombre de tu función. Su\ncolumna ",[31,110,111],{},"Status"," tiene el número, y su pestaña ",[31,114,115],{},"Response"," tiene lo que la\nfunción envió con él: un mensaje que escribió tu propio código, un contenido\nJSON corto de la plataforma de Supabase, o las palabras ",[21,118,119],{},"Internal Server Error",[10,121,122,123,128,129,132],{},"Si puedes cambiar la llamada, o pedírselo a la herramienta que escribe tu código, la\n",[51,124,127],{"href":125,"rel":126},"https:\u002F\u002Fsupabase.com\u002Fdocs\u002Fguides\u002Ffunctions\u002Ferror-handling",[55],"guía de manejo de errores de Supabase","\nmuestra cómo leer esa misma respuesta dentro de la app, porque la respuesta\noriginal viaja con el error como ",[21,130,131],{},"error.context",":",[14,134,137],{"className":135,"code":136,"language":19},[17],"const { data, error } = await supabase.functions.invoke('your-function', { body })\nif (error) console.log(error.context.status, await error.context.text())\n",[21,138,136],{"__ignoreMap":23},[10,140,141,144,145,148,149,152,153,156,157,162,163,166,167,170],{},[31,142,143],{},"El registro de la función."," En el panel de Supabase, abre ",[31,146,147],{},"Edge Functions",",\nelige la función y lee dos pestañas. ",[31,150,151],{},"Invocations"," lista cada petición con su\ncódigo de estado y lo que tardó. ",[31,154,155],{},"Logs"," guarda lo que la\n",[51,158,161],{"href":159,"rel":160},"https:\u002F\u002Fsupabase.com\u002Fdocs\u002Fguides\u002Ffunctions\u002Flogging",[55],"guía de logs de Supabase","\nllama eventos de plataforma, excepciones no capturadas y mensajes de log\npropios, es decir, todo lo que tu código imprimió con ",[21,164,165],{},"console.log"," o\n",[21,168,169],{},"console.error",". Reproduce el fallo una vez, anota la hora y lee las dos\npestañas en ese minuto.",[36,172,174],{"id":173},"lee-primero-el-código-de-estado","Lee primero el código de estado",[10,176,177],{},"El número te dice hasta dónde llegó tu petición antes de que la devolvieran, y\neso decide dónde está escrita la razón.",[179,180,181,197],"table",{},[182,183,184],"thead",{},[185,186,187,191,194],"tr",{},[188,189,190],"th",{},"Estado",[188,192,193],{},"Qué pasó",[188,195,196],{},"Dónde está la razón",[198,199,200,222,238,251,264,277,290],"tbody",{},[185,201,202,208,211],{},[203,204,205],"td",{},[21,206,207],{},"401",[203,209,210],{},"Rechazada antes de que tu código corriera, o tu código la rechazó",[203,212,213,214,217,218,221],{},"La respuesta. ",[21,215,216],{},"Invalid JWT"," o ",[21,219,220],{},"Missing authorization header"," es la plataforma",[185,223,224,229,232],{},[203,225,226],{},[21,227,228],{},"404",[203,230,231],{},"No hay ninguna función con ese nombre desplegada en este proyecto",[203,233,234,235],{},"La respuesta dice ",[21,236,237],{},"Requested function was not found",[185,239,240,245,248],{},[203,241,242],{},[21,243,244],{},"500",[203,246,247],{},"Tu código corrió y lanzó un error, o devolvió un 500 a propósito",[203,249,250],{},"La pestaña Logs",[185,252,253,258,261],{},[203,254,255],{},[21,256,257],{},"503",[203,259,260],{},"La función ni siquiera arrancó",[203,262,263],{},"La pestaña Logs: un import, un error de sintaxis o la configuración de la función",[185,265,266,271,274],{},[203,267,268],{},[21,269,270],{},"504",[203,272,273],{},"Ninguna respuesta en 150 segundos",[203,275,276],{},"La pestaña Invocations, y luego lo que tu código estaba esperando",[185,278,279,284,287],{},[203,280,281],{},[21,282,283],{},"546",[203,285,286],{},"Se quedó sin tiempo de CPU o sin memoria",[203,288,289],{},"La pestaña Logs dice cuál",[185,291,292,295,298],{},[203,293,294],{},"Cualquier otro 4xx",[203,296,297],{},"Lo eligió tu propio código, por ejemplo al pasar la respuesta de un servicio",[203,299,300],{},"La respuesta",[10,302,303,304,309,310,315],{},"Los significados vienen de la\n",[51,305,308],{"href":306,"rel":307},"https:\u002F\u002Fsupabase.com\u002Fdocs\u002Fguides\u002Ffunctions\u002Fstatus-codes",[55],"referencia de códigos de estado de Supabase","\ny de su ",[51,311,314],{"href":312,"rel":313},"https:\u002F\u002Fsupabase.com\u002Fdocs\u002Fguides\u002Ffunctions\u002Flimits",[55],"página de límites",".\nUn 404 no necesita más que esta tabla: compara el nombre de la función en tu app\ncon la lista del panel, y comprueba que esté desplegada en el proyecto con el\nque habla tu app. Las demás filas tienen cada una una sección más abajo.",[36,317,319],{"id":318},"_500-tu-función-corrió-y-falló","500: tu función corrió y falló",[10,321,322],{},"Algo en tu código lanzó un error, y la pestaña Logs tiene la línea.",[10,324,325,326,331,332,334,335,338],{},"La ",[51,327,330],{"href":328,"rel":329},"https:\u002F\u002Fsupabase.com\u002Fdocs\u002Fguides\u002Ftroubleshooting\u002Fedge-function-500-error-response",[55],"página de Supabase sobre este error","\nlo divide en dos. Si la respuesta dice exactamente ",[21,333,119],{},", tu\ncódigo se cayó y el log muestra el tipo de error y la línea donde ocurrió, por\nejemplo ",[21,336,337],{},"TypeError: Cannot read properties of undefined",". Si la respuesta dice\notra cosa, tu código escribió ese mensaje y devolvió el 500 a propósito, así que\nbusca en la función el estado que envió.",[10,340,341,342],{},"Hay una causa que conviene descartar antes que ninguna otra en una función que\nllama a OpenAI, Stripe o cualquier API de pago: ",[31,343,344],{},"un secreto que existe en tu\nmáquina y no en tu proyecto publicado.",[10,346,347,348,351,352,360,361,363],{},"Dentro de la función, un secreto se lee con ",[21,349,350],{},"Deno.env.get('OPENAI_API_KEY')",".\nCuando no hay ningún secreto con ese nombre, la documentación de Deno dice que\nesto ",[51,353,356,357],{"href":354,"rel":355},"https:\u002F\u002Fdocs.deno.com\u002Fapi\u002Fdeno\u002F~\u002FDeno.Env.get",[55],"devuelve ",[21,358,359],{},"undefined"," en\nlugar de fallar. Así que la función sigue, arma una petición con la palabra\n",[21,362,359],{}," donde debería ir la clave y la envía. OpenAI responde con su propio\n401, \"Incorrect API key provided\", y tu función lo pasa tal cual o se cae al\nleer una respuesta que no esperaba. En la imagen del sobre: la oficina abrió tu\ncarta, fue al cajón donde debía estar la clave, lo encontró vacío y devolvió una\nnota sobre una clave equivocada.",[10,365,366,367,370,371,376,377,380],{},"El arreglo está en la página ",[31,368,369],{},"Edge Function Secrets"," de tu proyecto de\nSupabase. La\n",[51,372,375],{"href":373,"rel":374},"https:\u002F\u002Fsupabase.com\u002Fdocs\u002Fguides\u002Ffunctions\u002Fsecrets",[55],"guía de secretos de Supabase","\ndice que una función lee un secreto nuevo al instante, así que no vuelves a\ndesplegar, y que ningún nombre de secreto puede empezar por ",[21,378,379],{},"SUPABASE_",", porque\nese prefijo está reservado para los valores que Supabase define por ti. Un\nsecreto que defines para el desarrollo local se queda en tu máquina; el proyecto\npublicado tiene su propia lista.",[36,382,384],{"id":383},"_401-rechazada-antes-de-que-tu-código-corriera","401: rechazada antes de que tu código corriera",[10,386,387,388,217,390,392],{},"Si la respuesta dice ",[21,389,216],{},[21,391,220],{},", tu función\nnunca vio la petición. La plataforma de Supabase la rechazó en la puerta.",[10,394,395,396,399,400,403,404,409],{},"Esa puerta es una verificación llamada ",[21,397,398],{},"verify_jwt",", activa por defecto, que lee\nla cabecera ",[21,401,402],{},"Authorization"," de cada petición antes de que corra tu código. La\n",[51,405,408],{"href":406,"rel":407},"https:\u002F\u002Fsupabase.com\u002Fdocs\u002Fguides\u002Ftroubleshooting\u002Fedge-function-401-error-response",[55],"página de Supabase sobre el 401","\nda tres maneras de no pasarla:",[411,412,413,420,433],"ul",{},[414,415,416,419],"li",{},[31,417,418],{},"La clave no coincide con las de tu proyecto."," Por ejemplo una clave copiada\nde otro proyecto, o una vieja que sigue puesta en algún sitio después de una\nrotación.",[414,421,422,428,429,432],{},[31,423,424,425,427],{},"No hay cabecera ",[21,426,402],{}," en absoluto."," supabase-js añade una por ti.\nUn ",[21,430,431],{},"fetch"," simple a la dirección de la función, o un webhook de otro servicio,\nno lo hace.",[414,434,435,438],{},[31,436,437],{},"Tu proyecto usa las claves de firma nuevas de Supabase."," La verificación\nintegrada solo entiende el formato antiguo, y la solución de Supabase es apagar\nla verificación para esa función.",[10,440,441],{},"Ese último punto va más allá de un caso. Supabase recomienda ahora apagar la\nverificación integrada y gestionar la autenticación directamente en el código de\ntu función. La recomendación tiene dos mitades, y la segunda es la que se queda\npor el camino.",[10,443,444,445,448,449,453,454,457,458,462],{},"Si el 401 llegó con un mensaje que escribió tu propio código, la plataforma dejó\npasar la petición y tu función la rechazó, o pasó un 401 del servicio al que\nllamaba, que a menudo es el secreto vacío de la sección del 500. Además es una\nverificación distinta de la que hay detrás de ",[21,446,447],{},"PGRST301"," en tus consultas a la\nbase de datos, que ",[51,450,452],{"href":451},"\u002Fblog\u002Fsupabase-invalid-jwt","tiene su propia página",". Si el\nregistro de la función muestra ",[21,455,456],{},"invalid claim: missing sub claim",", preguntó a\nSupabase Auth quién llamaba y recibió la clave de tu proyecto en lugar de un\nusuario con sesión iniciada, algo que\n",[51,459,461],{"href":460},"\u002Fblog\u002Finvalid-claim-missing-sub-claim","el artículo de ese mensaje"," sigue paso\na paso.",[83,464],{"alt":465,"caption":466,"src":467},"Una petición viaja del navegador a través de una puerta, entra en la función y sigue hacia un servicio externo. Cada sitio donde puede detenerse lleva su número: 401 en la puerta, 404 donde no hay ninguna función en la dirección, 500 dentro de la función, 546 y 504 en un reloj a su lado, y un 401 del servicio externo que la función devuelve.","El estado dice hasta dónde llegó la petición. Un 401 en la puerta nunca alcanzó tu código; un 401 devuelto por el servicio al que llamó, sí.","\u002Fblog\u002Fedge-function-returned-a-non-2xx-status-code\u002Fhow-far-it-got-1600x720.png",[36,469,471],{"id":470},"_546-y-504-sin-recursos-o-sin-tiempo","546 y 504: sin recursos o sin tiempo",[10,473,474],{},"Supabase detiene una función que consume demasiado, y los dos números dicen qué\nlímite tocó.",[10,476,477,480,481,484,485,488,489,492,493,496,497,217,500,503,504,509],{},[31,478,479],{},"546 es cómputo."," La\n",[51,482,314],{"href":312,"rel":483},[55]," da a una\nfunción 256 MB de memoria y a cada petición 2 segundos de tiempo de CPU. Pasa de\ncualquiera de los dos y la respuesta dice\n",[21,486,487],{},"Function failed due to not having enough compute resources (please check logs)",",\ncon el código ",[21,490,491],{},"WORKER_RESOURCE_LIMIT"," (los logs antiguos dicen ",[21,494,495],{},"WORKER_LIMIT",").\nLa pestaña Logs dice ",[21,498,499],{},"CPU Time exceeded",[21,501,502],{},"Memory limit exceeded",". La\n",[51,505,508],{"href":506,"rel":507},"https:\u002F\u002Fsupabase.com\u002Fdocs\u002Fguides\u002Ftroubleshooting\u002Fedge-function-546-error-response",[55],"página de Supabase sobre el 546","\npone como ejemplos el procesamiento de imágenes, los embeddings de IA y el web\nscraping, y su respuesta es hacer menos en cada petición o pasar la parte pesada\na otro servicio.",[10,511,512,515],{},[31,513,514],{},"504 es tiempo."," Una función que no envía ninguna respuesta en 150 segundos\nrecibe un 504. El límite de CPU solo cuenta el tiempo en que tu código está\ncalculando de verdad: la página de límites dice que no incluye la entrada y\nsalida asíncrona, y esperar a la red es justo eso. Así que una función que\nespera a un modelo de IA lento no gasta CPU. Va consumiendo el reloj de 150\nsegundos, y cuando se acaba recibes un 504 y no un 546.",[36,517,519],{"id":518},"dos-arreglos-que-lo-empeoran","Dos arreglos que lo empeoran",[10,521,522],{},"Dos cambios hacen que este mensaje desaparezca en minutos, y los dos te dejan\npeor que el error.",[10,524,525,528,529,532,533,537,538,542],{},[31,526,527],{},"Llamar a la API desde la página."," La forma más rápida de que una función que\nfalla deje de fallar es dejar de usarla: poner la clave en una variable ",[21,530,531],{},"VITE_","\ny llamar a OpenAI directamente desde tu frontend. Funciona al primer intento.\nTambién pone la clave en el archivo que descarga cada visitante, porque ese\nprefijo es\n",[51,534,536],{"href":535},"\u002Fblog\u002Fvite-and-next-public-env-vars","una instrucción para compilar el valor dentro de tu bundle",",\ny ahí cualquiera puede leerla. Entre las apps publicadas que hemos escaneado,\napareció una clave de OpenAI en el JavaScript de 33, y\n",[51,539,541],{"href":540},"\u002Fblog\u002Fopenai-api-key-exposed-in-frontend","lo que un desconocido puede hacer con una","\ntiene su propio artículo.",[10,544,545,548,549,551,552,557],{},[31,546,547],{},"Apagar la verificación y quedarse ahí."," Quitarse un 401 apagando\n",[21,550,398],{}," es la primera mitad del consejo de la propia Supabase. La\n",[51,553,556],{"href":554,"rel":555},"https:\u002F\u002Fsupabase.com\u002Fdocs\u002Fguides\u002Ffunctions\u002Ffunction-configuration",[55],"guía de configuración de Supabase","\ndeja claro lo que eso hace por sí solo: cualquiera puede entonces invocar tu\nEdge Function sin un JWT válido. La dirección de tu función está en tu frontend\npara que cualquiera la lea. Si la función guarda una clave de OpenAI, un\ndesconocido puede gastarla ahora a través de ti. Si usa la clave secreta del\nproyecto, que Supabase pone en el entorno de cada función, la petición de un\ndesconocido corre con una clave que se salta todas las reglas de row level\nsecurity que escribiste.",[10,559,560,561,565],{},"La segunda mitad, un control dentro de la función sobre quién llama, son unas\npocas líneas.\n",[51,562,564],{"href":563},"\u002Fblog\u002Fmove-a-secret-to-an-edge-function","Mover un secreto a una Edge Function","\nlas tiene, y explica por qué incluso la verificación por defecto deja pasar a\nmás gente de lo que su nombre sugiere.",[10,567,568,569,61],{},"El primer error se ve desde fuera, y así lo contamos. Nuestro escaneo gratuito\nlee el JavaScript de tu app publicada como lo haría un desconocido y nombra cada\nclave que encuentra, indicando si es de las que deben estar ahí. Tarda unos 20\nsegundos y no necesita cuenta: ",[51,570,572],{"href":571},"\u002Fsecurity-scanner","escanea tu app",[36,574,576],{"id":575},"que-la-clave-se-quede-donde-la-pusiste","Que la clave se quede donde la pusiste",[10,578,579,582],{},[31,580,581],{},"El arreglo de hoy lo puede deshacer el prompt de la semana que viene."," Un\nbuilder al que pides que vuelva a hacer funcionar algo es libre de tomar el\natajo de arriba, y tu app sigue funcionando mientras lo hace.",[10,584,585],{},[31,586,587,591],{},[51,588,590],{"href":589},"\u002Fwebsite-monitoring","Reeve Monitor"," lo revisa cada hora por ti:",[411,593,594,597,600,603],{},[414,595,596],{},"las nueve comprobaciones cada hora, en hasta tres apps",[414,598,599],{},"un correo el día en que un nuevo escaneo califica tu app peor que el anterior; una clave secreta en tu JavaScript limita por sí sola la nota a D",[414,601,602],{},"si la app está disponible, cada 60 segundos",[414,604,605],{},"un informe mensual de lo que vio",[10,607,608],{},[31,609,610,611,615],{},"Una función que corre con tu clave secreta se salta todas las reglas de row\nlevel security. ",[51,612,614],{"href":613},"\u002Fsupabase-backups","Reeve Care"," guarda una copia de tu base de\ndatos de Supabase donde nada de tu proyecto puede alcanzarla.",[411,617,618,621,624,627,630],{},[414,619,620],{},"una copia cifrada según un calendario, guardada en el almacenamiento propio de Reeve en la UE, fuera de tu cuenta de Supabase",[414,622,623],{},"cada copia verificada antes de contar, contando las filas de cada tabla",[414,625,626],{},"una restauración en un clic que guarda lo que hay ahora antes de reemplazarlo",[414,628,629],{},"también tus archivos subidos, en cuanto conectes una credencial de Storage",[414,631,632],{},"todo lo que hace Monitor",[36,634,636],{"id":635},"qué-hacer-ahora","Qué hacer ahora",[638,639,640],"key-takeaways",{},[411,641,642,645,648,651,654,657],{},[414,643,644],{},"Reproduce el fallo una vez y anota la hora.",[414,646,647],{},"Abre la petición en la pestaña Network de tu navegador y lee su estado y su respuesta antes de cambiar nada.",[414,649,650],{},"Lee las pestañas Invocations y Logs de la función en el panel de Supabase en ese minuto.",[414,652,653],{},"Ante un 500 de una función que llama a una API de pago, comprueba que el secreto esté definido en la página Edge Function Secrets del proyecto publicado. Después no hace falta volver a desplegar.",[414,655,656],{},"Ante un 401 de la plataforma, corrige la clave o apaga la verificación integrada, y añade en el mismo cambio un control de quién llama dentro de la función.",[414,658,659,660,61],{},"Nunca quites este error moviendo la clave a una variable ",[21,661,531],{},[10,663,664,665,668,669,673],{},"El control de quién llama es el paso más fácil de saltarse, y\n",[51,666,667],{"href":563},"la guía de secretos en Edge Functions","\nlo cubre entero. La ",[51,670,672],{"href":671},"\u002Fchecklist","checklist de seguridad de 10 minutos"," cubre lo\nque suele quedar abierto en una app recién lanzada.",{"title":23,"searchDepth":675,"depth":675,"links":676},3,[677,679,680,681,682,683,684,685,686],{"id":38,"depth":678,"text":39},2,{"id":90,"depth":678,"text":91},{"id":173,"depth":678,"text":174},{"id":318,"depth":678,"text":319},{"id":383,"depth":678,"text":384},{"id":470,"depth":678,"text":471},{"id":518,"depth":678,"text":519},{"id":575,"depth":678,"text":576},{"id":635,"depth":678,"text":636},"Fundamentos de seguridad","\u002Fblog\u002Fedge-function-returned-a-non-2xx-status-code\u002Fcover-1200x630.png","Un sobre devuelto con un sello amarillo cruzado sobre el frente y la solapa abierta, con la carta doblada todavía dentro.","\"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.",false,"md",[694,696,699,702,705],{"q":39,"a":695},"Significa que tu Edge Function de Supabase respondió con un estado de error, cualquier número fuera de los 200, y supabase-js convirtió esa respuesta en una frase fija. La frase es la misma para un fallo del código, una clave que falta, una petición rechazada antes de que tu código corriera y un tiempo de espera agotado. La respuesta en sí, que dice cuál de esos fue, sigue pegada al error, y la función guarda un log propio.",{"q":697,"a":698},"¿Cómo veo el error real que hay detrás?","Abre tu app publicada, pulsa F12, elige la pestaña Network y repite lo que falló. Haz clic en la petición cuya dirección contiene \u002Ffunctions\u002Fv1\u002F y lee su estado y su respuesta: eso es lo que la función envió de verdad. Después abre Edge Functions en tu panel de Supabase, elige la función y lee las pestañas Invocations y Logs a la misma hora. En código, la guía de Supabase lee esa misma respuesta con await error.context.json().",{"q":700,"a":701},"¿Por qué mi Edge Function funciona en local y falla al desplegarla?","Comprueba primero si un secreto existe en tu máquina y no en tu proyecto publicado. El entorno local lee su propio archivo de entorno, y la función desplegada lee los secretos definidos en el proyecto. Un secreto que falta vuelve como undefined en lugar de como un error, así que la función sigue y falla más adelante, normalmente cuando el servicio al que llama rechaza una clave vacía. Defínelo en la página Edge Function Secrets; Supabase indica que un secreto nuevo se lee al instante, sin volver a desplegar.",{"q":703,"a":704},"¿Debo apagar la verificación JWT para arreglar un 401?","Supabase recomienda ahora apagar la verificación integrada y comprobar quién llama dentro de tu función. Las dos mitades cuentan. Apagada sola, la verificación ya no detiene nada, y la propia documentación de Supabase advierte que así cualquiera puede invocar tu función sin un JWT válido. Si la función guarda una clave de OpenAI o la clave secreta del proyecto, eso es cualquiera en internet gastando tu cuota o saltándose tus reglas de row level security.",{"q":706,"a":707},"¿Qué significa el error 546 en una Edge Function de Supabase?","La función se quedó sin cómputo: más de 2 segundos de tiempo de CPU, o más que su memoria, en una petición. La respuesta dice \"Function failed due to not having enough compute resources (please check logs)\", y la pestaña Logs dice qué límite fue, CPU Time exceeded o Memory limit exceeded. Esperar a una API lenta no cuenta como tiempo de CPU; una función que espera demasiado recibe un 504 en su lugar.","\u002Fblog\u002Fedge-function-returned-a-non-2xx-status-code\u002Fcard-800x500.png",[710,711,60,712,713,714,715,716],"edge function returned a non-2xx status code","error edge function supabase","error supabase functions invoke","supabase edge function 500","supabase edge function 401","logs edge function supabase","error edge function lovable",{},true,"\"Edge Function returned a non-2xx status code\" en Supabase","\u002Fblog\u002Fedge-function-returned-a-non-2xx-status-code","2026-10-27",{"title":5,"description":690},"blog\u002Fedge-function-returned-a-non-2xx-status-code",[725,726,727],"\"Edge Function returned a non-2xx status code\" es lo que supabase-js dice de cualquier respuesta de tu función fuera de los 200. La razón real llegó en la misma respuesta, y los logs de la función guardan el resto.","Lee primero el número de estado. 401 significa que la petición se rechazó antes de que tu código corriera, 404 que no hay ninguna función desplegada con ese nombre, 500 que tu código falló, 546 o 504 que se quedó sin recursos o sin tiempo.","Dos arreglos rápidos hacen callar el mensaje y abren un agujero: llamar a la API desde tu frontend con la clave dentro, y apagar la verificación JWT sin poner un control propio en su lugar.","A4cvWj4EJ9Jb11ACHvHTT4YXpjjdTlKsIyi3Bop4Paw",[730,736,742,748,754,760,765,770,776,782,788,795,801,807,808,814,820,826,832,838,844,850,856,862,868,873,879,885,891,897,903,909,915,921,927,933,938,944,950,956,961,967,973,979,985,991,996,1002,1008,1014,1020,1026,1032,1038,1043,1049,1055,1061,1067,1073,1079,1085,1091,1097,1103,1109,1115,1121,1127,1132,1138,1144,1150,1156,1162,1168,1174,1180,1186,1192,1198,1204,1210,1216,1221,1227,1233,1239,1244,1250],{"path":731,"title":732,"description":733,"published":734,"category":687,"image":735,"draft":691},"\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":737,"title":738,"description":739,"published":740,"category":687,"image":741,"draft":691},"\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":743,"title":744,"description":745,"published":746,"category":687,"image":747,"draft":691},"\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":749,"title":750,"description":751,"published":752,"category":687,"image":753,"draft":691},"\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":755,"title":756,"description":757,"published":758,"category":687,"image":759,"draft":691},"\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":460,"title":761,"description":762,"published":763,"category":687,"image":764,"draft":691},"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":74,"title":766,"description":767,"published":768,"category":687,"image":769,"draft":691},"\"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":771,"title":772,"description":773,"published":774,"category":687,"image":775,"draft":691},"\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":777,"title":778,"description":779,"published":780,"category":687,"image":781,"draft":691},"\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":783,"title":784,"description":785,"published":786,"category":687,"image":787,"draft":691},"\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":789,"title":790,"description":791,"published":792,"category":793,"image":794,"draft":691},"\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":796,"title":797,"description":798,"published":799,"category":687,"image":800,"draft":691},"\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":802,"title":803,"description":804,"published":805,"category":687,"image":806,"draft":691},"\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":720,"title":5,"description":690,"published":721,"category":687,"image":708,"draft":691},{"path":809,"title":810,"description":811,"published":812,"category":687,"image":813,"draft":691},"\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":815,"title":816,"description":817,"published":818,"category":687,"image":819,"draft":691},"\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":821,"title":822,"description":823,"published":824,"category":687,"image":825,"draft":691},"\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":827,"title":828,"description":829,"published":830,"category":687,"image":831,"draft":691},"\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":833,"title":834,"description":835,"published":836,"category":687,"image":837,"draft":691},"\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":839,"title":840,"description":841,"published":842,"category":687,"image":843,"draft":691},"\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":845,"title":846,"description":847,"published":848,"category":687,"image":849,"draft":691},"\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":851,"title":852,"description":853,"published":854,"category":687,"image":855,"draft":691},"\u002Fblog\u002Fsupabase-users-logged-out","Supabase \"Invalid Refresh Token: Refresh Token Not Found\": 3 causas","Supabase \"Invalid Refresh Token: Refresh Token Not Found\" cierra la sesión de tus usuarios. Tres causas, y cómo las separa el log de Auth.","2026-10-19","\u002Fblog\u002Fsupabase-users-logged-out\u002Fcard-800x500.png",{"path":857,"title":858,"description":859,"published":860,"category":687,"image":861,"draft":691},"\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":863,"title":864,"description":865,"published":866,"category":687,"image":867,"draft":691},"\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":451,"title":869,"description":870,"published":871,"category":687,"image":872,"draft":691},"Supabase invalid JWT: ¿qué clave dejó de funcionar?","Un error Supabase invalid JWT es PGRST301: un token firmado no pasó la verificación. En un proyecto nuevo se trata de la sesión, no de la clave de API.","2026-10-16","\u002Fblog\u002Fsupabase-invalid-jwt\u002Fcard-800x500.png",{"path":874,"title":875,"description":876,"published":877,"category":687,"image":878,"draft":691},"\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":880,"title":881,"description":882,"published":883,"category":687,"image":884,"draft":691},"\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":886,"title":887,"description":888,"published":889,"category":687,"image":890,"draft":691},"\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":892,"title":893,"description":894,"published":895,"category":687,"image":896,"draft":691},"\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":898,"title":899,"description":900,"published":901,"category":687,"image":902,"draft":691},"\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":904,"title":905,"description":906,"published":907,"category":687,"image":908,"draft":691},"\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":910,"title":911,"description":912,"published":913,"category":687,"image":914,"draft":691},"\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":916,"title":917,"description":918,"published":919,"category":687,"image":920,"draft":691},"\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":922,"title":923,"description":924,"published":925,"category":687,"image":926,"draft":691},"\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":928,"title":929,"description":930,"published":931,"category":687,"image":932,"draft":691},"\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":563,"title":934,"description":935,"published":936,"category":687,"image":937,"draft":691},"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":939,"title":940,"description":941,"published":942,"category":687,"image":943,"draft":691},"\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":945,"title":946,"description":947,"published":948,"category":687,"image":949,"draft":691},"\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":951,"title":952,"description":953,"published":954,"category":687,"image":955,"draft":691},"\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":79,"title":957,"description":958,"published":959,"category":687,"image":960,"draft":691},"¿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":962,"title":963,"description":964,"published":965,"category":687,"image":966,"draft":691},"\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":968,"title":969,"description":970,"published":971,"category":687,"image":972,"draft":691},"\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":974,"title":975,"description":976,"published":977,"category":793,"image":978,"draft":691},"\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":980,"title":981,"description":982,"published":983,"category":793,"image":984,"draft":691},"\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":986,"title":987,"description":988,"published":989,"category":793,"image":990,"draft":691},"\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":992,"title":993,"description":994,"published":989,"category":687,"image":995,"draft":691},"\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":997,"title":998,"description":999,"published":1000,"category":687,"image":1001,"draft":691},"\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":1003,"title":1004,"description":1005,"published":1006,"category":687,"image":1007,"draft":691},"\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":1009,"title":1010,"description":1011,"published":1012,"category":793,"image":1013,"draft":691},"\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":1015,"title":1016,"description":1017,"published":1018,"category":687,"image":1019,"draft":691},"\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":1021,"title":1022,"description":1023,"published":1024,"category":687,"image":1025,"draft":691},"\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":1027,"title":1028,"description":1029,"published":1030,"category":687,"image":1031,"draft":691},"\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":1033,"title":1034,"description":1035,"published":1036,"category":687,"image":1037,"draft":691},"\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":535,"title":1039,"description":1040,"published":1041,"category":687,"image":1042,"draft":691},"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":1044,"title":1045,"description":1046,"published":1047,"category":687,"image":1048,"draft":691},"\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":1050,"title":1051,"description":1052,"published":1053,"category":687,"image":1054,"draft":691},"\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":1056,"title":1057,"description":1058,"published":1059,"category":687,"image":1060,"draft":691},"\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":1062,"title":1063,"description":1064,"published":1065,"category":793,"image":1066,"draft":691},"\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":1068,"title":1069,"description":1070,"published":1071,"category":793,"image":1072,"draft":691},"\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":1074,"title":1075,"description":1076,"published":1077,"category":793,"image":1078,"draft":691},"\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":1080,"title":1081,"description":1082,"published":1083,"category":687,"image":1084,"draft":691},"\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":1086,"title":1087,"description":1088,"published":1089,"category":687,"image":1090,"draft":691},"\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":1092,"title":1093,"description":1094,"published":1095,"category":687,"image":1096,"draft":691},"\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":1098,"title":1099,"description":1100,"published":1101,"category":687,"image":1102,"draft":691},"\u002Fblog\u002Frotate-supabase-service-role-key","Cómo rotar una clave service_role de Supabase filtrada","Supabase dice que primero cierres la fuga. Otras guías dicen que rotes ya. Cuál es lo correcto depende de dónde se filtró tu clave service_role.","2026-09-08","\u002Fblog\u002Frotate-supabase-service-role-key\u002Fcard-800x500.png",{"path":1104,"title":1105,"description":1106,"published":1107,"category":687,"image":1108,"draft":691},"\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":1110,"title":1111,"description":1112,"published":1113,"category":687,"image":1114,"draft":691},"\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":1116,"title":1117,"description":1118,"published":1119,"category":687,"image":1120,"draft":691},"\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":1122,"title":1123,"description":1124,"published":1125,"category":687,"image":1126,"draft":691},"\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":540,"title":1128,"description":1129,"published":1130,"category":687,"image":1131,"draft":691},"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":1133,"title":1134,"description":1135,"published":1136,"category":687,"image":1137,"draft":691},"\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":1139,"title":1140,"description":1141,"published":1142,"category":793,"image":1143,"draft":691},"\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":1145,"title":1146,"description":1147,"published":1148,"category":687,"image":1149,"draft":691},"\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":1151,"title":1152,"description":1153,"published":1154,"category":793,"image":1155,"draft":691},"\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":1157,"title":1158,"description":1159,"published":1160,"category":687,"image":1161,"draft":691},"\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":1163,"title":1164,"description":1165,"published":1166,"category":687,"image":1167,"draft":691},"\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":1169,"title":1170,"description":1171,"published":1172,"category":687,"image":1173,"draft":691},"\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":1175,"title":1176,"description":1177,"published":1178,"category":687,"image":1179,"draft":691},"\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":1181,"title":1182,"description":1183,"published":1184,"category":793,"image":1185,"draft":691},"\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":1187,"title":1188,"description":1189,"published":1190,"category":793,"image":1191,"draft":691},"\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":1193,"title":1194,"description":1195,"published":1196,"category":687,"image":1197,"draft":691},"\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":1199,"title":1200,"description":1201,"published":1202,"category":687,"image":1203,"draft":691},"\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":1205,"title":1206,"description":1207,"published":1208,"category":793,"image":1209,"draft":691},"\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":1211,"title":1212,"description":1213,"published":1214,"category":687,"image":1215,"draft":691},"\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":1217,"title":1218,"description":1219,"published":1214,"category":687,"image":1220,"draft":691},"\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":1222,"title":1223,"description":1224,"published":1225,"category":687,"image":1226,"draft":691},"\u002Fblog\u002Fsupabase-new-api-keys","Supabase publishable key: ¿es la misma que la anon key?","Supabase sustituyó anon y service_role por claves publicables y secretas. ¿Cuál pertenece a tu app y cuál no debe estar nunca?","2026-08-12","\u002Fblog\u002Fsupabase-new-api-keys\u002Fcard-800x500.png",{"path":1228,"title":1229,"description":1230,"published":1231,"category":793,"image":1232,"draft":691},"\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":1234,"title":1235,"description":1236,"published":1237,"category":687,"image":1238,"draft":691},"\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":1240,"title":1241,"description":1242,"published":1237,"category":793,"image":1243,"draft":691},"\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":1245,"title":1246,"description":1247,"published":1248,"category":793,"image":1249,"draft":691},"\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":1251,"title":1252,"description":1253,"published":1248,"category":687,"image":1254,"draft":691},"\u002Fblog\u002Fwhich-api-keys-are-safe-in-your-frontend","Qué claves de API son seguras en el navegador y cuáles no","Tu clave anon de Supabase está pensada para ser pública. Tu clave service_role no, y se salta todas las reglas que pongas. Cómo distinguirlas.","\u002Fblog\u002Fwhich-api-keys-are-safe-in-your-frontend\u002Fcard-800x500.png",1794213645030]