Skip to content

comparisons

tmux in a browser: three ways that actually work

ttyd or wetty behind your own reverse proxy, tmate's web link, or a purpose-built client — the real setup, trade-offs, and an actual ttyd + nginx recipe.

The mtmux team6 min read

There are three honest ways to reach a tmux session from a browser: wrap it in ttyd or wetty and put your own reverse proxy in front, use tmate's built-in web link for a quick read-only share, or run a client purpose-built for it. All three work. They differ in who runs the server, what's listening on a port, and whether the result is usable on a phone — not in whether tmux itself shows up on screen.

Option 1: ttyd or wetty behind your own proxy

ttyd and wetty do the same job: wrap a terminal command in a WebSocket and serve xterm.js to render it. Neither is tmux-specific — point either at tmux attach and the browser gets exactly what your terminal would show. Both are free, open source, and small enough to read in an afternoon.

The catch is in the name: you run the server. That means a reverse proxy, TLS, and authentication are your responsibility, not the tool's. Here's the actual recipe, using ttyd:

install and run ttyd, bound to localhost only
# macOS
brew install ttyd
# Debian/Ubuntu
sudo apt install ttyd
 
# wrap tmux, don't wrap a bare shell — this attaches to (or creates) a session named "main"
ttyd -p 7681 -i 127.0.0.1 tmux new-session -A -s main

Binding to 127.0.0.1 matters: it keeps ttyd unreachable from the network directly, so the only path in is through nginx, where auth and TLS actually live.

/etc/nginx/sites-available/tmux
server {
    listen 443 ssl;
    server_name term.example.com;
 
    ssl_certificate     /etc/letsencrypt/live/term.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/term.example.com/privkey.pem;
 
    auth_basic           "tmux";
    auth_basic_user_file /etc/nginx/.htpasswd;
 
    location / {
        proxy_pass http://127.0.0.1:7681;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $host;
    }
}
create the basic-auth password file
sudo htpasswd -c /etc/nginx/.htpasswd yourname
sudo certbot --nginx -d term.example.com
sudo nginx -t && sudo systemctl reload nginx

That's the whole thing: a session reachable at https://term.example.com, password-gated, over TLS. It's genuinely not much machinery, and it's machinery you fully own — no third-party relay sees your terminal at any point.

An open inbound port is now part of your threat model

This setup requires a port open to the internet (443, via nginx) and a DNS record pointing at your machine. That's the trade: full control, in exchange for being the one who patches nginx, rotates the htpasswd file, and notices if certbot's renewal silently fails.

tmate forks a second, purpose-built tmux server and hands out both an SSH command and a read-only web URL for the same session — tmate show-messages prints both. No reverse proxy, no port to open on your machine, nothing to patch: the web view is served from tmate's own infrastructure (or your own, if you self-host the relay).

tmate — start a session and get a web link
$ tmate
tmate identifies remote session as 3f8a2c1e...
ssh session read only: ssh [email protected]
web session read only: https://tmate.io/t/ro-3f8a2c1e

The web link is genuinely the fastest path from "I have a terminal" to "someone else can see it in a browser" of any option here — no install on the viewer's end, no account. It's also read-only by default, tied to that one ad-hoc session, and gone the moment you exit tmate. That's by design: tmate is built for sharing a session in progress, not for coming back to your own work tomorrow. More on that trade-off in tmate alternative.

Option 3: a purpose-built client

The gap both options above share is that neither was designed around a phone. ttyd and wetty render xterm.js, which assumes a keyboard with modifier keys; tmate's web link is read-only and desktop-oriented. A client built specifically for reaching your own tmux sessions from a browser — mtmux is one — starts from different defaults: it dials out from your machine instead of listening for inbound connections, so there's no port to open; it encrypts end-to-end (x25519 key agreement, ChaCha20-Poly1305 frames) through a relay that can't read the session; and the UI is built for touch, with the prefix key and modifiers as an on-screen row instead of a keyboard shortcut you don't have. Setup is npm i -g mtmux and pointing it at a session, not a proxy config.

SetupInbound portEncryptionMobile UIPersistent sessionPrice
ttyd / wetty + proxynginx, TLS, htpasswdyes, one you openTLS to your proxynone — desktop keyboard assumedyes, if tmux itself persistsfree, open source
tmate web linkinstall tmate, run itno — outbound to tmate's relaySSH transport; relay isn't zero-knowledge unless self-hostedread-only, no touch inputends when tmate exitsfree, open source (self-hostable)
Purpose-built (mtmux)npm i -g mtmuxnone — dials outx25519 + ChaCha20-Poly1305, zero-knowledge relaytouch-first, built for phoneattaches to your existing tmux serverfree (1 machine + 2 devices), Pro $8/mo

Where each one genuinely wins

If you already run and trust your own reverse proxy, and you only ever need this from a desktop browser, ttyd is less machinery than anything else here — it's a single static binary and a config block you already know how to write. If what you need is to show someone your terminal for the next ten minutes, tmate's web link is faster than any of the alternatives and requires literally nothing on the viewer's side. If what you want is your own sessions, reachable from your phone specifically, with no inbound port to maintain, that's the gap the purpose-built option closes — full trade-offs on the comparison page and setup in the docs.

Can I view a tmux session in a browser without installing anything extra?

Not directly — tmux itself has no browser output. You need something in between: ttyd/wetty wrapping the command, tmate's hosted web link, or a purpose-built client. All three require installing something on the machine running tmux; none require anything extra to just view from a desktop browser.

Is ttyd secure enough to expose to the internet directly?

Not on its own — ttyd has no built-in authentication in its default mode. Bind it to 127.0.0.1 and put a reverse proxy with TLS and auth in front, as in the recipe above, rather than exposing its port directly.

Does tmate's web link let me type, or only watch?

By default it's read-only — show-messages prints a ro- prefixed URL for read-only viewing. tmate also has a read-write SSH session for participants you actually want typing alongside you, but the plain web link is for watching.

Does a browser-based tmux client work well on a phone?

It depends which one. ttyd and wetty render a standard terminal that assumes a physical keyboard, so the tmux prefix key and modifiers are awkward on a soft keyboard. That specific problem — and what to do about it — is covered in tmux from your phone.

What's the difference between tmate and a browser-based tmux viewer?

tmate creates a new, separate tmux session for sharing; it isn't a window into a session you already have running. A browser client that attaches to your existing tmux server — like ttyd pointed at tmux attach, or a purpose-built client — shows you the actual session, not a copy.

Put this to work — mtmux attaches to the tmux server you already run.

Related reading

mobile

tmux from your phone: the modifier key problem

A native SSH app, a web terminal, or a purpose-built client can reach tmux from a phone — the real obstacle is Ctrl-b on a screen with no keyboard.

Ivo Hart7 min read