I am setting up a React application with a backend API and WebSocket server, using Nginx as a reverse proxy. However, my WebSocket client fails to connect to the server, and I get the error: Socket connection error: Failed to connect to the socket server.
Additionally, I want the Nginx configuration to be dynamic so that the same React build works in different environments (local, staging, production) without needing to rebuild the app for each environment.
Here’s my setup:
Nginx Configuration (
nginx.conf
)events {} http { include /etc/nginx/mime.types; include /etc/nginx/conf.d/sites-enabled/*.conf; server { set $api_url "http://192.168.1.3:3000"; # Dynamic backend server URL (replaceable for different environments) listen 80; server_name 192.168.1.3; location / { root /usr/share/nginx/html; index index.html index.htm; try_files $uri $uri/ /index.html; } location /api { rewrite ^/api/(.*)$ /$1 break; proxy_pass $api_url; proxy_http_version 1.1; proxy_set_header Connection ''; 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; } location /socket.io/ { proxy_pass $api_url; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "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; proxy_read_timeout 600s; proxy_send_timeout 600s; } location /static { root /usr/share/nginx/html; } } }
Docker Compose Configuration (
docker-compose.yml
)version: "3.8" services: web: image: nginx:alpine container_name: web-app ports: - "5000:80" volumes: - ./build:/usr/share/nginx/html:ro - ./nginx.conf:/etc/nginx/nginx.conf:ro environment: - NODE_ENV=production restart: always
React
.env
FileREACT_APP_API_URL=/api REACT_APP_SOCKET_HOST=
Socket Client (
socketclient.ts
)import { io, Socket } from "socket.io-client"; class SocketClient { private socket: Socket | null = null; connect(): Promise<void> { return new Promise((resolve, reject) => { if (!process.env.REACT_APP_SOCKET_HOST) { reject("Socket host is not defined."); return; } this.socket = io(process.env.REACT_APP_SOCKET_HOST as string, { reconnection: true, transports: ["websocket", "polling"], }); this.socket.on("connect", () => resolve()); this.socket.on("connect_error", (error) => { console.error("Socket connection error:", error); reject(error); }); }); } disconnect(): Promise<void> { return new Promise((resolve, reject) => { if (this.socket) { this.socket.disconnect(); this.socket.once("disconnect", () => { this.socket = null; resolve(); }); } else { reject("No socket connection."); } }); } emit(event: string, data: any): Promise<void> { return new Promise((resolve, reject) => { if (!this.socket) return reject("No socket connection."); this.socket.emit(event, data, (response: any) => { if (response?.error) { return reject(response.error); } resolve(); }); }); } on(event: string, callback: (data: any) => void): void { if (!this.socket) throw new Error("No socket connection."); this.socket.on(event, callback); } } export default new SocketClient();
Observed Behavior
The React frontend loads properly.
API requests (
/api
) proxied through Nginx work as expected.WebSocket connections (
socket.io
) fail with the error:Socket connection error: Failed to connect to the socket server