Saltar al contenido

Firefox lleva 24 años precargando tu paginación: el caso del link rel=next en Datasette

Publicado el 9 de noviembre de 2026

Por Vicente Bonillo Alejos

Mostrador de biblioteca con dos pilas de libros preparados; una persona de espaldas se lleva solo uno y la otra pila queda sin tocar.

Una cabecera pensada para clientes de API estaba haciendo que el servidor renderizara el doble de páginas de las necesarias. Y el fallo que lo permite lleva 24 años abierto.

Qué ha pasado

Simon Willison estaba mirando el panel de red de Firefox mientras cargaba la página de una tabla de Datasette y se encontró una petición que él no había hecho: el navegador se había ido a buscar, por su cuenta, la página siguiente de filas.

El motivo es una cabecera HTTP que Datasette envía en sus respuestas para que los clientes de la API sepan por dónde continuar la paginación:

link: <https://latest.datasette.io/fixtures/compound_three_primary_keys?_next=a%2Cd%2Cv>; rel="next"

Firefox la lee y decide precargar ese destino. Resultado: de todos los que abren una tabla, solo una fracción mínima pulsa en «siguiente», pero el servidor ha tenido que generar la página entera para todos los demás y tirarla a la basura.

Al tirar del hilo, Willison descubrió que el comportamiento de Firefox tiene una incidencia abierta desde hace 24 años, y que uno de los primeros comentarios de esa incidencia enlaza a un post suyo de 2002 quejándose de exactamente esto. No es que se hubiera olvidado del post: se había olvidado de que la función existía. La solución propuesta en la incidencia 3046 de Datasette es dejar de servir esa cabecera cuando lo que se devuelve es HTML. Lo tienes contado por él en su nota del 7 de noviembre.

Por qué importa

No es un bug exótico de un proyecto concreto. Es un patrón que puedes tener montado ahora mismo sin saberlo, y tiene cuatro consecuencias feas.

Duplicas la carga justo en el sitio más caro. Un listado paginado con filtros, ordenación y recuento es, en casi cualquier aplicación, de las consultas más pesadas que se hacen. Servirla dos veces por visita, y tirar una, es el peor reparto posible.

No se ve en los logs. Esas peticiones son GET legítimos, con la misma IP, la misma sesión y el mismo user agent que el usuario real. En el log parecen alguien navegando rápido. No hay anomalía que buscar: hay que ir al panel de red del navegador y mirar qué pide tu aplicación sin que nadie se lo haya pedido.

Te ensucia los números. Contadores de visitas, analítica de servidor, límites de peticiones por minuto que saltan antes de tiempo, cachés llenas de páginas que nadie ha mirado. Si alguna vez te ha cuadrado mal la relación entre visitas y clics en una lista, mira esto.

Y si tu «siguiente» tiene efectos secundarios, ya no es un desperdicio: es un bug. Un GET no debería cambiar estado nunca, pero todos hemos visto cursores que se consumen, tokens de un solo uso, marcas de «leído» o registros de exportación colgando de una URL de paginación. Si el navegador la visita solo, se ejecutan.

Qué haría alguien distinto a partir de hoy: separar los dos públicos. Las cabeceras de paginación son estupendas para una respuesta JSON que consume un script; en una respuesta HTML que va a un navegador sobran. Decide a quién se la mandas en función de lo que estés devolviendo, no por defecto en toda la aplicación.

Qué no cambia

La cabecera Link con rel="next" no es un error. Es la forma correcta y estándar de decirle a una máquina dónde está la página siguiente, y quitarla de la API sería un retroceso. El problema no es la cabecera: es servírsela a un cliente que, en lugar de guardarla, actúa.

Tampoco es un agujero de seguridad ni una catástrofe. En un sitio pequeño con páginas baratas no lo vas a notar jamás. Duele cuando la página es cara y el tráfico es alto, que es precisamente cuando ya tenías otros cinco problemas encima.

Y no, no lo cargaría todo en la cuenta de Firefox. La precarga automática a partir de rel=next fue una idea perfectamente razonable en una época en la que lo caro era la latencia de la línea y no el renderizado en servidor. Lo que ha cambiado en estos 24 años es dónde está el coste.

Nuestra lectura

Lo interesante aquí no es el bug, es cómo se encontró: mirando el panel de red de su propia aplicación. Eso lo hace muy poca gente, y es de las cosas más rentables que puedes hacer en media hora.

Mi postura: si una cabecera cambia el comportamiento del cliente, esa cabecera es parte de tu API pública y merece la misma decisión consciente que un endpoint. No se pone «porque es lo correcto» y se olvida. Se pone sabiendo quién la va a leer. Mezclar en la misma respuesta las pistas para máquinas y el HTML para personas garantiza que antes o después una máquina haga de persona en el peor momento.

Creo además que esto va a ir a más, no a menos. Cada vez hay más capas que piden cosas «por si acaso»: reglas de especulación en el navegador, previsualizadores de enlaces de las aplicaciones de mensajería, agentes que navegan por ti y cargan tres páginas para decidir cuál leer. Todos esos clientes se toman tus pistas al pie de la letra. El patrón de 2002 no se ha arreglado, se ha multiplicado.

Y me quedo con el detalle humano: que la persona que abre la incidencia en 2026 sea la misma que se quejaba de esto en 2002 y no se acordara. Así de bien envejecen los problemas que nadie nota porque solo cuestan trabajo de servidor y no rompen nada en pantalla.

Esto es exactamente lo que nos encontramos cuando alguien dice que su servidor «se pone lento a ratos y no sabemos por qué»: el pico no lo generan los usuarios, lo genera algo automático que lleva meses ahí y que nadie ha mirado nunca con el panel de red abierto.

Cualquier duda me dices y lo vemos. Un saludo, Vicente.

Fuente: Simon Willison

¿Te ha surgido una duda leyendo esto?

Pregúntanos. Respondemos aunque no acabes siendo cliente.