Cloudflare SSL Termination with Nginx - "Upgrade Required" and CORS Issues on HTTPS Requests

I am setting up an application with the following architecture: Frontend (React): Sends HTTPS API requests to the backend through the Nginx reverse proxy. Backend (Express.js): Handles API requests directly at http://backend-server:8080/. NGINX: Acts as a reverse proxy, routing traffic from https://api.example.com to the backend. Cloudflare SSL: Provides SSL certificates for secure HTTPS communication and subdomain . Current Functionality HTTP requests: Work without issues. WebSocket connections: Successfully established and functioning. HTTPS requests (e.g., https://api.example.com/api/queue): Failing with CORS errors. Health check endpoint (e.g., https://api.example.com/api/health): Returns "Upgrade Required" error due to backend behavior. Pipeline Configuration Frontend (React): Sends requests to https://api.example.com. Uses HTTPS for secure communication. Backend (Express.js): Listens on http://backend-server:8080. Hosts API endpoints, including /api/queue and /api/health. NGINX Reverse Proxy: Routes HTTPS traffic from https://api.example.com to the backend. Configured with Cloudflare-origin SSL certificates. Cloudflare SSL: Provides TLS termination for secure HTTPS connections.
1 Reply
Hashim
HashimOP2w ago
Configuration (Simplified) Nginx Configuration:
server {
listen 443 ssl http2;
server_name api.example.com;

ssl_certificate /path/to/certificate.pem;
ssl_certificate_key /path/to/key.pem;

ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers 'HIGH:!aNULL:!MD5';

# Handle CORS for API requests
add_header Access-Control-Allow-Origin "*"; # Allow all origins for testing
add_header Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS";
add_header Access-Control-Allow-Headers "Content-Type, Authorization, X-Requested-With";
add_header Access-Control-Allow-Credentials "true";

if ($request_method = 'OPTIONS') {
add_header Access-Control-Allow-Origin "*";
add_header Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS";
add_header Access-Control-Allow-Headers "Content-Type, Authorization, X-Requested-With";
add_header Content-Length 0;
return 204;
}

# Proxy backend traffic
location / {
proxy_pass http://backend-server:8080;
proxy_http_version 1.1;
proxy_set_header Connection '';
proxy_set_header Upgrade '';
proxy_set_header Host $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;
}

# Health check endpoint
location /api/health {
proxy_pass http://backend-server:8080/api/health;
}
}
server {
listen 443 ssl http2;
server_name api.example.com;

ssl_certificate /path/to/certificate.pem;
ssl_certificate_key /path/to/key.pem;

ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers 'HIGH:!aNULL:!MD5';

# Handle CORS for API requests
add_header Access-Control-Allow-Origin "*"; # Allow all origins for testing
add_header Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS";
add_header Access-Control-Allow-Headers "Content-Type, Authorization, X-Requested-With";
add_header Access-Control-Allow-Credentials "true";

if ($request_method = 'OPTIONS') {
add_header Access-Control-Allow-Origin "*";
add_header Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS";
add_header Access-Control-Allow-Headers "Content-Type, Authorization, X-Requested-With";
add_header Content-Length 0;
return 204;
}

# Proxy backend traffic
location / {
proxy_pass http://backend-server:8080;
proxy_http_version 1.1;
proxy_set_header Connection '';
proxy_set_header Upgrade '';
proxy_set_header Host $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;
}

# Health check endpoint
location /api/health {
proxy_pass http://backend-server:8080/api/health;
}
}
The Issues Health Check Endpoint (/api/health): Fails with "Upgrade Required" error. The backend expects WebSocket headers for all requests, even for plain HTTP requests. CORS Errors on /api/queue: HTTPS requests from the React frontend to https://api.example.com/api/queue fail due to CORS policy enforcement. Summary of the Issue Goal Set up SSL termination for an API server and ensure the /health endpoint works for health checks. Problem When accessing the /health endpoint via HTTPS, the server responds with "Upgrade Required" (HTTP 426).
This error indicates the server is expecting WebSocket behavior for a plain HTTP request. Technical Setup - Nginx Configuration: - SSL is enabled using valid certificates. - A reverse proxy is set up to forward requests to the backend server. - Proxy headers are configured correctly for both HTTP and WebSocket traffic. - Backend: - The backend handles HTTP and WebSocket traffic on separate routes. Observed Behavior - HTTP requests to the /health endpoint are treated as WebSocket upgrade requests, causing the "Upgrade Required" (HTTP 426) error. #### What Was Done to Diagnose the Issue 1. Tested the backend directly using curl: - Confirmed the backend responds with HTTP 426 even when accessed without Nginx. - This ruled out Nginx as the source of the issue. 2. Debugged Nginx logs: - Verified that the correct headers are being forwarded. - Found no issues with the proxy configuration. Root Cause - The backend server enforces WebSocket behavior on all routes, including /health, which should only handle plain HTTP requests. --- Solutions Immediate Fix - Modify the backend logic to allow plain HTTP requests for the /health endpoint without expecting WebSocket headers. Alternative Workaround - Introduce a separate health check endpoint that is specifically configured for HTTP traffic. --- Next Steps - Coordinate with the backend team to adjust the route configuration for /health or create a dedicated health check route..

Did you find this page helpful?