Una petición falló. No hay código de estado, porque ninguna respuesta llegó nunca. Y cuando buscas en tu registro de acceso la petición que falló, no hay línea para ella.

Esa combinación es la firma de un fallo por debajo de HTTP, y ERR_HTTP2_PROTOCOL_ERROR es la forma más común en que Chrome lo reporta. Vale la pena diez minutos porque casi todo lo escrito sobre él recomienda el mismo arreglo, y el arreglo es un diagnóstico que la gente confunde con una cura.

Qué dice realmente el código

La lista de errores de red de Chromium, que es de dónde se generan estas cadenas, lo define en seis palabras:

Hay un error de protocolo HTTP/2.

Ese es el registro completo. El número es -337, y nada en él nombra una causa, una dirección o una parte. Buscar la cadena te consigue mil páginas adivinando, porque la fuente misma está adivinando también.

Los seis que no son vagos, y por qué eso importa

Aquí está la parte que nadie menciona. Chromium no tiene un error HTTP/2. Tiene varios, y todos los demás son específicos:

  • ERR_HTTP2_SERVER_REFUSED_STREAM, -351, que la fuente describe extensamente: el servidor “rechazó la petición sin procesarla” y lo dijo, ya sea con una trama GOAWAY llevando código de error NO_ERROR y una Last-Stream-ID por debajo de la trama de esta petición, o una RST_STREAM con REFUSED_STREAM. El comentario añade el detalle operativo: “El cliente PUEDE reintentar (en una conexión diferente).”
  • ERR_HTTP2_PING_FAILED, -352: “El servidor HTTP/2 no respondió al mensaje PING.”
  • ERR_HTTP2_INADEQUATE_TRANSPORT_SECURITY, -360: “La seguridad de transporte es insuficiente para la versión HTTP/2.”
  • ERR_HTTP2_FLOW_CONTROL_ERROR, -361, ERR_HTTP2_FRAME_SIZE_ERROR, -362, y ERR_HTTP2_COMPRESSION_ERROR, -363.

Así que cuando obtienes -337 en lugar de uno de esos, has aprendido algo real: no fue un flujo rechazado, no fue un keepalive muerto, no fue un problema de cifra, no fue control de flujo, no fue un tamaño de trama, no fue compresión de cabecera. ERR_HTTP2_PROTOCOL_ERROR es el “ninguno de los anteriores” de HTTP/2, y los anteriores son seis cosas nombradas que ahora puedes dejar de investigar.

Ese es un resultado más pequeño que una causa, y es mucho mejor que nada, que es lo que el mensaje por sí solo parece ofrecer.

Por qué tus registros están vacíos

La razón de que no haya línea en el registro de acceso está en la especificación, y vale la pena leerla porque también explica por qué un usuario reporta varias cosas rompiéndose a la vez.

RFC 9113 separa dos tipos de fallo. “Un error de trama es un error relacionado con una trama específica que no afecta al procesamiento de otras tramas.” Un error de conexión es la otra cosa completamente: “cualquier error que evite el procesamiento adicional de la capa de trama o que corrompa cualquier estado de conexión.”

HTTP/2 multiplexa. Una conexión lleva muchas peticiones al mismo tiempo, así que un error de conexión no falla la petición que lo provocó. Falla cada petición en vuelo en esa conexión.

Qué dice el informe
Una página cargada a medias, faltan algunas imágenes y un envío de formulario no hizo nada. Tres errores separados, reportados por separado
Qué pasó
Un error de conexión derribó todo en vuelo en esa conexión en el mismo instante. Una causa, tres síntomas, y sin código de estado en ninguno de ellos

Y porque el intercambio nunca se completó como HTTP, no hay nada para tu servidor registrar. Ningún método, ningún camino, ningún estado. El usuario vio algo fallar y tus registros muestran un hueco donde debería haber estado.

Qué es seguro reintentar, al que el protocolo sí responde

Esta es la pregunta que importa si un pago o un envío de formulario estaba en vuelo, y tiene una respuesta real en lugar de un encogimiento de hombros.

RFC 9113 dice que “una terminal que encuentra un error de conexión DEBERÍA primero enviar una trama GOAWAY (Sección 6.8) con el identificador de trama de la última trama que procesó con éxito de su par.”

Ese identificador es una línea trazada a través de las peticiones. Cualquier cosa numerada por encima de ella no fue recibida, lo que significa que no fue procesada, lo que significa que reintentar no puede duplicar nada. Esa es precisamente la razón por la que la propia nota de Chromium sobre un flujo rechazado dice que el cliente puede reintentar en una conexión diferente.

El problema es que esta información existe durante cerca de un segundo, dentro del navegador, y luego desaparece. No está en tu registro de servidor, porque las tramas que la llevaban están por debajo de la capa que tu servidor registra.

Session Replay

Extensión gratuita de Chrome. Un clic en la página que se está portando mal captura la captura de pantalla, la consola y el registro de red, y te devuelve un enlace para pegar en el ticket.

Instalar la extensión

“Desactiva HTTP/2” es un diagnóstico, no un arreglo

El consejo que encontrarás en todas partes es desactivar HTTP/2 y ver si el problema se detiene. Es un buen consejo y es ampliamente malinterpretado.

Si los errores se detienen, has aprendido dónde vive el fallo: algo en la capa HTTP/2 entre ese usuario y tú está manejando mal las tramas. No has aprendido que HTTP/2 no sea confiable, y no has arreglado nada. Has desactivado el multiplexado para cada visitante para trabajar alrededor del error de una parte.

Esa parte no es usualmente tu servidor. Las causas comunes son las cosas en el medio: un proxy inverso o equilibrador de carga con su propia implementación de HTTP/2, una CDN, equipo de red corporativa que termina y restablece TLS, y software de seguridad en la propia máquina del usuario haciendo lo mismo. Cada uno habla HTTP/2 en tu nombre, y cada uno es un lugar donde una trama puede estar malformada sin que nadie en uno u otro extremo haga algo mal.

Lo que explica por qué esto a menudo afecta una oficina, un ISP o un producto antivirus, y a nadie más.

Qué preguntar a la persona que lo sufrió

Dos preguntas, y la segunda vale más de lo que parece.

La cadena exacta de la consola o la página de error. ERR_HTTP2_PROTOCOL_ERROR y ERR_HTTP2_SERVER_REFUSED_STREAM son problemas diferentes con respuestas diferentes, y un informe que dice “un error de red” ha descartado la única distinción que era gratis.

¿Sucede en una red diferente? Si se detiene en datos móviles, la causa está entre ellos y tú en lugar de en tu servidor, y has estrechado la culpa al equipo de su red en una pregunta. Esta es la misma prueba que separa un proxy corporativo de un verdadero problema de certificado en la pantalla tu conexión no es privada, y funciona aquí por la misma razón.

Con qué quedarse

El mensaje es vago porque la fuente es vaga, y saber eso te detiene leyendo significado en él que no está ahí. Lo que sí te dice es todo lo que no es, y seis alternativas nombradas es un estrechamiento real.

El resto sigue de dónde el fallo se sienta. Está por debajo de HTTP, así que no hay código de estado y a menudo no hay línea de registro. Es un error de conexión, así que lleva varias peticiones con él y llega como varios informes de fallo. Y la cosa que te diría si un reintento es seguro es una trama que el navegador vio y tu servidor nunca hizo.

La lista de Chromium de estos errores, con la definición de una línea de cada uno, está en net_error_list.h, y el protocolo en sí es RFC 9113.