Are you a developer who uses macOS, and do you want to add project names to your localhost development URLs? Here is how I created custom URLs for my localhost projects.

Custom URLs are convenient because you can separate bookmarks and sessions across several projects on the localhost domain. Another perk is that you no longer have to remember which port they use.

What will we do?

In this article, we will install Caddy on macOS using Homebrew and use a minimal configuration to set up custom project URLs with HTTPS support.

Caddy is a web server suitable for production hosting, and we will use only a tiny fraction of its capabilities for our development goals.

Using its reverse proxy functionality, we will run Caddy on our local machine to create routes from custom URLs to localhost on specific ports.

Installing Caddy

Use Homebrew to install Caddy on your machine:

brew install caddy

A quick way to check if Caddy has been installed correctly is by calling its version command:

caddy version

Next, create the global Caddy configuration file or Caddyfile in the Homebrew configuration directory. Its path will either be:

  • /opt/homebrew/etc/Caddyfile on M1 and newer machines.
  • /usr/local/etc/Caddyfile on Intel and older machines.

The easiest way to generate the file in the correct directory is by using the output of the brew --prefix command to create the file:

touch $(brew --prefix)/etc/Caddyfile

You can edit this file in your favorite IDE. I even created a ZSH alias that opens this file using Visual Studio Code for easy access.

Trusting the SSL certificates

Caddy ships with internal, self-signed SSL certificates; you must tell your system to trust them.

Caddy will ask you to trust the certificates the first time you run a configuration that requires these certificates.

You can also manually trigger the trust command:

brew services start caddy
caddy trust
# You will probably need to use `sudo`:
# sudo caddy trust

You will only need to run the trust command once. After this, you should not have to use sudo for any caddy command.

Populating the Caddyfile

Open the Caddyfile you've created; it is time to configure some entries.

Note: I recommend creating one entry first, testing it to see if it works, and then adding the rest.

We start with our desired domain and reverse proxy it to its original localhost counterpart:

project-name.localhost {
    reverse_proxy localhost:8000
}

# You can also group multiple domains:
project-one.localhost,
project-two.localhost,
project-three.localhost {
    reverse_proxy localhost:8080
}

# Or use wildcards:
*.some-project.localhost, {
    reverse_proxy localhost:5173
}

The first entry tells Caddy to pass requests from https://project-name.localhost to localhost:8000.

Take note of the localhost top-level domain (TLD). This is a reserved TLD that will loop back to your host machine, which is convenient because we don't have to register such domains in our machine's host file (/etc/host).

Caddy will also automatically use a self-signed certificate on our custom domain because of the localhost TLD. You could add tls internal to a domain entry to make it more explicit, but it is unnecessary.

Validating the configuration file

Caddy has a built-in command to check the validity of Caddy configuration files; use this command to check the Caddyfile used by Homebrew:

caddy validate --config $(brew --prefix)/etc/Caddyfile

Running and restarting

For Caddy to work, you must run It as a Homebrew service. Additionally, after updating the Caddyfile, you must restart the service to ensure the changes take effect.

Start using Homebrew's built-in command:

brew services start caddy

If the service is already running, but you've changed the Caddyfile since it started, restart the service instead:

brew services restart caddy

Note: Restarting is quick, so don't be afraid to call this command.

If you want to stop the service, use the stop command instead:

brew services stop caddy

Wrap-up

Use Caddy to make prettier URLs for your localhost domains and scope domain-specific data.

The reverse proxy method is the simplest use case in the documentation. You could also set up more complex configurations. I highly recommend checking the Caddy documentation for more advanced use cases.

See also

Bug report: Safari

The solution described in this article does not work in Safari, as Apple's favorite browser cannot find the host.

At the time of writing, I have not found out why or how to fix it. If you happen to know why, please let me know!

Changelog

2024-04-02:

  • Moved the caddy trust step below the creation of the Caddyfile.
  • Added a brew services start command to the caddy trust step.
  • Added a section about Safari bugging out.
  • Hat/tip to Kevin Renskers for the points above!
  • Improved wording throughout the article.