Skip to content

How Do I Secure OpenClaw Gateway Port 18789? (Complete Protection Guide)

I wanted to access my OpenClaw Gateway from my laptop while sitting at a coffee shop. Simple enough, right? Just forward port 18789 through my router and connect remotely.

Wrong. That decision would have given anyone on the internet full access to all my agents, sessions, and workspace files.

Let me show you what I learned about securing OpenClaw Gateway’s WebSocket API, and why the “easy” solution is actually the most dangerous one.

The Problem: Gateway Is the Crown Jewel

When I first started with OpenClaw, I treated it like any other service running on my machine. I figured, “It’s just a local tool, what’s the risk?”

Then I read the documentation more carefully:

Gateway is the core. Every message in, every response out, every tool call flows through it.

That’s when it clicked. Gateway isn’t just another component—it’s the central hub that controls everything:

┌─────────────────────────────────────────────────────────┐
│ OpenClaw System │
│ │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ Agent 1 │ │ Agent 2 │ │ Agent 3 │ │
│ └────┬────┘ └────┬────┘ └────┬────┘ │
│ │ │ │ │
│ └──────────────┼──────────────┘ │
│ │ │
│ ┌──────▼──────┐ │
│ │ GATEWAY │◄── Port 18789 │
│ │ (WebSocket)│ WebSocket API │
│ └──────┬──────┘ │
│ │ │
│ ┌──────────────┼──────────────┐ │
│ │ │ │ │
│ ┌────▼────┐ ┌────▼────┐ ┌────▼────┐ │
│ │Sessions │ │Workspace│ │ Tools │ │
│ │ Files │ │ Files │ │ │ │
│ └─────────┘ └─────────┘ └─────────┘ │
│ │
└─────────────────────────────────────────────────────────┘

Gateway runs a WebSocket API on port 18789. This API lets you connect your own interface or external integrations. But here’s the scary part: anyone with access to port 18789 has full control over your entire OpenClaw system.

My First (Terrible) Idea: Port Forwarding

I’ll be honest—my first instinct was to just forward the port through my router:

Terminal window
# What I THOUGHT about doing (DON'T DO THIS)
# Router config: Forward external port 18789 → internal IP:18789

Then I could connect from anywhere:

// What I imagined would work
const ws = new WebSocket('ws://my-home-ip:18789');

This is exactly what the OpenClaw documentation warns against as one of the “Five Mistakes”:

Port 18789 exposed to the internet. Full access to all agents, sessions, and workspace files for anyone who finds it.

Think about what that means:

  • All your agent conversations—exposed
  • All your session data—exposed
  • All your workspace files—exposed
  • Anyone who finds your port can control your agents

A simple port scanner would find it within hours. Maybe minutes.

The Right Way: Two Secure Options

OpenClaw’s documentation is clear about the solution:

By default Gateway only listens on localhost. For remote access: VPN via Tailscale or an SSH tunnel. Exposing 18789 to the open internet means full access to all your data, sessions, and agents.

Let me walk you through both approaches.

Option 1: Tailscale VPN (Zero Configuration)

Tailscale creates a secure mesh network between your devices. It’s like having a private internet just for your machines.

Step 1: Install Tailscale

On your server (where OpenClaw Gateway runs):

install-tailscale.sh
# Ubuntu/Debian
curl -fsSL https://tailscale.com/install.sh | sh
# Start and authenticate
sudo tailscale up

On your laptop:

Terminal window
# Same installation process
curl -fsSL https://tailscale.com/install.sh | sh
sudo tailscale up

Step 2: Get Your Tailscale IP

get-tailscale-ip.sh
tailscale ip
# Output: 100.x.y.z (your Tailscale IP)

Step 3: Configure Gateway to Listen on Tailscale Interface

By default, Gateway only listens on localhost. You need to bind it to your Tailscale IP:

gateway-config.yaml
# In your OpenClaw Gateway configuration
server:
host: "100.x.y.z" # Your Tailscale IP
port: 18789

Or set it via environment variable:

set-gateway-host.sh
export OPENCLAW_GATEWAY_HOST="100.x.y.z"

Step 4: Connect from Your Laptop

Now, from your laptop (which is also on Tailscale):

connect-via-tailscale.js
// Use your server's Tailscale IP
const ws = new WebSocket('ws://100.x.y.z:18789');
ws.onopen = () => {
console.log('Connected securely via Tailscale!');
};

Why This Works

┌──────────────────┐ ┌──────────────────┐
│ Your Laptop │ │ Your Server │
│ │ │ │
│ ┌────────────┐ │ Tailscale │ ┌────────────┐ │
│ │ Client │ │ Mesh VPN │ │ Gateway │ │
│ │ │◄─┼────────────────────┼─►│ :18789 │ │
│ └────────────┘ │ Encrypted │ └────────────┘ │
│ │ Private │ │
│ Tailscale IP: │ Network │ Tailscale IP: │
│ 100.a.b.c │ │ 100.x.y.z │
└──────────────────┘ └──────────────────┘
│ │
│ │
▼ ▼
Internet (blocked) Internet (blocked)
Port 18789 NOT exposed Port 18789 NOT exposed

The beauty of Tailscale:

  • No port forwarding needed
  • No firewall configuration
  • End-to-end encryption
  • Works behind NAT
  • Zero configuration after initial setup

Option 2: SSH Tunnel (More Control)

If you prefer more control or can’t use Tailscale, SSH tunneling is the classic approach.

Step 1: Create the Tunnel

From your laptop:

