HTTP2 con node.js detrás del proxy nginx

Resuelto Shade asked hace 7 años • 5 respuestas

Tengo un servidor node.js ejecutándose detrás de un proxy nginx. node.js ejecuta un servidor HTTP 1.1 (sin SSL) en el puerto 3000. Ambos se ejecutan en el mismo servidor.

Recientemente configuré nginx para usar HTTP2 con SSL (h2). Parece que HTTP2 está habilitado y funcionando.

Sin embargo, quiero saber si el hecho de que la conexión proxy (nginx <--> node.js) utilice HTTP 1.1 afecta el rendimiento. Es decir, ¿me estoy perdiendo los beneficios de HTTP2 en términos de velocidad porque mi conexión interna es HTTP 1.1?

Shade avatar Jan 13 '17 21:01 Shade
Aceptado

En general, el mayor beneficio inmediato de HTTP/2 es el aumento de velocidad que ofrece la multiplexación para las conexiones del navegador, que a menudo se ven obstaculizadas por una alta latencia (es decir, una velocidad lenta de ida y vuelta). Esto también reduce la necesidad (y el gasto) de múltiples conexiones, lo cual es una solución alternativa para intentar lograr beneficios de rendimiento similares en HTTP/1.1.

Para las conexiones internas (por ejemplo, entre un servidor web que actúa como proxy inverso y servidores de aplicaciones back-end), la latencia suele ser muy, muy baja, por lo que los beneficios de velocidad de HTTP/2 son insignificantes. Además, cada servidor de aplicaciones normalmente ya será una conexión separada, por lo que nuevamente no hay ganancias aquí.

Por lo tanto, obtendrá la mayor parte de su beneficio de rendimiento simplemente admitiendo HTTP/2 en el borde. Esta es una configuración bastante común, similar a la forma en que HTTPS a menudo finaliza en el proxy inverso/equilibrador de carga en lugar de completarse por completo.

Sin embargo, existen beneficios potenciales al admitir HTTP/2 en todo momento. Por ejemplo, podría permitir que el servidor envíe todo el proceso desde la aplicación. También beneficios potenciales de un tamaño de paquete reducido para ese último salto debido a la naturaleza binaria de HTTP/2 y la compresión de encabezados. Aunque, al igual que la latencia, el ancho de banda suele ser un problema menor para las conexiones internas, por lo que su importancia es discutible. Finalmente, algunos argumentan que un proxy inverso hace menos trabajo al conectar una conexión HTTP/2 a una conexión HTTP/2 que a una conexión HTTP/1.1, ya que no es necesario convertir un protocolo a otro, aunque soy escéptico si eso es posible. Se nota porque son conexiones separadas (a menos que actúe simplemente como un proxy de paso TCP). Entonces, para mí, la razón principal para HTTP/2 de extremo a extremo es permitir Server Push de extremo a extremo, pero incluso eso probablemente se maneje mejor con encabezados de enlace HTTP y 103-Early Hints debido a las complicaciones en la gestión de push a través de múltiples conexiones. y no conozco ningún servidor proxy HTTP que admita esto (pocos son compatibles con HTTP/2 en el backend, no importa encadenar conexiones HTTP/2 como esta), por lo que necesitaría un equilibrador de carga de capa 4 que reenvíe empaquetadores TCP en lugar de encadenar solicitudes HTTP, lo que trae otras complicaciones.

Por ahora, aunque los servidores todavía están agregando soporte y el uso de inserción del servidor es bajo (y todavía se está experimentando para definir las mejores prácticas), recomendaría tener solo HTTP/2 en el punto final. Al momento de escribir este artículo, Nginx tampoco admite HTTP/2 para conexiones ProxyPass (aunque Apache sí lo hace), y no tiene planes de agregar esto , y hacen un comentario interesante sobre si una sola conexión HTTP/2 podría introducir lentitud. (el énfasis es mío):

¿Está prevista la compatibilidad con el proxy HTTP/2 en un futuro próximo?

Respuesta corta:

No, no hay planes.

Respuesta larga:

Casi no tiene sentido implementarlo, ya que el principal beneficio de HTTP/2 es que permite multiplexar muchas solicitudes dentro de una sola conexión, eliminando así [casi] el límite en el número de solicitudes simalténeas, y no existe tal límite cuando se habla con tus propios backends. Además, las cosas pueden incluso empeorar cuando se utiliza HTTP/2 en backends, debido a que se utiliza una única conexión TCP en lugar de varias .

