Nginx
Jspreadsheet Server serves HTTP (REST API) and WebSocket (real-time sync) on one port. Nginx in front handles TLS termination, domain routing and rate limiting. The WebSocket upgrade is required: without the Upgrade/Connection headers the Socket.IO handshake falls back or fails.
Dedicated subdomain
The recommended layout gives the server its own hostname, as in the deployment guide:
Server block with WebSocket upgrade
server {
listen 443 ssl;
server_name sheets.example.com;
ssl_certificate /etc/letsencrypt/live/sheets.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/sheets.example.com/privkey.pem;
client_max_body_size 100M;
location / {
proxy_pass http://127.0.0.1:3000;
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 3600s;
}
}
Read timeout for idle WebSockets
Set proxy_read_timeout explicitly: WebSocket connections are long-lived, and the default 60s disconnects inactive collaborators.
Path prefix on an existing site
To mount the server under a path (e.g. https://example.com/sheets/) of an existing site:
Location block with prefix stripping
location /sheets/ {
proxy_pass http://127.0.0.1:3000/;
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 3600s;
}
The trailing slash on proxy_pass strips the /sheets prefix before forwarding.
Client configuration
Point the client at the prefixed URL:
const remote = client.connect({
url: 'https://example.com/sheets',
auth: { token },
});
Rate limiting
The server serializes operations per document, but auth-failure storms and anonymous Formify submissions should be throttled at the proxy:
Throttle zone for the API
limit_req_zone $binary_remote_addr zone=sheets_api:10m rate=30r/s;
location /api/ {
limit_req zone=sheets_api burst=60 nodelay;
proxy_pass http://127.0.0.1:3000;
# ... same proxy headers as above
}
Notes
Use TLS in production
Tokens travel in the WebSocket handshake query string and in Bearer headers. See the security checklist.
Configure CORS on the server
Set allowed origins in the server's config.cors option, not as Access-Control-Allow-Origin headers in Nginx; setting both produces invalid duplicate headers.
Multiple instances
Do not round-robin. One server process owns each live document, so upstream balancing must route by document guid. See Scaling.
What's Next?
- Deployment: the full production setup this proxy fronts.
- Scaling: guid routing across multiple instances.