create-ssh-tunnel.sh
# Basic tunnel
ssh -L 18789:localhost:18789 user@your-server-ip -N
# Or with key file
ssh -L 18789:localhost:18789 -i ~/.ssh/your-key user@your-server-ip -N
# Or in background
ssh -L 18789:localhost:18789 user@your-server-ip -N -f

The -L flag creates local port forwarding:

  • 18789 = local port on your laptop
  • localhost:18789 = destination from the server’s perspective
  • -N = don’t execute remote command (just forwarding)
  • -f = run in background

Step 2: Connect Through the Tunnel

Now connect to localhost on your laptop:

connect-via-tunnel.js
// The tunnel forwards localhost:18789 to server:18789
const ws = new WebSocket('ws://localhost:18789');
ws.onopen = () => {
console.log('Connected through SSH tunnel!');
};

How SSH Tunneling Works

┌──────────────────┐ ┌──────────────────┐
│ Your Laptop │ │ Your Server │
│ │ │ │
│ ┌────────────┐ │ SSH Tunnel │ ┌────────────┐ │
│ │ Client │ │ (Encrypted) │ │ Gateway │ │
│ │ localhost: │──┼────────────────────┼──│ :18789 │ │
│ │ 18789 │ │ │ └────────────┘ │
│ └────────────┘ │ │ │
│ │ │ Port 22 only │ ▲ │
│ │ │ exposed │ │ │
│ ▼ │ │ localhost:18789 │
│ Local forward │ │ (not exposed) │
└──────────────────┘ └──────────────────┘

Benefits of SSH tunneling:

  • Uses existing SSH infrastructure
  • Strong encryption
  • Fine-grained access control via SSH keys
  • Can tunnel multiple ports

Comparison: Which Should You Choose?

AspectTailscaleSSH Tunnel
SetupVery easyModerate
PersistenceAutomaticManual (or use autossh)
Multiple devicesBuilt-inOne tunnel per connection
Behind NATWorksRequires SSH server accessible
Access controlTailscale adminSSH key management
CostFree for personalFree

I use Tailscale for day-to-day work because it’s seamless. SSH tunnels are my backup when I’m on a network that blocks VPN traffic.

Common Mistakes to Avoid

Mistake 1: Binding to 0.0.0.0

WRONG-gateway-config.yaml
# DON'T DO THIS
server:
host: "0.0.0.0" # Listens on ALL interfaces
port: 18789

This exposes Gateway on every network interface, including your public IP. Even with a firewall, it’s risky.

Mistake 2: Relying on “Security Through Obscurity”

WRONG-obscurity.sh
# DON'T DO THIS
# "I'll use a non-standard port, no one will find it"
ssh -L 58789:localhost:18789 user@server

Port scanners find non-standard ports in minutes. This is not security.

Mistake 3: No Firewall Rules

Even with Tailscale or SSH, add firewall rules as defense in depth:

firewall-rules.sh
# Allow Tailscale traffic only
sudo ufw allow in on tailscale0 to any port 18789
# Or for SSH, only allow from specific IPs
sudo ufw allow from your-ip to any port 22
# Deny everything else
sudo ufw default deny incoming
sudo ufw enable

Testing Your Setup

Test 1: Verify Gateway Is Not Exposed

From an external network (use your phone’s mobile data):

test-exposure.sh
# Should FAIL (connection refused/timeout)
nc -zv your-public-ip 18789
# Or
telnet your-public-ip 18789

If this connects, you’ve exposed the port. Fix it immediately.

Test 2: Verify Secure Access Works

From your laptop (on Tailscale or with SSH tunnel active):

test-secure-access.sh
# Should succeed
nc -zv localhost 18789
# Or for Tailscale
nc -zv 100.x.y.z 18789

Test 3: WebSocket Connection Test

test-websocket.js
const WebSocket = require('ws');
const ws = new WebSocket('ws://localhost:18789'); // or Tailscale IP
ws.on('open', () => {
console.log('✓ Connection successful');
ws.close();
});
ws.on('error', (err) => {
console.error('✗ Connection failed:', err.message);
});

What I Do Now

My current setup:

  1. Tailscale installed on all devices that need OpenClaw access
  2. Gateway bound to Tailscale IP only (not 0.0.0.0)
  3. Firewall rules as backup protection
  4. SSH tunnel as fallback when Tailscale is blocked

This gives me secure remote access without exposing port 18789 to the internet.

Quick Reference

quick-reference.sh
# Tailscale approach
tailscale up # Connect to mesh
tailscale ip # Get your IP
export OPENCLAW_GATEWAY_HOST="100.x.y.z" # Configure Gateway
# SSH tunnel approach
ssh -L 18789:localhost:18789 user@server -N -f # Create tunnel
# Then connect to localhost:18789
# Verify not exposed
nc -zv your-public-ip 18789 # Should fail

The Bottom Line

Gateway is the core of your OpenClaw system. Every message, every response, every tool call flows through it. Port 18789 gives complete control over all your agents, sessions, and workspace files.

Never expose it directly to the internet.

Use Tailscale for zero-configuration security, or SSH tunnels for more control. Both keep your Gateway accessible while preventing unauthorized access.

The five minutes it takes to set up proper security is nothing compared to the risk of exposing your entire OpenClaw system to the world.


Final Words + More Resources

My intention with this article was to help others share my knowledge and experience. If you want to contact me, you can contact by email: Email me

Here are also the most important links from this article along with some further resources that will help you in this scope:

Oh, and if you found these resources useful, don’t forget to support me by starring the repo on GitHub!

Comments