OpenClaw is an agentic AI framework — basically it lets you run and orchestrate AI agents on your own infrastructure. I have been running it as a Docker container on Unraid and it works pretty well. As with most things I self host, I wanted to get it behind an Apache2 reverse proxy with a proper domain and SSL certificate.
Getting the basic proxy working wasn’t too bad, but the GUI kept showing a broken WebSocket connection — specifically it was trying to connect to wss:// and failing. The problem is that OpenClaw’s gateway is fundamentally WebSocket based, and a standard ProxyPass setup just silently drops the WebSocket upgrade handshake and the whole thing falls apart.
The fix is to use mod_proxy_wstunnel and mod_rewrite together to detect the Upgrade: websocket header and route those connections differently to normal HTTP traffic. You also need to tell OpenClaw itself to trust your proxy’s IP in its gateway config, otherwise it will throw 1008 unauthorised errors on the WebSocket connections.
First make sure you have the required modules enabled:
a2enmod proxy proxy_http proxy_wstunnel headers rewrite
systemctl restart apache2
Then the Apache config:
<IfModule mod_ssl.c>
<VirtualHost *:443>
ServerName your.domain.com
ServerAlias www.your.domain.com
ServerAdmin admin@your.domain.com
ProxyPreserveHost On
ProxyRequests Off
Header always set Strict-Transport-Security "max-age=63072000; includeSubDomains"
Header always set Referrer-Policy "no-referrer"
RequestHeader set X-Real-IP %{REMOTE_ADDR}s
RequestHeader append X-Forwarded-For %{REMOTE_ADDR}s
RequestHeader set X-Forwarded-Proto "https"
# WebSocket + HTTP routing
RewriteEngine On
RewriteCond %{HTTP:Upgrade} websocket [NC]
RewriteRule /(.*) ws://xx.xx.xx.xx:18789/$1 [P,L]
RewriteCond %{HTTP:Upgrade} !=websocket [NC]
RewriteRule /(.*) http://xx.xx.xx.xx:18789/$1 [P,L]
ErrorLog ${APACHE_LOG_DIR}/openclaw_error.log
CustomLog ${APACHE_LOG_DIR}/openclaw_access.log combined
Include /etc/letsencrypt/options-ssl-apache.conf
SSLCertificateFile /etc/letsencrypt/live/your.domain.com/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/your.domain.com/privkey.pem
</VirtualHost>
</IfModule>
<VirtualHost *:80>
ServerName your.domain.com
Redirect permanent / https://your.domain.com/
</VirtualHost>
Replace xx.xx.xx.xx with the IP of your OpenClaw Docker container (or the Unraid host IP if you’re using port mapping), and swap in your actual domain.
The other thing you need to do is configure OpenClaw’s gateway to trust your proxy IP, and set the allowed origin for the Control UI. You can do this via the OpenClaw config or CLI:
openclaw config set gateway.trustedProxies '["your.proxy.ip"]'
openclaw config set gateway.controlui.allowedOrigins '["https://your.domain.com"]'
openclaw gateway restart
Once that’s all in place it should connect cleanly with no WebSocket errors. Remove the Let’s Encrypt section at the bottom and let Certbot add that for you if you’re setting it up fresh.