If you have ever spun up Uptime Kuma and then watched it break the moment you put it behind a reverse proxy, you are not alone. The dashboard spins, the “Connecting…” badge appears, and a few seconds later you get the dreaded “Cannot connect to the socket server. [Error: websocket error] Reconnecting…” message in the bottom-left corner.
I ran into this exact issue myself on my first homelab setup. I had Uptime Kuma running fine on port 3001, pointed Nginx at it, and instantly broke the live dashboard. The reason is simple once you know it: Uptime Kuma is one of the few web apps that depends entirely on WebSocket for its real-time updates, and most reverse proxies ship with WebSocket support turned off by default.
This guide is the one I wish I had back then. We will deploy Uptime Kuma with Docker Compose, configure a reverse proxy with the two headers that fix everything, enable HTTPS with Let’s Encrypt, and then walk through the common WebSocket errors and how to solve them. We will also cover Apache, Caddy, Traefik, Cloudflare Tunnel, and Nginx Proxy Manager, so pick the one that fits your stack.
By the end, you will have a working self-hosted Uptime Kuma behind a reverse proxy with WebSocket fixes, plus a troubleshooting checklist for the next time something misbehaves. The whole setup takes 15 to 30 minutes on a clean VPS, and the principles apply to any monitoring tool that uses WebSocket, not just Uptime Kuma.
Table of Contents
Quick Answer: Why Uptime Kuma Breaks Behind a Reverse Proxy?
Uptime Kuma breaks behind a reverse proxy because it relies on WebSocket for its live dashboard, and the proxy is not passing the right headers. You need exactly two extra headers in your proxy config: Upgrade and Connection, both set to upgrade. Without them, the browser cannot establish a long-lived WebSocket connection, and Uptime Kuma shows “Cannot connect to the socket server.”
Louis Lam, the creator of Uptime Kuma, says it directly in the official GitHub Wiki: “Unlike other web apps, Uptime Kuma is based on WebSocket. You need two more headers ‘Upgrade’ and ‘Connection’ in order to accept WebSocket on a reverse proxy.” The project has 76,000+ GitHub stars, and this single sentence is the source of thousands of forum posts asking why the dashboard stops responding.
For Nginx, the minimum config snippet is:
location / {
proxy_pass http://127.0.0.1:3001;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
}If you remember nothing else from this article, remember those four lines. They are the fix 90% of people need, and they work for any WebSocket app, not just Uptime Kuma. We will explain exactly what each line does in Part 2, but if you just need the fix, copy the snippet, reload Nginx, and your dashboard will come back online.
What Is Uptime Kuma and Why Does It Need a Reverse Proxy?
Uptime Kuma is an open-source, self-hosted monitoring tool built with Node.js and Vue 3. It runs on your own server and periodically checks whether your services are up using HTTP, TCP, Ping, DNS, or Push monitors, then sends alerts via 90+ notification channels when something goes down. It is the de facto replacement for the now-defunct Uptime Robot self-hosted option, and it has more features than most paid SaaS monitoring tools.
You do not strictly need a reverse proxy to run Uptime Kuma. You can bind it to port 80 on a dedicated VPS and call it a day. But a reverse proxy unlocks several real benefits:
HTTPS: a single TLS termination point for multiple services on one host.
Clean URLs: each service gets its own subdomain like
status.example.com,app.example.com,api.example.com.Isolation: the proxy binds to public ports while Uptime Kuma stays on
127.0.0.1, so only the proxy can reach it.Certificate management: Let’s Encrypt via Certbot or a Cloudflare proxy handles renewal automatically.
Future flexibility: you can swap Uptime Kuma for another service behind the same subdomain without touching DNS.
The catch is that almost every reverse proxy needs explicit WebSocket configuration. Regular HTTP is easy, but WebSocket requires an HTTP upgrade handshake that most proxies do not forward by default. Skip that step, and your dashboard becomes a static page that never updates.
Prerequisites and System Requirements
Before we start, here is what you need on hand. I tested this entire setup on a $6/month VPS with 2 GB of RAM and it ran Uptime Kuma plus Nginx comfortably. A Raspberry Pi 4 with 4 GB of RAM works just as well for a homelab setup.
A server: any Linux VPS (Ubuntu 22.04+, Debian 12+, or another modern distro), a Raspberry Pi 4, or a homelab box. Minimum 1 GB of RAM is plenty for Uptime Kuma alone.
Docker and Docker Compose: Uptime Kuma ships as the official
louislam/uptime-kumaimage, so Docker is the easiest install path. Docker 24+ with Compose v2 is ideal.A domain or subdomain: point an A record at your server’s public IP. I use a subdomain like
status.example.comso the root domain stays free for other services and the apex can keep a short TTL.Ports 80 and 443 open: required for HTTPS with Let’s Encrypt. If you are behind a strict firewall with no inbound access, Cloudflare Tunnel is your friend (we cover that later in this article).
15 to 30 minutes: this is not a one-minute deploy, but it is also not a weekend project.
One important note: Uptime Kuma stores its database in a SQLite file inside the container. Mount a local volume to persist data across container restarts and updates. Without a volume, every container restart wipes your monitors, notifications, and status pages. We will set that up next.
You also need a non-root user with sudo access on the host. Running docker compose as root works, but if you already have a docker group, use the standard per-user setup. It keeps audit logs clean and avoids surprises with mounted paths.
Part 1: Deploy Uptime Kuma With Docker Compose
Docker Compose is the cleanest way to run Uptime Kuma because it gives you version control over the configuration. You can commit the compose file to a private Git repo, deploy the same stack to multiple servers, and roll back if a new Uptime Kuma release breaks your setup.
Create a folder on your server to hold the stack:
mkdir -p /opt/uptime-kuma && cd /opt/uptime-kumaNow create a file called docker-compose.yml with the following content. I am using the :2 tag because Uptime Kuma 2.x is the current major version as of 2026. The 2.x line introduced several backend changes (JSON-based backups were removed, the database moved to a new format) compared to the 1.x line, so starting on :2 is the right call for new setups.
version: "3.8"
services:
uptime-kuma:
image: louislam/uptime-kuma:2
container_name: uptime-kuma
restart: unless-stopped
ports:
- "127.0.0.1:3001:3001"
volumes:
- ./data:/app/data
- /var/run/docker.sock:/var/run/docker.sock
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3001"]
interval: 60s
timeout: 10s
retries: 5
Two notes on this compose file that I learned the hard way:
I bind the port to
127.0.0.1instead of0.0.0.0. That way the only thing that can talk to Uptime Kuma directly is the reverse proxy on the same host. It is a small but worthwhile security win because your local SQLite database is not bound to the public IP.Mounting
/var/run/docker.socklets Uptime Kuma monitor other Docker containers on the host. Skip this line if you do not need the Docker monitor type. The Docker socket mount is a well-known security trade-off because it gives the container root-equivalent access to the host, but for a self-hosted monitoring tool this is the standard pattern.
Now bring the stack up and tail the logs:
docker compose up -d
docker compose logs -fAfter about 30 seconds, you should see “Uptime Kuma is ready” in the logs. Test it locally with curl http://127.0.0.1:3001 before moving on. If you get HTML back, the container is healthy and it is time to put a reverse proxy in front of it. If you get “Connection refused,” check the logs again – the most common cause on first run is a port conflict with another service.
Part 2: Configure Nginx Reverse Proxy With WebSocket Headers
Nginx is the most common reverse proxy for Uptime Kuma, so we will start there. This is also the configuration where the WebSocket fix lives, so pay attention. Every line in the location block matters, and I have debugged each one on real production servers.
Install Nginx
On Ubuntu or Debian:
sudo apt update
sudo apt install -y nginx
sudo systemctl enable nginx
sudo systemctl start nginxConfirm Nginx is running with systemctl status nginx. If you see a “Welcome to nginx!” page when you visit your server’s IP, you are ready to add the Uptime Kuma virtual host.
Create the Nginx server block
Create a new file at /etc/nginx/sites-available/uptime-kuma.conf and paste the configuration below. Replace status.example.com with your actual subdomain.
server {
server_name status.example.com;
location / {
proxy_pass http://127.0.0.1:3001;
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;
# WebSocket keepalive - prevents dashboard disconnects
proxy_read_timeout 86400s;
proxy_send_timeout 86400s;
proxy_buffering off;
}
listen 80;
}
Now let me explain why each line matters, because I have debugged every one of these on real servers over the last few years.
The two WebSocket headers that fix everything
proxy_http_version 1.1; forces HTTP/1.1, which is required for WebSocket. Older default HTTP/1.0 connections cannot upgrade to WebSocket because the Upgrade header is a 1.1 feature. If you skip this line, Nginx will happily proxy HTTP traffic but the WebSocket handshake will fail silently.
proxy_set_header Upgrade $http_upgrade; and proxy_set_header Connection "upgrade"; tell Nginx to forward the WebSocket upgrade handshake from the browser to Uptime Kuma. If either is missing, Uptime Kuma falls back to long-polling and eventually gives up, leaving you with the “Cannot connect to the socket server” message that prompts this exact article search.
Trust Proxy and X-Forwarded headers
The three X-Real-IP, X-Forwarded-For, and X-Forwarded-Proto headers are not strictly required for WebSocket, but they matter for Uptime Kuma’s logging and the Trust Proxy setting.
By default, Uptime Kuma sees the proxy’s IP as the client IP. Inside the Uptime Kuma dashboard, go to Settings -> General -> Trust Proxy and enable it. Now Uptime Kuma will trust the X-Forwarded-For header and log the real visitor IP, which is what you want for HTTP monitors reporting location data and for any audit logs you collect.
Tuning proxy_read_timeout
The proxy_read_timeout 86400s; line is the unsung hero. The default Nginx timeout is 60 seconds, which means Nginx closes the WebSocket connection every minute, and you see the dashboard disconnect and reconnect every minute. Bumping it to 86400 seconds (24 hours) means the connection stays open for an entire day of uptime. I learned this the hard way after a weekend of watching my dashboard blink off and on every 60 seconds before I found the timeout setting.
Save the file, enable it, and reload Nginx:
sudo ln -s /etc/nginx/sites-available/uptime-kuma.conf /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginxIf nginx -t reports “syntax is ok” and “test is successful,” you are ready for HTTPS. If you get a “conflicting server name” error, another site-enabled file is already using the same server_name – check for the default default site and either remove the link or use a unique server name.
Part 3: Enable HTTPS With Let’s Encrypt and Certbot
HTTPS is non-negotiable for any web service in 2026. Modern browsers flag HTTP-only sites as “Not Secure,” and Uptime Kuma sends an admin password over the same connection, so plain HTTP is a real risk. Let’s Encrypt gives you free certificates, and Certbot handles renewal automatically.
Install Certbot
sudo apt install -y certbot python3-certbot-nginxObtain the certificate
Certbot’s Nginx plugin will edit your config file, add the SSL block, set up HTTP-to-HTTPS redirect, and reload Nginx. One command does it all:
sudo certbot --nginx -d status.example.comFollow the prompts, agree to the terms, and choose whether to redirect all HTTP traffic to HTTPS (I recommend yes). Certbot will print the paths to your certificate and key, and confirm that auto-renewal is configured. On first run, Certbot may also ask for an email address for renewal failure alerts – provide one, because ignoring renewal alerts has bitten me in the past.
Verify auto-renewal
Test the renewal process with a dry run:
sudo certbot renew --dry-runIf that completes cleanly, you can forget about certificates. Certbot installs a systemd timer or cron job that renews the certificate automatically 30 days before expiry. Your Uptime Kuma instance will keep running with valid HTTPS for years.
Now open https://status.example.com in your browser. You should see the Uptime Kuma setup wizard. Create your admin account, and you have a fully working self-hosted monitoring dashboard. The padlock icon in the address bar confirms that WebSocket is now flowing over a TLS connection.
Reverse Proxy Comparison: Nginx vs Apache vs Caddy vs Traefik vs Cloudflare Tunnel
Not everyone runs Nginx, so here is a side-by-side of every reverse proxy that Uptime Kuma officially supports. The table covers the project maintainer’s own recommendations from the GitHub Wiki, which lists config samples for 8 different proxy types.
| Reverse Proxy | WebSocket Support | HTTPS Setup | Best For | Configuration Complexity |
|---|---|---|---|---|
| Nginx | Manual headers | Certbot or manual | Traditional VPS, homelab | Medium |
| Apache | mod_proxy_wstunnel | Certbot or manual | Legacy cPanel hosts | Medium |
| Caddy | Automatic | Automatic | Simplest setup, auto-HTTPS | Low |
| Traefik | Automatic | Automatic via Let’s Encrypt | Docker Compose stacks | Low (with labels) |
| Cloudflare Tunnel | Automatic | Cloudflare handles TLS | Behind CGNAT, zero open ports | Low |
Nginx – the default
We covered this in Part 2. Nginx is the most documented option and the most common choice. The downside is the manual WebSocket header configuration, which trips up beginners. If you already have Nginx running for other services, adding Uptime Kuma is a copy-paste of the config from Part 2.
Apache – requires ProxyPreserveHost
Apache users hit a different gotcha. You need both mod_proxy and mod_proxy_wstunnel enabled, plus ProxyPreserveHost On in your VirtualHost block. The official Yunohost forum thread on this issue confirms: “If you are using a reverse proxy, the security fix may cause connection issue to the WebSocket server. You should add ProxyPreserveHost on.”
Enable the modules once and reload:
sudo a2enmod proxy proxy_wstunnel proxy_http ssl headers
sudo systemctl restart apache2Then add a VirtualHost for the subdomain:
<VirtualHost *:443>
ServerName status.example.com
ProxyPreserveHost On
ProxyPass / http://127.0.0.1:3001/
ProxyPassReverse / http://127.0.0.1:3001/
SSLEngine on
SSLCertificateFile /etc/letsencrypt/live/status.example.com/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/status.example.com/privkey.pem
</VirtualHost>Without ProxyPreserveHost On, Apache may rewrite the host header in a way that breaks Uptime Kuma’s internal routing, and the WebSocket handshake silently fails.
Caddy – auto HTTPS
Caddy is the easiest option because it does WebSocket and HTTPS for you. A single Caddyfile line is enough:
status.example.com {
reverse_proxy 127.0.0.1:3001
}That is it. Caddy detects the WebSocket upgrade automatically, requests a Let’s Encrypt certificate, and renews it. If you want the simplest possible setup and are okay with a less common proxy, Caddy wins on configuration brevity. Caddy also handles HTTP/3 and QUIC by default, which is a nice bonus for 2026.
Traefik – Docker labels
Traefik is a Docker-native reverse proxy. You configure routing through labels in your docker-compose.yml, no separate config file needed. This is the cleanest approach if you already run Traefik in front of your other Docker services.
services:
uptime-kuma:
image: louislam/uptime-kuma:2
labels:
- "traefik.enable=true"
- "traefik.http.routers.kuma.rule=Host(`status.example.com`)"
- "traefik.http.routers.kuma.entrypoints=websecure"
- "traefik.http.routers.kuma.tls.certresolver=letsencrypt"
- "traefik.http.services.kuma.loadbalancer.server.port=3001"Traefik 3.0+ handles WebSocket upgrades automatically. If you are running an older 2.x version and seeing “bad gateway” errors, upgrade first. Traefik 2.x needs explicit traefik.http.middlewares.kuma.headers.customrequestheaders for older Uptime Kuma releases.
Cloudflare Tunnel – zero open ports
Cloudflare Tunnel is the only option on this list that does not require open ports 80 or 443 on your server. A lightweight daemon (cloudflared) creates an outbound tunnel to Cloudflare’s edge, and traffic flows from Cloudflare to your service. This is the official project recommendation for users behind CGNAT or strict firewalls.
Install cloudflared, log in, create the tunnel, and route it:
cloudflared tunnel login
cloudflared tunnel create uptime-kuma
cloudflared tunnel route dns uptime-kuma status.example.comThen add a config file at ~/.cloudflared/config.yml:
tunnel: <TUNNEL_ID>
credentials-file: /root/.cloudflared/<TUNNEL_ID>.json
ingress:
- hostname: status.example.com
service: http://127.0.0.1:3001
- service: http_status:404
Run cloudflared tunnel run uptime-kuma as a systemd service, and you have a public URL with HTTPS and WebSocket support, no firewall changes required. The tunnel is also more resilient than a direct IP because Cloudflare’s edge absorbs DDoS attempts before they reach your server.
Nginx Proxy Manager Setup for Uptime Kuma
Nginx Proxy Manager (NPM) deserves its own section because it is one of the top related searches for this topic, and yet there is almost no dedicated blog guide for it. NPM is the most popular GUI frontend for managing Nginx reverse proxies, and it handles WebSocket support with a single checkbox.
In the NPM web UI:
Go to Proxy Hosts -> Add Proxy Host.
Set Domain Names to your subdomain, for example
status.example.com.Set Forward Hostname/IP to your Docker host IP (often the same as the NPM host), and Forward Port to
3001.Check the WebSockets Support box on the Details tab. This is the equivalent of adding the
UpgradeandConnectionheaders manually. This is the toggle that most people miss – NPM hides it on the Details tab, not the main form.Add a Let’s Encrypt certificate on the SSL tab. Enable “Force SSL” and “HTTP/2” for the best experience.
That is the entire setup. NPM’s “WebSockets Support” toggle does the same thing as the four-line Nginx config from Part 2. If your dashboard still shows “Cannot connect to the socket server” after enabling it, double-check the toggle was actually saved – a known quirk is that the toggle state can revert if you do not click Save before navigating away. Also verify that Uptime Kuma is reachable from the NPM container with docker exec -it nginx-proxy-manager curl http://<docker-host>:3001.
WebSocket Troubleshooting: Diagnosing ‘Cannot Connect to the Socket Server’
Even with the right config, things sometimes break. Here is how I diagnose WebSocket problems in order of frequency, mapped to the exact error you see in the bottom-left of the Uptime Kuma dashboard. Each symptom has a different fix, and the order below is from most to least common based on Reddit and forum reports.
Symptom: ‘Cannot connect to the socket server. Reconnecting’
This is the canonical WebSocket failure. The HTTP page loads but the real-time dashboard never connects. The page is static, monitor status never updates, and you see a yellow “Reconnecting…” badge.
Fix checklist:
Confirm the Nginx config has
proxy_http_version 1.1;.Confirm both
UpgradeandConnection "upgrade"headers are present.Reload Nginx with
sudo systemctl reload nginx(a full restart viarestartis sometimes needed).Check Nginx error logs:
sudo tail -f /var/log/nginx/error.log.Verify the WebSocket upgrade request reaches Uptime Kuma by checking the browser DevTools Network tab for a 101 Switching Protocols response.
A user on r/selfhosted reported the exact issue and solved it by restarting Nginx: “EDIT: I’m not sure what fixed it, but I restarted the NGINX service and it’s working now!” That fix works because Nginx caches config reloads differently from full restarts. If you have been editing the config and reloading without success, try a hard restart.
Symptom: 502 Bad Gateway
The reverse proxy reaches the server but gets no response from Uptime Kuma. This is almost always a Docker container issue, not a proxy config issue. The browser shows a 502 error page instead of the Uptime Kuma dashboard.
Fix checklist:
Check the container is running:
docker ps. If it is not, start it withdocker compose up -d.Check the container logs:
docker logs uptime-kuma.Confirm the port binding:
docker port uptime-kumashould show3001/tcp -> 127.0.0.1:3001.Test from the host:
curl http://127.0.0.1:3001should return HTML.Check the Uptime Kuma container has disk space: a full disk stops the SQLite write and breaks the server.
Symptom: Dashboard disconnects frequently
If the dashboard connects fine but disconnects every minute or two, the cause is almost always the Nginx timeout. The default proxy_read_timeout is 60 seconds, and Nginx closes idle WebSocket connections after that. The localtonet blog troubleshooting table lists this exact symptom: “WebSocket connection lost / dashboard disconnects frequently – Proxy timeout or missing WebSocket upgrade headers.”
Fix: set proxy_read_timeout 86400s; and proxy_send_timeout 86400s; in the location block. We already covered this in Part 2, but if you skipped it, this is why your dashboard keeps blinking. You can also add proxy_buffering off; to prevent Nginx from buffering the WebSocket frames.
Symptom: Apache user with no headers
Apache users sometimes see the dashboard load but the WebSocket fail. The cause is missing ProxyPreserveHost On. Add it to your VirtualHost block and reload Apache. Also make sure mod_proxy_wstunnel is enabled – it is not loaded by default on most Apache installations.
Symptom: Cloudflare proxy breaks WebSocket
If you use Cloudflare’s orange-cloud proxy (not Cloudflare Tunnel), you must enable WebSockets in the Cloudflare dashboard under Network -> WebSockets. Cloudflare disables WebSocket support by default for the proxy mode. This is a separate setting from Cloudflare Tunnel, which handles WebSocket automatically. Also check that your Cloudflare SSL/TLS encryption mode is “Full” or “Full (Strict)” – a “Flexible” mode will show the certbot cert but Cloudflare will talk HTTP to your server, which can break WebSocket on some configurations.
Advanced Topics: Trust Proxy, Subdirectory, and Push Monitor
Once your basic setup is solid, these three advanced topics will round out the deployment. They are the kind of details that take a working setup to a production-grade one.
Trust Proxy for correct client IP logging
Uptime Kuma records the source IP for HTTP monitors. With a reverse proxy in front, the source IP would otherwise always be 127.0.0.1. To fix this, we already added the X-Forwarded-For header in Part 2. Now open the Uptime Kuma dashboard, go to Settings -> General -> Trust Proxy, and enable it. Uptime Kuma will read the forwarded IP from the header and log the real visitor IP for each check.
This is especially important if you run HTTP monitors with geographic expectations. Without Trust Proxy, every monitor resolves to “local” or the proxy’s IP, which makes location-based dashboards useless.
Why subdirectory is not supported
Uptime Kuma does not work behind a subdirectory path like example.com/uptime/. The project does not natively support a base URL prefix, and the GitHub Wiki confirms this is by design. You must use a subdomain like status.example.com. If you must run multiple services on one domain, use a reverse proxy to route different subdomains to different backends – that is the whole point of running a reverse proxy in the first place.
There is a community workaround using a custom Nginx rewrite, but it breaks on every Uptime Kuma WebSocket reconnect and is not recommended. Save yourself the headache and use a subdomain.
Push monitor for NAT/CGNAT environments
If your Uptime Kuma runs on a server behind NAT or CGNAT, you cannot use HTTP monitors for services on other networks. Instead, use a Push monitor: a small script on the target machine sends a heartbeat to Uptime Kuma every N seconds. If the heartbeat stops, Uptime Kuma marks the monitor as down. This works through any firewall because the connection is outbound.
To set up a Push monitor, create a new monitor in Uptime Kuma, choose “Push” as the type, and Uptime Kuma will give you a unique URL. Add a cron job on the target machine that does curl -s <push-url> every 60 seconds. If the cron stops running (service crashed, network down, host unreachable), Uptime Kuma flags the monitor as down within the heartbeat interval.
Backup, Update, and Maintenance
A Uptime Kuma setup is only as good as its backups. Losing the database means losing every monitor, every notification config, and every status page you have built up.
Backup: the entire state lives in the ./data directory you mounted in the compose file. Stop the container, copy data somewhere safe, and you have a complete backup including database, settings, monitors, and notification configs. For a running backup, you can use sqlite3 ./data/kuma.db ".backup /backup/kuma-backup.db" while the container is running – SQLite’s online backup API is safe even with active writes.
Update: pull the new image and recreate the container with docker compose pull && docker compose up -d. Uptime Kuma 2.x runs the migration automatically on startup. The process can take 20+ minutes on large databases, so do not interrupt it. If you have hundreds of monitors, plan the update for a maintenance window.
Reset admin password: if you get locked out, run docker exec -it uptime-kuma npm run reset-password inside the container. The script will prompt for a new password and update the database directly.
Watch the disk: Uptime Kuma writes a heartbeat record to SQLite every few seconds per monitor. Over months, the database grows. Monitor the data directory size and either prune old heartbeat records or migrate to MariaDB if you have hundreds of monitors.
Frequently Asked Questions
How do I set up Uptime Kuma behind a reverse proxy?
Deploy Uptime Kuma with Docker Compose on a local port like 3001, then put Nginx (or Apache, Caddy, Traefik, or Cloudflare Tunnel) in front. The critical step is adding the Upgrade and Connection upgrade headers so WebSocket traffic passes through. Enable Trust Proxy in Uptime Kuma settings for correct client IP logging.
What WebSocket headers are needed for Uptime Kuma?
You need two headers in your reverse proxy config: Upgrade set to $http_upgrade, and Connection set to upgrade. You also need proxy_http_version 1.1. Without these, the WebSocket handshake fails and the dashboard shows Cannot connect to the socket server.
Why does Uptime Kuma show ‘Cannot connect to the socket server’?
This error means the WebSocket connection between your browser and Uptime Kuma is failing. The most common cause is missing Upgrade and Connection headers in the reverse proxy config. Other causes include a stopped Docker container (502 Bad Gateway instead), default proxy_read_timeout of 60 seconds causing frequent reconnects, or Cloudflare proxy mode with WebSockets disabled.
Can Uptime Kuma run behind a subdirectory?
No. Uptime Kuma does not support running under a path prefix like example.com/uptime/. It must run on its own subdomain such as status.example.com. This is confirmed in the official GitHub Wiki and is by design – the project does not implement base URL prefix routing.
What reverse proxies are compatible with Uptime Kuma?
Uptime Kuma officially supports Nginx, Apache, Caddy, Traefik, HAProxy, Nginx Proxy Manager, and Cloudflare Tunnel. Each requires slightly different config, but all need to pass WebSocket upgrade headers. Cloudflare Tunnel is the easiest option because it handles WebSocket and HTTPS automatically with zero open ports.
Conclusion
Setting up a self-hosted Uptime Kuma behind a reverse proxy with WebSocket fixes comes down to two things: forwarding the Upgrade and Connection headers, and giving the WebSocket connection enough time to stay alive. Get those right, and the rest is straightforward Docker Compose and Certbot work.
Start with the Nginx config in Part 2, verify everything works on HTTP, then add HTTPS with the Certbot command in Part 3. If you hit “Cannot connect to the socket server,” walk through the troubleshooting checklist in the dedicated section. Most issues resolve within five minutes once you know which symptom maps to which cause.
Your next steps: deploy this stack, add a few HTTP monitors for your key services, set up a Telegram or Discord notification, and publish the public status page on a separate subdomain. If you want to monitor machines behind NAT, add a Push monitor with a cron job. That is the complete self-hosted Uptime Kuma setup for 2026 and beyond – the same principles will keep working as new Uptime Kuma versions ship through the rest of the year.