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
- Open
https://smee.io/newin your browser to redirect to a new channel URL likehttps://smee.io/<channel id>. - Paste that channel URL into GitHub as your webhook payload URL.
- Install
smee-clienton Node.js^20.18or>=22, then runsmeewith--url,--path, and--portin its own terminal so it printsForwarding WEBHOOK_PROXY_URL to http://127.0.0.1:PORT/PATH. - Trigger a webhook or redeliver a previous delivery in GitHub and watch
smeelog each forwardedPOSTwith 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
Authorizationheader 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 aPOSTto an empty channel returned200and 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.
| Tool | Keeps deliveries | Security | Limits | Gets events to localhost |
|---|---|---|---|---|
| smee.io | No, browser localStorage only | None, the channel ID is enough | None in its docs | Yes, smee-client |
| webhooks.sh free bin | Latest 10, for 48 hours | Secret view URL, sensitive headers redacted | 100 requests a day, 100KB bodies | No |
| webhooks.sh Solo | 90 days of history | GitHub, Stripe and Shopify signature checks | $12 a month, 250,000 requests | Yes, wsh listen |
| ngrok Free | Traffic Inspector, 24 hours | Account required, optional OAuth | 20,000 requests and 1 GB a month | Yes, tunnel |
| Cloudflare quick tunnel | No | None by default | 200 in-flight requests | Yes, 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.