caddy proxy same domain to different ports for websocket and http rpc

An Ethereum geth node usually have 2 interfaces:

  • http: port 8545
  • websocket: port 8546

Most of RPC service allow you to simply replace https with wss to use the websocket interface. In this post I will show you how to:

  • Setup authentication with a key in URL for your RPC
  • Share the same domains for both http and websocket

Assume that you have geth run on the server, and have enable http and ws API on above port. We will proceed as follow

Install Caddy

sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https curl
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | sudo tee /etc/apt/sources.list.d/caddy-stable.list
sudo apt update
sudo apt install caddy

Check https://caddyserver.com/docs/install#debian-ubuntu-raspbian for more detail installation.

Configure caddy

Edit your /etc/caddy/Caddyfile:

rpc.example.com {
  @auth {
    not query apikey=<a-secret-key-here>
  }

  @websockets {
    header Connection *Upgrade*
    header Upgrade    websocket
  }
  respond @auth "Unauthorized" 401

  reverse_proxy @websockets {
    header_up Host localhost
    to 127.0.0.1:8546
  }

  reverse_proxy / {
    header_up Host localhost
    to 127.0.0.1:8545
  }
}

The secret key can be generate with something like:

openssl rand -hex 36

Restart caddy

systemctl restart caddy

Now try curl rpc.example.com and wscat -c rpc.example.com you will get 401.

But if we do

curl "https://rpc.example.com?apikey=<key>"

# or wscat
wscat -c "wss://rpc.example.com?apikey=<key>"

It will succesfully establish connection