Skip to content
localhost-aliases
macOS menu bar appopen source

Real names for the dev servers already running on your Mac.

what you type today

http://localhost:3000

what you type instead

http://myapp.test

Point a name at the port your dev server already listens on. One admin prompt when the app launches, nothing permanently installed, and no change to how you start anything.

Download 2.0.0

LocalhostAliases-2.0.0.dmg · 91.0 MB · macOS 13.0+ · Apple Silicon · 26 August 2026
sha256 178441ba0c0f

Patchbay

3 aliases · 2 livePort
  • myapp.test127.0.0.2:80 → 127.0.0.1:3000:3000
  • api.myapp.test127.0.0.3:80 → 127.0.0.1:3001:3001
  • docs.test127.0.0.4:80 → 127.0.0.1:4321:4321

how it works

A name, an address, and a splice

A name resolves to an address and never to a port. So each alias is given a loopback address of its own, and a root process splices port 80 there onto the port you already use. Scroll to follow one request down the wire.

one request, end to endmyapp.test → 127.0.0.1:3000
  1. 1 · you type a name

    http://myapp.test

    The browser asks the system for the address behind that name, exactly as it would for any site.

  2. 2 · the name resolves

    /etc/hosts

    One line, in the block the app owns, answers with 127.0.0.2 — an address it added to lo0. Names resolve to addresses and never to ports, which is why each alias needs one of its own.

  3. 3 · the root agent is listening

    127.0.0.2:80

    One process, started by the prompt at launch, holds :80 there. The address alone says which alias this is.

  4. 4 · the bytes are copied across

    raw TCP

    No Host header is read, nothing is rewritten, nothing is logged — the bytes are never looked at. HMR and WebSockets pass straight through.

  5. 5 · your dev server answers

    127.0.0.1:3000

    Exactly as you started it, on the port it already had. It never learns that any of this happened.

Two hops and a lookup. /etc/hosts answers myapp.test with 127.0.0.2, an address on lo0; the root agent holds :80 there and copies raw bytes to 127.0.0.1:3000. Nothing on the second hop is parsed, which is why HMR and WebSockets need no configuration — and why the same wire can carry https:// once a certificate exists for the name.

why bother

What a name actually buys you

A port number is not a name: it means nothing, and it moves the next time something else grabs it first.

  • localhost:3000
  • localhost:5173
  • localhost:8080
  • shop.test

A name you can read

Five tabs on five ports look identical, so you open them in turn until one is the project you meant. Named, the tab bar is an index — and history, autofill and bookmarks key on something that means the project.

localhost:3000 :3001 :5173

one cookie jar, last writer wins

shop.test

its own

api.test

its own

One origin per project

Cookies ignore the port, so everything you serve from localhost shares a single jar and two apps that both set session quietly overwrite each other. Each alias is its own origin.

https://myapp.test
http://myapp.teststill bound

https:// when you want it

Off by default. Turn it on and every alias also answers over TLS, certificate issued and renewed for you, with http:// still bound. One step stays manual, on purpose.

what changes here

Two markers in /etc/hosts, one address on lo0, one prompt

Everything privileged is one idempotent apply, and this is the whole of it — so you can decide before you type a password rather than after.

/etc/hosts1 managed block
### Host Database##127.0.0.1 localhost# >>> localhost-aliases >>>127.0.0.2 myapp.test127.0.0.3 api.test# <<< localhost-aliases <<< # my own line, from years ago192.168.1.20 nas

Above and below the markers: preserved byte for byte. The agent refuses to write at all if one byte outside the block would change.

  • one prompt, per launch

    It starts the root agent, which is also the forwarder. Adding, removing, renaming and re-porting aliases after that never ask again. A reboot clears lo0, so the next launch prompts once more.

  • one address per alias

    ifconfig lo0 alias 127.0.0.2 — taken from 127.0.0.2 … 127.0.0.254 and kept for the alias’s life. 127.0.0.1 is never used, and an address it did not allocate is never removed.

  • nothing installed

    No launch daemon, no privileged helper, no sudoers entry. The agent exits by itself when the app stops, so quitting leaves nothing running as root — and needs no second prompt.

  • reversible

    Uninstall — from the tray, from the dashboard or with make uninstall — takes the block and the loopback addresses back out, and removes the certificate authority by fingerprint.

The tradeoff, stated here rather than discovered later

The agent reconciles your machine to a file your own account can write, and that is exactly what buys you one prompt instead of one per change: while the app runs, anything running as you can ask root to edit that block. It is bounded — every hostname, address and port is re-validated on each read, only 127.0.0.2–127.0.0.254 is ever touched, and nothing named in that file is ever executed. Quit the app and nothing runs as root. The long answer is in the FAQ.

get it

macOS 13 or later, Apple Silicon, an admin account

It runs entirely on your machine. Every address it creates is loopback, so nothing becomes reachable from your network or the internet.

Download 2.0.0

LocalhostAliases-2.0.0.dmg · 91.0 MB · macOS 13.0+ · Apple Silicon · 26 August 2026
sha256 178441ba0c0f

  • What it will not doIt does not start, restart or wrap your dev server, expose anything beyond loopback, or inspect a byte it forwards. There is no Linux or Windows build. How aliases work has the edges in full.
  • For coding agentsAn MCP server ships with the app, so your agent can list and create aliases and write http://myapp.test in your docs and tests instead of guessing a port. One-click install into Claude Code or Codex, on the MCP page.
  • Names end in .testReserved for development by RFC 6761 and claimed by nothing on macOS, so the /etc/hosts line simply answers. .local is not offered: it costs about five seconds per lookup, and the FAQ has the measurements.