Multipath TCP (MPTCP) para DiscourseHub y PWA de iOS Safari detrás de Caddy

Continuando la discusión desde Caddy delante del Nginx de la aplicación mediante Docker Compose:

Tras mi discusión anterior sobre la ejecución de Caddy delante del contenedor nginx de Discourse para proporcionar HTTP/3 y QUIC 0-RTT, he estado investigando una pregunta separada pero relacionada: si Multipath TCP (MPTCP) podría mejorar la continuidad de la conexión para usuarios de iOS que se mueven entre redes Wi-Fi y móviles.

Mi trabajo anterior se centraba principalmente en el rendimiento de la carga inicial de páginas. Esta pregunta es diferente: accedo regularmente a mi archivo privado de estudio de Discourse mientras me desplazo entre actividades, y las interrupciones de conexión o el buffering pueden retrasar el acceso a información importante.

Por qué lo pregunto

En algunas ubicaciones, el Wi-Fi es fiable mientras que la recepción móvil es deficiente; en otras, la situación es inversa.

El conmutado de red habitual en iOS no siempre ha proporcionado la continuidad que necesito. Ya utilizo una tecnología separada de enlazado/conmutación de red (bonding/failover) para la resiliencia de la conexión, pero me interesa comprender si parte de esa continuidad podría lograrse de forma nativa entre un cliente iOS y mi servidor de Discourse.

En particular, me interesa la transferencia de conexión (handover) y evitar solicitudes detenidas, más que necesariamente agregar el ancho de banda de dos conexiones.

Lo que he encontrado hasta ahora

La documentación de compatibilidad de MPTCP para Linux lista a Caddy v2.10.0 en adelante como compatible con MPTCP de forma predeterminada, sujeto al soporte del sistema operativo.

La documentación de MPTCP de Apple describe cómo las aplicaciones pueden optar por MPTCP a través de URLSessionConfiguration.

Sin embargo, también encontré una explicación en los foros de desarrolladores de Apple que indica que WKWebView no expone una opción de configuración equivalente para MPTCP.

Esto me deja incierto sobre si DiscourseHub, Safari o una PWA instalada en iOS podrían beneficiarse de MPTCP, particularmente cuando también está disponible HTTP/3 sobre QUIC.

Entorno del servidor confirmado

He verificado el VPS de producción utilizando comandos de solo lectura, sin modificar su configuración.

  • Proxy inverso: Caddy v2.11.4, imagen Docker oficial caddy:2.11.4

  • Anfitrión: Ubuntu, kernel de Linux 6.8.0-146-generic

  • MPTCP del anfitrión: net.mptcp.enabled = 1

  • MPTCP del contenedor: /proc/sys/net/mptcp/enabled = 1

  • Red de Docker: caddy_default, controlador bridge, opciones {}

  • Entorno de Go: GODEBUG sin establecer

  • Puertos publicados: TCP 80, TCP 443 y UDP 443, en IPv4 e IPv6

El nginx de Discourse permanece detrás de Caddy, con Caddy proporcionando el punto de acceso público HTTPS y HTTP/3.

Estas comprobaciones sugieren que el servidor tiene los requisitos básicos para el soporte de MPTCP, aunque no he verificado la negociación real de MPTCP a través del reenvío de puertos publicados de Docker.