Por otro lado, implementar el protocolo HTTP/2 y la multiplexación de solicitudes dentro de una única conexión en el módulo ascendente requerirá cambios importantes en el módulo ascendente.

Debido a lo anterior, no hay planes para implementar soporte HTTP/2 en el módulo ascendente, al menos en el futuro previsible. Si todavía cree que es necesario hablar con los servidores a través de HTTP/2, no dude en proporcionar parches.

Finalmente, también se debe tener en cuenta que, si bien los navegadores requieren HTTPS para HTTP/2 (h2), la mayoría de los servidores no lo hacen y, por lo tanto, podrían admitir este salto final sobre HTTP (h2c). Por lo tanto, no habría necesidad de cifrado de extremo a extremo si no está presente en la parte del Nodo (como a menudo no lo está). Sin embargo, dependiendo de dónde se encuentre el servidor backend en relación con el servidor front-end, usar HTTPS incluso para esta conexión es quizás algo que debería considerarse si el tráfico viajará a través de una red no segura (por ejemplo, CDN al servidor de origen a través de Internet).

EDITAR AGOSTO 2021

HTTP/1.1 al estar basado en texto en lugar de binario lo hace vulnerable a varios ataques de contrabando de solicitudes. En Defcon 2021, PortSwigger demostró una serie de ataques de la vida real , en su mayoría relacionados con problemas al degradar las solicitudes HTTP/2 del front-end a solicitudes HTTP/1.1 del back-end. Probablemente esto podría evitarse en su mayoría hablando HTTP/2 en todo momento, pero dado el soporte actual de servidores front-end y CDN para hablar HTTP/2 al backend, y backends para admitir HTTP/2, parece que llevará mucho tiempo. para que esto sea común, y los servidores front-end HTTP/2 que garanticen que estos ataques no sean explotables parece la solución más realista.

Barry Pollard avatar Jan 14 '2017 08:01 Barry Pollard

NGINX ahora admite HTTP2/Push proxy_passy es increíble...

Aquí también estoy publicando favicon.ico, minified.css, minified.js, Register.svg, buy_litecoin.svg desde mi subdominio estático. Me tomó algún tiempo darme cuenta de que puedo enviar mensajes desde un subdominio.

location / {
            http2_push_preload              on;
            add_header                      Link "<//static.yourdomain.io/css/minified.css>; as=style; rel=preload";
            add_header                      Link "<//static.yourdomain.io/js/minified.js>; as=script; rel=preload";
            add_header                      Link "<//static.yourdomain.io/favicon.ico>; as=image; rel=preload";
            add_header                      Link "<//static.yourdomain.io/images/register.svg>; as=image; rel=preload";
            add_header                      Link "<//static.yourdomain.io/images/purchase_litecoin.svg>; as=image; rel=preload";
            proxy_hide_header               X-Frame-Options;
            proxy_http_version              1.1;
            proxy_redirect                  off;
            proxy_set_header                Upgrade $http_upgrade;
            proxy_set_header                Connection "upgrade";
            proxy_set_header                X-Real-IP $remote_addr;
            proxy_set_header                Host $http_host;
            proxy_set_header                X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header                X-Forwarded-Proto $scheme;
            proxy_pass                      http://app_service;
        }
buycanna.io avatar Sep 03 '2018 00:09 buycanna.io

Por si alguien está buscando una solución a esto cuando no es conveniente hacer que sus servicios sean compatibles con HTTP2. Aquí está la configuración básica de NGINX que puede utilizar para convertir el servicio HTTP1 en el servicio HTTP2.

server {
  listen [::]:443 ssl http2;
  listen 443 ssl http2;

  server_name localhost;
  ssl on;
  ssl_certificate /Users/xxx/ssl/myssl.crt;
  ssl_certificate_key /Users/xxx/ssl/myssl.key;

  location / {
    proxy_pass http://localhost:3001;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_set_header Host $host;
  }
}
Sheng avatar Jan 16 '2019 22:01 Sheng

NGINX no admite HTTP/2 como cliente. Como se ejecutan en el mismo servidor y no hay latencia ni ancho de banda limitado, no creo que haya una gran diferencia en ambos sentidos. Me aseguraría de que esté utilizando keepalives entre nginx y node.js.

https://www.nginx.com/blog/tuning-nginx/#keepalive

Faisal Memon avatar Jan 13 '2017 17:01 Faisal Memon