The network edge: proxy, sharing, login and domains
Every VM is on the internet at
https://vmname.exe.xyz/, “with certificates and TLS handled by the HTTPS proxy”. Sites are private until shared, and Login with exe tells your app who is visiting.
Sources: https://exe.dev/docs/proxy, https://exe.dev/docs/sharing, https://exe.dev/docs/login-with-exe, https://exe.dev/docs/cnames, https://exe.dev/docs/internet-access, https://exe.dev/docs/receive-email, https://exe.dev/docs/send-email.
The HTTPS proxy
exe.dev “proxies traffic to https://vmname.exe.xyz/ to your VM seamlessly, handling certificates, TLS termination, and optionally offering basic authentication.”
Which port gets proxied
- Automatic choice. exe.dev picks from the ports listed under
EXPOSEin the image’s Dockerfile. It prefers port 80 and otherwise falls back to the smallest exposed TCP port of 1024 or higher. - Choosing a port yourself. Run
ssh exe.dev share port <vmname> <port>to change it. This keeps the current visibility setting. - exeuntu. On the default image the share port is
8000(https://exe.dev/docs/migrating-to-exe).
Additional ports
The proxy transparently forwards ports 3000 to 9999, so https://vmname.exe.xyz:3456/ reaches port 3456. “You may only mark a single port public”. The other ports can only be reached by users with access to the VM.
Headers your app receives
| Header | Meaning |
|---|---|
X-Forwarded-Proto | https when the client used TLS |
X-Forwarded-Host | The full host header (including port) the client requested |
X-Forwarded-For | Prior value plus the client IP as seen by exe.dev |
X-ExeDev-UserID | Stable, unique user ID (only when authenticated) |
X-ExeDev-Email | The user’s email (only when authenticated) |
JS dev servers
Next.js 15.2 and later needs allowedDevOrigins: ['my-vm.exe.xyz']. Vite 5 and later needs server.allowedHosts: ['my-vm.exe.xyz'] (https://exe.dev/docs/faq/nextjs-and-friends).
Private by default, then share
“By default, only users with access to the VM can access the HTTP proxy. Users accessing https://vmname.exe.xyz/ for the first time will be redirected to log into exe.dev.”
Access levels
- Web. Opens the VM’s private HTTPS proxy after the person logs in. “It does not give them a shell.”
- Root. Web access plus SSH, the browser Terminal and Shelley. “A Root collaborator can read, change, or delete anything inside the VM”. It needs an existing exe.dev account and does not transfer ownership.
| Goal | Command |
|---|---|
| Share web with a person (invites by email) | share add <vm> <email> |
| Give an existing user Root | share add <vm> <email> --root |
| Share web with the whole team | share add <vm> team |
| Give the whole team Root | share add <vm> team --root |
| Share by link (Web only, recipient must register) | share add-link <vm> |
| Revoke a link | share remove-link <vm> <token> |
| Make it public | share set-public <vm> |
| Make it private again | share set-private <vm> |
| Downgrade Root to Web | share remove <vm> <email> --root |
| Remove all access | share remove <vm> <email> |
| Inspect | share show <vm> |
Two details that come up in conversation:
- Team shares follow current membership: “new members gain access and former members lose it.”
- Revoking a link stops new people from using it, but it doesn’t remove people who already accepted.
Team admins can restrict sharing with team settings vm-sharing off|admins-only|all-members (https://exe.dev/docs/teams/sharing-controls).
Login with exe
This feature lets “you build authorization without managing passwords or e-mails yourself.”
- Private sites always carry the authentication headers, because a visitor must have been shared in to reach the site.
- Public sites carry the headers only for logged-in users. Send people through
https://vmname.exe.xyz/__exe.dev/login?redirect={path}to require an email. - Logging out is a POST to
https://vmname.exe.xyz/__exe.dev/logout. - Local development. Nothing adds the headers on
localhost, so the docs suggestmitmdumpin reverse mode to injectX-Exedev-EmailandX-Exedev-Useridwhile you test.
# From the docs: allow only listed emails
if ($http_x_exedev_email = "alice@example.com") { set $allowed "true"; }
if ($allowed = "false") { return 403; }
Custom domains
Setting up a custom domain takes two steps:
- At your DNS provider, add a CNAME (or an ALIAS/ANAME/flattened CNAME for an apex domain) pointing to
vmname.exe.xyz. - Run
ssh exe.dev domain add <vm> <domain>.
TLS certificates are issued automatically. Until a domain is registered, requests for it get 421 Misdirected Request.
Other domain commands:
- List:
domain ls <vm>for one VM, ordomain ls -afor all of them. - Remove:
domain rm <vm> <domain>. - Wildcard certificates:
domain add --wildcardgives you a*.example.comcertificate after a one-time delegation of ACME challenges. - Wildcard DNS: a wildcard CNAME
*.app.example.complusdomain add vmname '*.app.example.com'sends every one-level subdomain to one VM.
The Cloudflare gotcha
Cloudflare defaults DNS records to Proxied (orange cloud). That “replaces your desired CNAME/ALIAS targets with Cloudflare IP addresses, and therefore breaks exe.dev’s custom domain support.” The fix has two parts:
- Use DNS Only (grey cloud), or keep the proxy and rewrite requests to
vmname.exe.xyzwith Cloudflare Snippets or Workers. The docs note that “Snippets are a paid feature.” - Set CNAME flattening to Flatten CNAME at root. Don’t use “Flatten all CNAMEs”, which “prevents us from learning the VM name.”
Outbound: egress policy
The default policy is all. Setting it to integrations blocks the public internet but keeps exe.dev metadata and attached integration endpoints reachable:
ssh exe.dev egress my-vm integrations
ssh exe.dev new --egress=integrations
ssh exe.dev cp my-vm --egress=integrations # copies inherit the policy unless set
New VMs use the DNS server at 169.254.169.254. Older VMs need /etc/resolv.conf updated before you restrict egress (https://exe.dev/docs/internet-access).
Email in and out
Receiving
Turn it on with ssh exe.dev share receive-email vmname on. Mail sent to *@vmname.exe.xyz lands in ~/Maildir/new/.
Limits from the docs:
- No spam, virus or phishing checks.
- No custom domains yet.
- 1 MB maximum message size.
- More than 1,000 files sitting in
new/disables receiving.
Source: https://exe.dev/docs/receive-email.
Sending
Send plain text with POST http://169.254.169.254/gateway/email/send, using the fields to, subject and body. Attachments are supported, and sending is rate-limited. Allowed recipients are:
- you
- your team
- people who logged into your shared VM
- people who emailed the VM, which is thread-scoped and DKIM-verified
Source: https://exe.dev/docs/send-email.