Preguntas

  1. DiscourseHub: ¿Su red iOS utiliza componentes capaces de solicitar MPTCP, o su contenido web principal se maneja a través de APIs de red que no exponen esta capacidad?

  2. Safari y PWAs instaladas: ¿Hay alguna diferencia entre acceder a Discourse a través de DiscourseHub, Safari y una PWA instalada en iOS? ¿Debe el cliente solicitar explícitamente MPTCP, o podría alguno de estos métodos de acceso beneficiarse de forma transparente?

  3. Red de Docker: ¿Puede la configuración bridge/NAT preservar la negociación inicial de MPTCP y los subflujos adicionales, o podría esto causar que las conexiones retrocedan a TCP ordinario?

  4. HTTP/3 frente a MPTCP: Dado que HTTP/3 utiliza QUIC sobre UDP en lugar de TCP, ¿la migración de conexión de QUIC proporcionaría alguna continuidad comparable al cambiar de red? ¿Existe una distinción práctica entre HTTP/2 sobre MPTCP y HTTP/3 sobre QUIC en esta situación?

  5. Recuperación en el lado de Discourse: ¿Hay mecanismos existentes en Discourse o en sus clientes iOS para recuperar solicitudes interrumpidas cuando cambia la red subyacente? ¿Hay una forma recomendada de establecer qué transporte está utilizando realmente un cliente iOS?

No estoy proponiendo que Discourse mismo gestione múltiples interfaces de red. Estoy tratando de comprender dónde reside la responsabilidad entre el cliente iOS, el protocolo de transporte, Docker/Caddy y Discourse, y si hay capacidades existentes de las que podría beneficiarme.

Agradecería cualquier orientación o corrección a mi comprensión.

¡Gracias!

Seguimiento: edición interrumpida en iOS y continuidad de la conexión HTTP/3

Ahora he encontrado un ejemplo práctico del problema de continuidad de la conexión sobre el que estaba preguntando.

El 21 de octubre de 2026, mientras editaba una publicación en mi instancia privada de Discourse en un iPhone 16 Pro utilizando la PWA de Safari, la barra azul Guardando se quedó atascada. Acababa de dictar algunas notas adicionales de la clase.

La composición ya no era accesible, incluso al tocar la barra azul Guardando, por lo que no pude copiar el texto sin guardar en otro lugar.

Tras esperar más tiempo de lo habitual, desactivé manualmente el Wi-Fi para intentar usar la conectividad celular. La página no cambió inmediatamente, pero los intentos posteriores de navegar también parecían colgarse.

Posteriormente, examiné los registros de Caddy y nginx, así como el historial de revisiones de la publicación.

A las 12:20:08 BST:

  • Caddy registró una solicitud PUT /posts/70695 de HTTP/3 desde la conexión Wi-Fi de la universidad, con todos los 1.000 bytes del cuerpo de la solicitud recibidos, pero con estado 0.

  • nginx registró la solicitud correspondiente como HTTP 400, sin tiempo de procesamiento de ruta de Discourse ni de servidor upstream.

  • Dos otras solicitudes de iPhone originadas desde la conexión celular también recibieron HTTP 400 en el mismo segundo.

  • No se registró ninguna revisión exitosa de la publicación en ese momento.

Una edición posterior tuvo éxito a las 12:20:55 BST, pero faltaba el texto dictado.

También hubo un autoguardado exitoso de borrador de Discourse a las 12:19:44 BST, aunque no puedo establecer si contenía el texto perdido.

Aprecio que desactivar manualmente el Wi-Fi pudo haber interrumpido las solicitudes pendientes. Sin embargo, la operación original de Guardar ya estaba atascada antes de que cambiara la conexión de red.

Esto plantea dos preguntas relacionadas con mi publicación original:

  1. Migración de conexiones QUIC: ¿Existe una forma recomendada de determinar si el navegador de iOS intentó migrar la conexión HTTP/3 existente, en lugar de establecer una nueva conexión o abandonar las solicitudes pendientes?

  2. Recuperación del compositor de Discourse: Si una solicitud de edición de publicación se atasca o se interrumpe, ¿se espera que el compositor permanezca inaccesible detrás del estado de Guardando? ¿Existe algún mecanismo de tiempo de espera, reintentos o recuperación que debería preservar el texto sin guardar?

Me interesa particularmente la segunda pregunta porque no pude volver al editor para copiar mi trabajo antes de intentar restablecer la conectividad.

No he cambiado ninguna configuración de Caddy, nginx o HTTP/3 como resultado de este incidente, ya que las pruebas disponibles aún no identifican qué causó que la operación inicial de Guardar se detuviera.