worker_processes 1; events { worker_connections 1024; } http { include mime.types; default_type application/octet-stream; sendfile on; # helper for ws upgrade header map $http_upgrade $connection_upgrade { default upgrade; '' close; } # decide if request at "/" is a websocket upgrade: map $http_upgrade $root_backend { default http://127.0.0.1:8501; # normal HTTP -> Gradio "~*upgrade" http://127.0.0.1:8765; # WS upgrade -> bridge "~*websocket" http://127.0.0.1:8765; } upstream app { server 127.0.0.1:8501; } # Gradio upstream meshcat { server 127.0.0.1:7000; } # MeshCat HTTP (/static) & WS at "/" server { listen 7860; # MeshCat viewer HTML lives under /static/ — mount at /meshcat/ location /meshcat/ { proxy_pass http://meshcat/static/; # trailing slash matters proxy_set_header Host $host; } # MeshCat HTML references /static/... absolutely — forward those too location /static/ { proxy_pass http://meshcat/static/; proxy_set_header Host $host; } # ROOT "/": # - normal HTTP (page loads, XHR, etc.) -> Gradio app # - WebSocket upgrade (opened by MeshCat JS to ws://HOST:7860/) -> WS bridge location / { proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection $connection_upgrade; proxy_set_header Host $host; proxy_read_timeout 3600s; proxy_send_timeout 3600s; proxy_pass $root_backend; } } }