Virtual hosts

A virtual host is the pairing of a hostname with something that answers for it. Vhostly keeps one block per site, in files of its own, included from the httpd.conf you maintain by hand.

Choosing a domain

Any hostname works, but the suffix matters:

  • .test is reserved by RFC 6761 for exactly this. It will never be sold as a real TLD and browsers will not try to resolve it publicly. This is the default and the recommendation.
  • .local is claimed by multicast DNS. It works, but resolution can be slow or inconsistent while Bonjour tries to answer first.
  • .dev and .app are real TLDs on the HSTS preload list, so browsers force HTTPS on every request. Usable with HTTPS enabled, confusing without it.

Subdomains are ordinary hostnames — add api.myapp.test as its own site pointing at its own folder or port.

How the domain resolves

There is no local DNS server and no wildcard. Vhostly appends the hostname to the lines already in /etc/hosts:

127.0.0.1       localhost myapp.test api.myapp.test
::1             localhost myapp.test api.myapp.test

Both lines get it, and that is deliberate. macOS asks for the IPv6 address first; if ::1 does not list the name, the lookup waits for that query to time out — about five seconds — before falling back to IPv4. Every page load would pay it.

Removing a site strips the hostname from any line that carries it and leaves the rest of the line alone. Vhostly does not tag its entries with a marker comment, so if you keep your own hosts entries, the tokens it adds sit alongside them on the same lines.

The static block

What gets written is unremarkable Apache configuration:

<VirtualHost *:80>
    ServerName myapp.test
    DocumentRoot "/Users/you/Sites/myapp/public"

    <Directory "/Users/you/Sites/myapp/public">
        Options Indexes FollowSymLinks
        AllowOverride All
        Require all granted
    </Directory>

    LogLevel warn
    ErrorLog "/opt/homebrew/var/log/httpd/myapp_error.log"
    CustomLog "/opt/homebrew/var/log/httpd/myapp_access.log" common
</VirtualHost>

AllowOverride All means your .htaccess files are read, which is what framework routing depends on. The log file names come from the ServerName with .test removed.

Preview config on the row shows the block as it stands on disk, including the :443 block if the site has one.

Active and disabled

Sites live in one of two files:

FileHolds
httpd-vhosts.confActive sites — Apache serves these
httpd-vhosts.conf.disabledDisabled sites, kept verbatim

Apache only ever includes the first. Toggling a site moves its block between the two, adds or removes the hosts entry, and restarts Apache.

Hide inactive in the panel header takes disabled sites out of the list, and Show inactive brings them back. The header still counts every site. Vhostly remembers the choice the next time you open the app.

Editing

The pencil on a site's row opens the same form as New, filled in.

Editing a site rewrites its block from the form rather than patching it in place: the old block is removed from both files and a fresh one is appended to the active file. If the site has HTTPS, its :443 block is rebuilt too, so a new port, document root or name reaches both. Renaming a site issues a certificate for the new name. Two further consequences follow from the rewrite.

The site moves to the end of the file. Ordering only matters if two blocks claim the same ServerName, which is a mistake in its own right.

Anything you added to the block by hand is lost. These files are generated. If you need directives Vhostly does not offer, put them in an .htaccess file in the document root, or in a separate config file included from httpd.conf.

Repair

Repair on the Virtual Hosts panel reconciles the machine with the vhost files. It walks every site, adds a hosts entry for each active one, removes it for each disabled one, and — if you tick the HTTPS option — brings up HTTPS on every active site that does not have it yet.

Before restarting, it runs apachectl -t. If the configuration does not parse, nothing is restarted and the error is shown, so a broken config cannot take your running Apache down with it.

Reach for it after editing the files by hand, after restoring a backup, or when a site resolves but should not.