When working with modern web applications that utilize both HTTP/2 and WebSocket connections through the same endpoint, traditional Nginx proxy configurations often fall short. The Atmosphere framework used in AngularJS applications presents a unique case where WebSocket connections are initiated through query parameters rather than dedicated endpoints.
The application initiates WebSocket connections with URLs containing specific Atmosphere parameters:
ws://the-user-ip/?X-Atmosphere-tracking-id=0&X-Atmosphere-Framework=2.3.2-javascript&X-Atmosphere-Transport=websocket
Here's a refined configuration that handles both protocols in the same location block without conditional logic:
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
server {
listen 443 ssl http2;
server_name ~^\d+\.example\.co$;
location / {
proxy_pass http://ipmask_docker_app;
proxy_http_version 1.1;
# Standard HTTP headers
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;
# WebSocket upgrade headers
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
# Atmosphere-specific handling
proxy_set_header X-Atmosphere-tracking-id $arg_X-Atmosphere-tracking-id;
proxy_set_header X-Atmosphere-Framework $arg_X-Atmosphere-Framework;
}
}
The solution leverages several Nginx features:
- The
map
directive creates conditional header values - HTTP/1.1 is required for WebSocket support
- All Atmosphere parameters are preserved through the proxy
- Single location block handles both protocols
If connections fail, check these common issues:
- Verify
proxy_http_version 1.1
is present - Ensure WebSocket headers are being passed through
- Check for HTTPS/WSS protocol mismatches
- Monitor Nginx error logs for connection issues
For high-traffic implementations:
proxy_buffer_size 128k;
proxy_buffers 4 256k;
proxy_busy_buffers_size 256k;
proxy_read_timeout 3600; # Extended for long-lived WS connections
When building modern web applications, we often need to handle both traditional HTTP requests and WebSocket connections through the same endpoint. This becomes particularly tricky when trying to proxy these connections through Nginx while maintaining proper protocol upgrades and header handling.
The Atmosphere framework uses a clever detection mechanism for WebSocket connections through specific query parameters:
ws://the-user-ip/?X-Atmosphere-tracking-id=0&X-Atmosphere-Framework=2.3.2-javascript&X-Atmosphere-Transport=websocket&Content-Type=application/json&X-atmo-protocol=true
This means our Nginx configuration needs to inspect the request and conditionally set headers based on whether it's a WebSocket connection.
Here's an improved version that properly handles both protocol types:
upstream backend_app {
server backend:5050;
}
server {
server_name "~^\\d+\\.example\\.co$";
listen 443 ssl http2;
listen [::]:443 ssl http2;
location / {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
proxy_set_header Host $http_host;
# Default HTTP/HTTPS proxy settings
proxy_pass http://backend_app;
proxy_http_version 1.1;
# WebSocket detection and upgrade
if ($args ~* "X-Atmosphere-tracking-id") {
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
}
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
}
}
The critical components that make this work:
- proxy_http_version 1.1: Required for WebSocket support
- Conditional header setting: Only applies WebSocket headers when the Atmosphere query params are detected
- Single location block: Handles both protocols in one place
To verify your configuration works:
- Check Nginx error logs for any WebSocket upgrade failures
- Use browser developer tools to inspect the WebSocket handshake
- Test with both regular HTTP requests and WebSocket connections
If modifying the client application is possible, separating WebSocket traffic to a different path is cleaner:
location / {
proxy_pass http://backend_app;
# Standard HTTP proxy settings
}
location /websocket {
proxy_pass http://backend_app;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
}
This approach is more maintainable but requires client-side changes.