# jpillora/chisel: a TCP and UDP tunnel over HTTP, secured with SSH

> Chisel ships a server and a client in one Go binary, carries TCP and UDP inside HTTP, and authenticates with SSH keys or a users.json file. It is a practical way through a firewall, not a general-purpose VPN.

**jpillora/chisel** — A fast TCP/UDP tunnel over HTTP

- Repository: https://github.com/jpillora/chisel
- Stars: 16,603 · Forks: 1,610
- Language: Go
- License: MIT
- Published: 2026-09-21 · Updated: 2026-09-21 · Language: en
- Canonical page: https://hysenlabs.com/projects/jpillora-chisel

## The firewall problem chisel is built to solve

Most networks let HTTP out and little else. A service you need to reach sits behind that boundary, and you cannot open a port or install a VPN concentrator. Chisel takes a TCP or UDP stream, wraps it in HTTP, and carries it to a server you control on the other side. The README frames the use case directly: "mainly useful for passing through firewalls, though it can also be used to provide a secure endpoint into your network."

The audience is narrow and specific. It is for engineers who can run a binary on both ends and who are comfortable with SSH key fingerprints. It is not aimed at end users who want a click-to-connect client, and it is not a mesh product. One server process and one client process is the whole topology, which is the point: there is nothing to schedule, no control plane, and no daemon beyond the two processes you start yourself.

## How chisel moves bytes: HTTP transport, SSH security

The transport is HTTP, and the security layer is SSH. That combination is the design decision everything else follows from. The repository's go.mod lists golang.org/x/crypto, which is where crypto/ssh lives, and the README states connections are "Encrypted connections using the SSH protocol (via crypto/ssh)." The server generates an ECDSA key pair, and the client can pin the resulting fingerprint so a man-in-the-middle on the HTTP path is detectable.

A single client TCP connection can carry multiple tunnel endpoints, so you do not pay a new connection per forwarded port. Remote addresses are specified as pairs: the client asks the server to expose a port, or the server asks the client to, which is the reverse port forwarding mode. The README also describes a server that optionally doubles as a reverse proxy and can optionally accept SOCKS5 connections. Authentication has two independent mechanisms: a users.json authfile on the server side, and fingerprint matching for the server identity on the client side. Those solve different problems and the README treats them as separate features.

Reconnection is handled by the client with exponential backoff, tunable via --min-retry-interval and --max-retry-interval, and keepalive pings that time out. The README says this catches "silently dead connections (sleep/wake, NAT timeouts, server restarts)." That detail matters more than it looks: a tunnel that stays open but carries nothing is worse than one that drops, because nothing upstream notices.

## Installing chisel and running a first tunnel

The project offers four install paths. Prebuilt binaries come from the releases page, or the install script. Docker images are multi-arch and published to both Docker Hub and GitHub Container Registry. Fedora carries a community-maintained package. And the Go toolchain can build it directly.

```bash
curl https://i.jpillora.com/chisel! | bash
```

That script downloads and installs a release binary. If you prefer to build from source, the README gives this command:

```bash
go install github.com/jpillora/chisel@latest
```

Either way you end up with one executable that contains both modes. Running it with no arguments prints the command list, which shows server and client as the two subcommands.

To start a server, give it a port and, optionally, a backend to proxy ordinary web requests to:

```bash
chisel server --port 8080 --backend http://example.com
```

With that running, a client connects to the server URL and requests a tunnel. The README's demo uses a single numeric argument, which is the remote port the server should listen on:

```bash
chisel client https://<your-app>.fly.dev 3000
```

The README describes the result: the client "tunnels your localhost:3000 to the server's localhost:3000." If you visit the server's URL in a browser instead of using the tunnel, you hit the backend proxy and see a copy of example.com. That is the quickest way to confirm the server is actually up before you debug the tunnel.

For anything beyond a demo you want authentication. The server takes an --authfile pointing at a users.json shaped like this:

```json
{
  "<user:pass>": ["<addr-regex>", "<addr-regex>"]
}
```

When a user connects, the password is verified and each remote address is matched against that user's list of regular expressions.

## Where chisel gets awkward

The authfile pattern matching is the sharpest edge in the documentation. The README states plainly that patterns are not anchored by default, and gives the example that "10.0.0.1:80" also matches "210.0.0.1:8080", with "." matching any character. A rule written casually will authorize more than the author intended. The README's own remedy is to anchor your patterns, and it is worth treating as mandatory rather than optional.

Key management has a similar trap. The --key flag is deprecated in favor of --keygen and --keyfile, and the README notes that without --key or --keyfile, a new key is generated on each run. A new fingerprint on every restart means fingerprint pinning on the client either fails or gets disabled out of frustration, which quietly removes the man-in-the-middle protection the feature exists to provide. The --keyfile flag also accepts the inline key string itself, a base64 value with a "ck-" prefix, so the key can live in an environment variable rather than on disk. That is convenient and also means the key belongs in whatever secret store you already use.

