Skip to content

XHR requests fail with 400 on HTTP and HTTP/2 error on HTTPS when using nginx reverse proxy #7961

@ygqygq2

Description

@ygqygq2

Environment

  • Kubernetes Version: 1.33.4
  • Envoy Gateway Version: 1.6.1
  • Installation: Default configuration (Helm)

Problem

I have an nginx frontend pod that serves static files and reverse proxies /api/* to a backend Java Gateway service.

What works:

  • ✅ Static files (HTML/CSS/JS) load correctly via Envoy Gateway
  • Direct access to API endpoints in a separate browser tab works perfectly (both HTTP and HTTPS)

What fails:

  • ❌ XHR/API requests from the web application via HTTP (port 80): Return 400 Bad Request
  • ❌ XHR/API requests from the web application via HTTPS (port 443): Return HTTP/2 error

Key Finding

This is the most important clue: When I open API endpoints like http://gateway-ip/api/getConfig or https://gateway-ip/api/getConfig directly in a new browser tab, they work perfectly fine and return correct JSON responses.

However, when the exact same endpoints are called via XHR from the JavaScript application running in the browser, they fail with 400 or HTTP/2 errors.

This suggests the issue is related to how Envoy Gateway handles XHR requests specifically, possibly related to CORS, request headers, or how it processes requests from JavaScript versus direct browser navigation.

Architecture

Browser → Envoy Gateway (LoadBalancer) → nginx Pod → /api/* → Java Gateway Service

Nginx Configuration

Basic reverse proxy setup in nginx:

location / {
    root /usr/share/nginx/html;
    try_files $uri $uri/ /index.html;
}

location /api/ {
    proxy_pass http://java-gateway:8080/api/;
    proxy_http_version 1.1;
    proxy_set_header Host $http_host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
}

Observed Behavior

From browser Network tab:

  • Static resources: Status: 200, Type: javascript/png/etc - ✅ Success
  • API direct access (new tab): Status: 200, Type: xhr/document - ✅ Success
  • API XHR calls (from app): Status: 400 (HTTP) or (failed) HTTP/2 error (HTTPS), Type: xhr - ❌ Failed

All XHR requests to endpoints like /api/getConfig, /api/getLoginConfig fail when called from JavaScript, but the same URLs work when accessed directly.

What I've Tried

I attempted various ClientTrafficPolicy configurations:

  • Increased buffer limits up to 10Mi
  • Disabled all timeouts (set to 0s)
  • Configured HTTP/2 window sizes
  • Added BackendTrafficPolicy with permissive circuit breaker settings

Result: None of these changes resolved the issue. XHR requests continue to fail.

Comparison

The exact same nginx configuration works perfectly with:

  • ✅ nginx-ingress-controller
  • ✅ Direct NodePort access to nginx

The issue only occurs with Envoy Gateway.

Question

Is there a known compatibility issue with Envoy Gateway when nginx acts as a reverse proxy to backend services? This is a very common architecture pattern (frontend nginx → backend APIs), and it's surprising that it doesn't work out of the box.

The fact that direct browser navigation works but XHR calls fail suggests there might be specific headers or request characteristics that Envoy Gateway is rejecting when the request comes from JavaScript.

Any guidance on required configuration or workarounds would be greatly appreciated. This is currently a production blocker for us.

Additional Context

  • Browser screenshots showing the failures are attached above
  • Envoy proxy logs show 400 responses for XHR requests
  • No obvious errors in Envoy Gateway controller logs
  • Same API endpoints work when accessed directly in browser, fail when called via XHR from app
Image

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions