Get started
Three commands, once per machine. After that, the last one is all you need.
-
Install the agent
On macOS and Linux, without administrator rights. It goes to ~/.local/bin and checks the download's checksum.
curl -fsSL https://connect.exposehub.eu/install.sh | shOn Windows, download exposehub-windows-amd64.exe or, on ARM, exposehub-windows-arm64.exe, and use it from a terminal.
-
Log in
Create a token on the Agent tokens page of the dashboard, one per machine, then paste it when the agent asks. It is saved for your user only and stays out of your shell history.
exposehub login -
Open a tunnel
Give it the port your service listens on. The agent prints the public address and keeps it for as long as it runs.
exposehub http 8000Forwarding https://k3v9q2mw7x.exposehub.eu -> http://localhost:8000 Inspector http://127.0.0.1:4040
Stop it with Ctrl+C. If the connection drops, the agent reconnects by itself and keeps the address. exposehub update installs the newest agent the server offers.
Ports and local sites
The target can be a port, a host and port, or a full URL:
exposehub http 8000
exposehub http shop.test
exposehub http https://localhost:8443
A local site with a name of its own, such as shop.test, keeps seeing that name in the Host header, so virtual hosts in your local web server keep working. The public address arrives in X-Forwarded-Host. A plain port sees the public address as Host instead. Use --host-header preserve|rewrite to change either.
Your service also gets the visitor's address in X-Forwarded-For and the scheme in X-Forwarded-Proto, as behind any proxy.
Dev servers
Vite and webpack dev servers turn away requests that name a host they do not know. The agent notices that answer, sends the dev server its own name from then on and says so in the terminal; pages and hot reload then work without any setting. With --host-header preserve the agent leaves the public name alone, and the dev server needs it in its own list of allowed hosts.
Next.js serves its pages under any name, but lets hot reload through only from addresses it allows. The agent passes the hot-reload connection of a page opened through the tunnel on as coming from the dev server's own address, and says so in the terminal; pages of other sites stay shut out. With --host-header preserve it leaves that alone and prints the allowedDevOrigins line to add to next.config.js instead.
Several tunnels at once
One agent can open up to ten tunnels, for example a shop, its API and an admin, each on its own address. Name them on the command line:
exposehub http shop=8000 api=3000 admin=8080
Each name becomes a subdomain. Flags such as --auth or --allow apply to all of them, the inspector shows them together, and every request line says which tunnel it came through.
For tunnels you start every day, put them in an exposehub.yml in your project and run exposehub start. Each tunnel can have its own settings:
tunnels:
shop:
target: 8000
subdomain: shop
auth: ["ada:${SHOP_PASSWORD}"]
allow: ["Office VPN"]
hooks:
target: 8000
subdomain: shop-hooks
log: bodies
docs:
target: docs.test
hostname: dev.example.com
exposehub start shop docs opens only those. The file is looked for in the current directory and its parents, so it works from anywhere in the project. ${NAME} reads an environment variable, so passwords stay out of the file; a variable that is not set stops the agent instead of opening a tunnel without a password.
Webhooks
Payment providers, git hosts and other services can call code running on your machine. Start a tunnel with a fixed name and enter its address in the provider's webhook settings:
exposehub http 8000 --subdomain shop-hooks
https://shop-hooks.exposehub.eu/webhooks/payment
Set a breakpoint and debug the real request. When you need it again, replay it from the inspector instead of triggering a new payment.
Inspector
While the agent runs, http://127.0.0.1:4040 shows every request through your tunnels as it happens: method, path, status and timing, and for each one the full request and answer with headers and bodies.
- Replay sends a request to your service again, as often as you like.
- Copy as curl gives you the request as a command to tweak and run.
- Filter by path, method or status, and see the error share, the median and the 95th-percentile time at a glance.
Nothing of it leaves your machine. The agent keeps the last 1000 requests, up to 512 KiB of each body. If port 4040 is taken, it uses the next free port up to 4049 and prints which. --inspect off turns it off.
Request logs
The Request logs page in the dashboard keeps a summary of every request to your tunnels for 7 days: time, address, method, path, status, duration, size and the visitor's network. It works after the agent has stopped, which the inspector does not.
- Only you see your logs. Share them with a colleague in your team on that page, and stop sharing whenever you like.
- Values in the address that look like secrets (token, key, signature, password and the like) are stored as [redacted]. The visitor's IP address is shortened to its network.
- Headers and bodies are kept only when you start the tunnel with
--log-bodies, up to 64 KiB each, with authorization headers and cookies blanked out. --no-logkeeps nothing of a tunnel's requests.
Password protection
Let visitors in only with a name and password:
exposehub http 8000 --auth ada:s3cret --auth ben:0th3r
The tunnel server checks it, so a request without the right password never reaches your machine. The browser asks for it once. Your service does not see the Authorization header the password came in, so its own login keeps working.
Use it whenever a tunnel is open for longer, and always on a domain of your own: such a domain is listed publicly as soon as its certificate is issued, and scanners ask it for files like .env within seconds.
Limit to networks
A tunnel can answer only requests from certain networks, such as your company VPN. Everyone else gets a page saying the tunnel is limited to certain networks, with their own address, and the request never reaches your machine. Set it up on the Access page of your team:
- Networks are named lists of addresses and ranges, IPv4 or IPv6, for example the exit addresses of the VPN. On the VPN, "Add my address" fills in the address you connect from.
- The team default applies to every address without a rule of its own, random ones included: anyone, or only listed networks.
- Saved addresses are names your team keeps: nobody else can open a tunnel on them, and each has a rule. Keep the team on the VPN and open just the address you give a payment provider for webhooks.
- Your own domains get a rule there too.
Team owners and admins manage networks and the team default. Members save addresses and limit them further, but cannot open what the team keeps closed. Changes reach running tunnels within seconds.
On the command line, --allow limits a single run further. It never opens what the dashboard keeps closed: a visitor has to pass both.
exposehub http 8000 --allow 'Office VPN' --allow 2001:db8:42::/48
Your own domain
A team can open tunnels on a domain it controls, such as dev.example.com, with a certificate issued for it.
-
Add it
On the Domains page of your team. Team owners and admins can add domains.
-
Create two DNS records
The page shows them with your values: a TXT record that proves the domain is yours, and a record that points it at the tunnel server. Then press Check DNS.
TXT _exposehub.dev.example.com exposehub-verify=… CNAME dev.example.com connect.exposehub.eu
For the top of a domain (example.com itself), DNS does not allow a CNAME; the page then asks for the address instead.
-
Open a tunnel on it
exposehub http 8000 --hostname dev.example.com --auth ada:s3cretThe certificate is issued within about a minute of connecting the domain. Until then, browsers show a certificate warning.
Teams and tokens
Everyone has a personal team. Create more teams in the dashboard and invite colleagues: a team shares its addresses and domains, and each person has tokens of their own.
- Create one token per machine and name it after it. When a laptop is lost, revoke that one token; agents using it are disconnected within seconds.
- Members see and revoke their own tokens. Team owners and admins see and revoke everyone's, and manage the team's domains.
- When someone leaves a team, their tokens for it stop working and their request logs in it are deleted.
- The Tunnels page lists your agents that are connected right now. Disconnect one you left running on another machine: that agent stops, and its token keeps working. Owners and admins see and disconnect the whole team's.
Commands and flags
exposehub login- Save your agent token. It asks for it, so it never lands in your shell history.
exposehub http TARGET...- Open a tunnel to each target: a port, host[:port] or URL. NAME=TARGET asks for that subdomain.
exposehub start [NAME...]- Open the tunnels of exposehub.yml, all of them or the ones named.
exposehub update- Install the agent version the server offers.
exposehub logout- Forget the saved token. The token stays valid until you revoke it in the dashboard.
exposehub server [URL]- Show which tunnel server is used and why, or switch to another.
exposehub version- Print the agent's version.
Flags for exposehub http
--subdomain NAME- Ask for a name under exposehub.eu, for example shop. Without it the server picks a random one.
--hostname HOST- Use a domain your team connected in the dashboard instead of a subdomain.
--auth NAME:PASSWORD- Let visitors in only with this name and password. Repeat it for several people.
--allow VALUE- Let only visitors from this address, range (203.0.113.0/24) or named network of your team in. Repeat it for more. Adds to the rules in the dashboard.
--log-bodies- Keep headers and bodies in the request log too, not only the summary.
--no-log- Keep nothing of this tunnel's requests in the dashboard.
--inspect ADDR- Where the inspector listens, 127.0.0.1:4040 by default, or off.
--host-header MODE- preserve or rewrite the Host header your service sees. The default depends on the target.
--domain DOMAIN- Which tunnel domain to use, when the server offers more than one.
--server URL- Another tunnel server than the saved one. EXPOSEHUB_SERVER works too.
--token TOKEN- Another token than the saved one. EXPOSEHUB_TOKEN works too.
--verbose- Print connection details while it runs.
When something is off
- Visitors see "This tunnel is reconnecting"
- Your agent lost its connection and is coming back; the address is kept for it. The page retries every 5 seconds by itself.
- Visitors see "Nothing is connected here"
- No agent is serving that address. Start it again; the page shows the command with the right name.
- Visitors see "The site behind this tunnel is not answering"
- The tunnel works, but your service does not: it is stopped, still starting, or listens on another port. The page names the address the agent tried and retries every 5 seconds.
- Visitors see "This tunnel is limited to certain networks"
- They are not on a network the tunnel allows. The page shows their address: connect the VPN, or add the address to a network on the Access page.
- The agent prints "Waiting" and that nothing answers at your local address
- Nothing listens on the local port yet. The tunnel is open all the same and starts serving as soon as your service does.
- "the server closed the tunnel: it was disconnected in the dashboard"
- You or a team owner closed it on the Tunnels page. The token is still valid: start the agent again.
- "is saved by another team"
- Another team keeps that name for itself. Pick another one.
- "your team has no network called …"
- The name after --allow does not match a network on the Access page. Names are not case sensitive, but spelling counts; put names with spaces in quotes.
- "the server rejected the token"
- The token was revoked, the team was deleted or you left it. Create a new token and run
exposehub loginagain. - "is in use by another agent of your team"
- A colleague is serving that name right now. Pick another one, or ask them to stop.
- "is in use by another account"
- Another team uses that name at the moment. Pick another one.
- The browser warns about the certificate on my own domain
- The certificate is issued within about a minute of connecting the domain. If you clicked through the warning before that, close the tab or restart the browser: some browsers keep showing the site as not secure for the rest of the session.
What is stored
Requests to your tunnels go to your machine and back; their content is not stored unless you start a tunnel with --log-bodies. The request log keeps summaries for 7 days, visible only to you and those you share it with. The inspector keeps everything on your machine.
The privacy notice explains in full what is processed and why.