Platform support is a real boundary. Binaries are built with the latest Go release, which sets minimum versions of Windows 10 or Server 2016, macOS 12, Linux kernel 3.2 and FreeBSD 12.2. The README directs older systems, Windows 7 named among them, to release v1.8.1 or earlier. Anyone maintaining a long-lived embedded or legacy host should confirm this before planning a deployment.

Finally, chisel is the wrong tool for routed multi-host networking. There is no virtual interface, no subnet advertisement, and no peer-to-peer topology. If you need a host to reach an entire remote network as though it were local, a tunnel that forwards individual ports will not get you there.

## Chisel against a full VPN and against plain SSH forwarding

The closest alternative for many teams is a full VPN such as WireGuard or OpenVPN. The difference is architectural, not a matter of tuning. A VPN creates a network interface and routes IP packets, so every host and port on the far side is reachable once the tunnel is up. Chisel forwards specific endpoints. You name the ports you want, and only those exist. That is a smaller attack surface and a simpler mental model, and it also means chisel cannot answer a question like "reach the whole 10.0.0.0/24 subnet."

Plain SSH port forwarding is the other comparison, and it is closer than it first appears. Chisel is secured by the SSH protocol via crypto/ssh and can carry SSH itself over stdio, which the README says supports ssh -o ProxyCommand and provides SSH over HTTP. So the choice is not SSH versus not-SSH. The difference is that chisel's transport is HTTP, which is what gets it through proxies and firewalls that permit web traffic but block SSH, and that it reconnects on its own with exponential backoff and keepalive detection. A hand-rolled ssh -L tunnel has neither of those properties by default. If your network already permits SSH outbound, plain forwarding plus autossh covers similar ground with tools you already have.

## Maintenance, release cadence and the MIT licence

The repository is not archived and the last push was on 2026-09-01. The most recent release is v1.12.0, dated 2026-08-29, preceded by two release candidates in July and August 2026. That is a project publishing tagged releases rather than a dormant repository, and the release candidate sequence suggests changes are staged before they are stamped final.

The build is ordinary Go. The Makefile defines targets for linux, darwin, windows and freebsd, all with CGO_ENABLED=0, plus lint and test targets that run go vet and go test -race. Cross-compilation is therefore a single command per platform, and releases are produced with goreleaser. For a team that wants to build its own artifact rather than trust a download, that path is short.

Upgrade cost is mostly about the key and fingerprint story. If you rely on fingerprint pinning, an upgrade that changes server key handling affects every client. The --key flag's deprecation is the clearest example: configurations still using it should move to --keygen and --keyfile. The licence is MIT, which permits commercial and closed-source use with the usual requirement to keep the copyright notice; that is a general description of the licence text and not legal advice, so have counsel review it if the distinction matters to you.

## Conclusion

Adopt chisel when you need one binary to punch a TCP or UDP path through an HTTP-only firewall and you can manage SSH keys or an authfile. Skip it when you need a routed, multi-host VPN or a long-term unattended deployment on an old OS, since binaries require Windows 10, macOS 12 or Linux kernel 3.2 and older systems need release v1.8.1 or earlier. Before rolling it out, check the fingerprint printed by chisel server against the one the client pins, and confirm your address patterns in authfile are anchored, because the README warns that patterns are not anchored by default and "10.0.0.1:80" also matches "210.0.0.1:8080".

## FAQ

### What is jpillora/chisel used for?

It tunnels TCP and UDP traffic over HTTP, secured with the SSH protocol, and is mainly used to pass through firewalls or to provide a secure endpoint into a network. A single executable contains both the server and the client.

### Does chisel use SSH?

Yes. The README lists encrypted connections using the SSH protocol via crypto/ssh, and the go.mod depends on golang.org/x/crypto. Client connections can also run over stdio, which supports ssh -o ProxyCommand.

### How do I install chisel?

You can install a prebuilt binary with the project's install script, use the multi-arch Docker images on Docker Hub or ghcr.io, install the Fedora package with dnf, or build from source with go install github.com/jpillora/chisel@latest.

### How do I use chisel to open a tunnel?

Start the server with chisel server --port 8080, then run chisel client against the server URL with the remote port you want, for example chisel client https://<your-app>.fly.dev 3000. The README says that tunnels your localhost:3000 to the server's localhost:3000.

## Sources

- [Issues](https://github.com/jpillora/chisel/issues)
- [jpillora/chisel on GitHub](https://github.com/jpillora/chisel)
- [License: MIT](https://github.com/jpillora/chisel/blob/master/LICENSE)
- [README](https://github.com/jpillora/chisel/blob/master/README.md)
- [Releases](https://github.com/jpillora/chisel/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/jpillora-chisel
