Slow page loads under HTTP/1.1 when concurrent uploads are active — migrating from ingress-nginx to NGF v2.4.1/2.5.0 #5223
Unanswered
SJames-Magnet
asked this question in
Q&A
Replies: 0 comments
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
HTTP/2 not working through AWS NLB with TLS re-encryption to NGF — migrating from ingress-nginx
Environment
Problem
After migrating from ingress-nginx to NGINX Gateway Fabric, page loads become extremely slow (30-70+ seconds) when a file upload (TUS protocol) is running in a separate browser tab. This did not happen with ingress-nginx under the same NLB configuration.
The root cause is that HTTP/2 is not being served to the browser. Under HTTP/1.1, the browser's 6-connection-per-origin limit creates head-of-line blocking when uploads consume connections.
What HAR Analysis Shows
We captured a HAR from the page-load tab while an upload was running in another tab:
wait) are healthy: 300-900ms per requestblockedtime: requests queuing for 30-70 seconds before being sent/api/auth/v0.0/authorization/api/auth/v0.0/authorization/api/cases/v1.0/cases/api/cases/v1.0/casesArchitecture
Ingress-nginx Configuration (Working)
The ingress-nginx NLB has these annotations and works with HTTP/2:
ssl_reject_handshake offuse-http2: "true"What We've Tried on NGF
Step 1: Matched NLB annotations to ingress-nginx
Added to the NGF NginxProxy service patches:
Result: Target group changed to TLS, all targets healthy, HTTP/2 ALPN negotiated with client. But HTTP/2 stream fails.
Step 2: Changed Gateway listener to HTTPS
No
hostnamefield on the listener (catch-all).Step 3: Upgraded NGF to v2.5.1
To fix
ssl_reject_handshake onon the default_server block (PR #4878). After upgrade, the default_server uses the gateway cert instead of rejecting the handshake.Step 4: Set
disableSNIHostValidation: trueon NginxProxyThe NLB connects without proper SNI, causing a Host/SNI mismatch. This setting disables the
if ($ssl_server_name != $host) { return 421; }check.Step 5: Verified HTTP/2 works in-cluster (bypassing NLB)
Connecting directly to an NGF pod by IP address (no SNI, same as NLB behavior):
This proves NGF handles HTTP/2 correctly, including when accessed without SNI (v2.5.1 default_server routes by Host header).
Step 6: Verified HTTP/1.1 through NLB works
Step 7: Tried
disableHTTP2: trueon NginxProxyThis removed
http2 on;from the NGINX config. Result: broke both HTTP/1.1 AND HTTP/2 through the NLB. Both returned empty responses after 5-second timeout. This proves the NLB backend TLS connection requires NGF to havehttp2 onfor the TLS/ALPN handshake to function correctly.Step 8: Tested ALPN policy behavior
Set
alpn-policy: None— the NLB still served h2 to clients. This confirms that with TLS target groups, the NLB relays the backend's ALPN to the client regardless of the listener ALPN policy setting.Additional NGINX tuning applied (did not resolve HTTP/2 issue)
proxy_buffering offvia SnippetsFiltertcp_nodelay on,ssl_buffer_size 4khttp2_max_concurrent_streams 256,http2_chunk_size 4k,http2_recv_buffer_size 512kclient_max_body_size 500mCurrent State
With all fixes applied (NGF v2.5.1, TLS target group, TCP health checks,
disableSNIHostValidation, hostname-less HTTPS listener):The NLB successfully:
But after sending the HTTP/2 request to NGF, no response comes back — the connection times out after 5 seconds and the NLB sends
close_notify:Config Differences: ingress-nginx vs NGF
ssl_reject_handshake(default server)proxy_bufferingtcp_nodelayssl_buffer_sizehttp2 onssl_certificate_by_lua_filelisten reuseport backlog=4096aio threadsKey Architectural Difference
ingress-nginx uses one catch-all server block with Lua (
ssl_certificate_by_lua_file) to dynamically select certificates and route requests. All traffic flows through this single block regardless of SNI.NGF generates separate server blocks per hostname. With v2.5.1, the hostname-less HTTPS listener provides a cert for the
default_serverblock (so TLS handshakes succeed without SNI), and NGINX routes requests to the correct server block based on the:authority/Host header. This works correctly when accessed directly but fails when the NLB proxies the HTTP/2 connection.Questions
We replicated the exact ingress-nginx NLB configuration (
alpn-policy: HTTP2Preferred,backend-protocol: ssl, TLS target group, TCP health checks). HTTP/1.1 works but HTTP/2 fails with stream errors. What about NGF's architecture causes this to fail when ingress-nginx handles it successfully? Is this a known limitation?Is there a recommended architecture for NGF behind AWS NLB that preserves HTTP/2 to the client without TCP passthrough? We cannot manage the public TLS certificate in-cluster.
Could the empty default_server block (no locations) be causing the HTTP/2 stream failure? The default_server has the cert but no
server_nameand nolocationblocks. When the NLB opens an HTTP/2 connection, could the initial SETTINGS/WINDOW_UPDATE frames be processed by the empty default_server (which has nothing to respond with), causing the timeout?Is there a way to make NGF generate a single server block (similar to ingress-nginx's catch-all pattern) when all HTTPRoutes share the same hostname? Or a way to add routes/locations to the default_server block?
Could the NLB's HTTP/2 behavior differ based on target group creation method? Our target group was transitioned from TCP to TLS (via annotation change on an existing service). Ingress-nginx's was created as TLS from the start. Could this affect HTTP/2 proxying behavior?
Are there any NginxProxy or Gateway settings we've missed that could affect HTTP/2 connection handling between the NLB and NGF?
Reproduction Steps
aws-load-balancer-ssl-cert+aws-load-balancer-ssl-ports: "443"(TLS termination)aws-load-balancer-backend-protocol: ssl(TLS target group)aws-load-balancer-alpn-policy: HTTP2Preferredaws-load-balancer-healthcheck-protocol: TCPdisableSNIHostValidation: trueon NginxProxycurl --http2 https://<hostname>/Any guidance would be greatly appreciated.
Beta Was this translation helpful? Give feedback.
All reactions