smee.io and smee-client: forward GitHub webhooks to localhost, caveats and alternatives

smee.io is a free webhook proxy by Probot: GitHub posts to a smee.io channel URL and the smee-client CLI replays each event to localhost. Channels have no authentication and nothing is stored on the server; in our October 29, 2026 test, an event sent while no client was connected was lost.

smee.io is a free webhook proxy: GitHub or any other sender posts to a smee.io channel, and the smee-client CLI replays each event to your local server, which is the setup GitHub's own docs use. Two caveats matter before you rely on it: channels have no authentication, and nothing is stored on the server. Below we walk through the setup, the caveats we saw in our live test, running your own server, and the smee alternatives.

How smee.io and smee-client work

  1. Open https://smee.io/new in your browser to redirect to a new channel URL like https://smee.io/<channel id>.
  2. Paste that channel URL into GitHub as your webhook payload URL.
  3. Install smee-client on Node.js ^20.18 or >=22, then run smee with --url, --path, and --port in its own terminal so it prints Forwarding WEBHOOK_PROXY_URL to http://127.0.0.1:PORT/PATH.
  4. Trigger a webhook or redeliver a previous delivery in GitHub and watch smee log each forwarded POST with your local server's status code.

The client keeps a Server-Sent Events stream open to the channel and re-sends each incoming event as an HTTP POST to your local server. If you prefer passing one full local URL instead of --port and --path, pass -t or --target (default http://127.0.0.1:PORT/PATH), and for automated tests the Node.js API takes new SmeeClient({ source, target }) with start() and close().

# 1. Get a channel URL (see the Location header)
curl -sI https://smee.io/new | grep -i location

# 2. Install and run the client
npm install --global smee-client
smee --url https://smee.io/<channel> \
  --path /webhook --port 3000

Caveats: no auth, no storage, lost events

  • The smee.io server README states it is meant for development and not for production.
  • Channels are not authenticated, so anyone who gets your channel ID can see your payloads and headers (in our test, an Authorization header we sent came through to the listener as-is).
  • Nothing is stored on the server or in a database, and the channel web page only keeps events in your browser's localStorage.
  • Events sent while no client is connected are lost even though the sender gets an HTTP 200, which we confirmed on October 29, 2026 when a POST to an empty channel returned 200 and a client connecting 2 seconds later never received it.

Because GitHub thinks the delivery succeeded, you have to go into GitHub and redeliver the past webhook delivery after you reconnect.

Run your own smee server

If sending webhooks through the public server is a problem, the server is open source at github.com/probot/smee.io and runs from the ghcr.io/probot/smee.io Docker image. You start the container, point smee --url at your own instance, and set REDIS_URL if you run more than one instance. Channels still have no authentication on a self-hosted server, so anyone who can reach your instance and knows a channel ID can read that channel's stream.

docker run -p 3000:3000 ghcr.io/probot/smee.io

# Point the client at your own server
smee --url https://smee.example.com/<channel> \
  --target http://127.0.0.1:8080/webhook

smee.io alternatives

The right replacement depends on whether you need a relay, a direct tunnel, or a stored record of deliveries.

  • Tunnels like ngrok and cloudflared send requests straight to your local app so the sender gets your app's real HTTP response, though deliveries fail whenever your laptop is asleep or the tunnel is closed.
  • A free webhooks.sh bin captures and displays deliveries in the browser with nothing running on your machine.
  • webhooks.sh Solo gives you a persistent endpoint with history plus a local relay CLI.
ToolKeeps deliveriesSecurityLimitsGets events to localhost
smee.ioNo, browser localStorage onlyNone, the channel ID is enoughNone in its docsYes, smee-client
webhooks.sh free binLatest 10, for 48 hoursSecret view URL, sensitive headers redacted100 requests a day, 100KB bodiesNo
webhooks.sh Solo90 days of historyGitHub, Stripe and Shopify signature checks$12 a month, 250,000 requestsYes, wsh listen
ngrok FreeTraffic Inspector, 24 hoursAccount required, optional OAuth20,000 requests and 1 GB a monthYes, tunnel
Cloudflare quick tunnelNoNone by default200 in-flight requestsYes, tunnel

Replace smee with the wsh CLI

If you want the smee workflow where a remote URL catches everything and a CLI forwards events to localhost, webhooks.sh Solo ($12 a month, cancel any time) gives you a named endpoint on webhooks.host that keeps 90 days of history and up to 250,000 requests per billing period. You run wsh listen <endpoint> --to localhost:3000 to relay live deliveries to your server, and if your terminal wasn't running when an event arrived, you can replay the captured request to localhost from the stored history instead of triggering it again in GitHub.

Solo endpoints can also verify signatures for GitHub webhooks, and headers like X-Hub-Signature-256 stay visible in the viewer while headers containing authorization, cookie, key, token, or secret are redacted. If you just want to inspect payloads before paying for anything, create a free bin with no signup: it lasts 48 hours and takes up to 100 requests a day, and you can claim it with a free Google sign-in to keep the same URL with 30 days of rolling history.

# Free bin to see GitHub's payloads first
curl -X POST https://webhooks.sh/v1/bins

# Solo: relay deliveries to your local app
wsh listen <endpoint> --to localhost:3000

FAQ

What is smee.io?

smee.io is a free, open-source webhook payload delivery service from the Probot project meant for development only. You point a webhook sender at a smee.io channel URL, and smee-client receives the events over Server-Sent Events and posts them to localhost.

How do I use smee-client with GitHub webhooks?

Open https://smee.io/new to get a channel URL and paste it as the webhook payload URL in GitHub. Then run npm install --global smee-client and start the relay with smee --url <channel> --path /webhook --port 3000.

Is smee.io secure?

No, smee.io channels have no authentication, which means anyone who knows your channel ID can watch the payloads and headers arrive. It's fine for non-sensitive test events, but don't send production data or secrets through it.

Why is smee not forwarding my webhooks?

Make sure smee is still running in its own terminal with the right --path and --port flags, and check whether the event shows up on the channel web page. If the webhook fired while smee wasn't connected, that event is gone and you'll need to redeliver it from GitHub.

Does smee.io store webhook payloads?

No, the smee.io server does not store payloads in a database; the channel page only saves events in your browser's localStorage while it's open. In our October 29, 2026 test, a webhook sent while no client was connected returned HTTP 200 to the sender and never arrived when a listener connected 2 seconds later.

What is a good alternative to smee.io?

For a localhost relay that keeps 90 days of history and lets you replay missed deliveries, use webhooks.sh Solo ($12 a month) with wsh listen. To inspect payloads in the browser without installing anything, open a free webhooks.sh bin (no signup, 48 hours, 100 requests a day). Use ngrok or a Cloudflare quick tunnel (cloudflared tunnel --url http://localhost:3000) when the sender needs to reach your local server directly.

Last updated

Try it in 10 seconds.

Create a free bin at view.webhooks.page — no signup, no install. Point your sender at the URL and watch requests land live. Bins expire after 48 hours.