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:
.testis 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..localis claimed by multicast DNS. It works, but resolution can be slow or inconsistent while Bonjour tries to answer first..devand.appare 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:
| File | Holds |
|---|---|
httpd-vhosts.conf | Active sites — Apache serves these |
httpd-vhosts.conf.disabled | Disabled 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.