Domain Routing & HTTPS
Corral gives every project a custom local domain — like my-worker.test — with automatic HTTPS. No more juggling localhost port numbers.
How It Works
When you visit my-worker.test in your browser, the request flows through:
my-worker.test
→ macOS resolver sends DNS query to dnsmasq (127.0.0.1:5353)
→ dnsmasq resolves *.test → 127.0.0.1
→ Caddy on port 443 terminates HTTPS, reads the Host header
→ Reverse-proxies to wrangler dev on localhost:8787
Three services make this work:
- dnsmasq — a lightweight DNS server that resolves all
*.testdomains to127.0.0.1 - Caddy — a reverse proxy that routes requests to the correct project based on the hostname, with automatic HTTPS
- macOS resolver — a system-level file (
/etc/resolver/test) that tells macOS to use dnsmasq for.testlookups
The .test TLD
Corral uses .test as the default top-level domain. This TLD is reserved by IANA specifically for testing and development — it will never resolve to a real website.
Each project gets a subdomain based on its name:
| Project Name | Local Domain |
|---|---|
my-worker | my-worker.test |
auth-service | auth-service.test |
My Cool App | my-cool-app.test |
You can override a project’s subdomain:
corral project set my-worker subdomain api
# Now accessible at: api.test
Changing the TLD
You can switch to a different TLD in settings:
corral settings set tld localhost
Or set a custom value:
corral settings set tld dev
When you change the TLD, Corral reconfigures DNS and the reverse proxy automatically. All projects switch to the new TLD.
Automatic HTTPS
Caddy generates locally-trusted HTTPS certificates automatically. When you visit https://my-worker.test, Caddy:
- Generates a certificate signed by its built-in local CA
- Serves the certificate to your browser
- Proxies the request to your running worker
No manual certificate setup is required.
Framework Host Validation
Modern frameworks like Vite 6+ block requests from unrecognized hostnames as a security measure. When you access my-app.test instead of localhost, the dev server may reject the request.
Corral handles this automatically for all Vite-based frameworks (Vite, Astro, SvelteKit, Remix, Nuxt, Angular) — no configuration needed on your part.
Next.js
Next.js uses its own host validation (allowedDevOrigins) that can’t be configured automatically. Add your .test domain to next.config.js:
// next.config.js
module.exports = {
allowedDevOrigins: ["my-app.test"],
};
Corral shows a reminder in the app and project logs when you start a Next.js project.
Serving a project at localhost (address mode)
The host validation above is about the dev server refusing the .test host — Corral handles that. A different problem is when your app’s own code hard-codes a localhost assumption, for example:
// Passes only when the URL's hostname is exactly localhost / 127.0.0.1.
const isLocal = ["localhost", "127.0.0.1", "[::1]"].includes(new URL(request.url).hostname);
Behind my-app.test, that check sees my-app.test and fails. For these projects you can give the project a localhost:<port> front door in addition to its .test URL.
In the macOS app, open the project’s Settings → Dev Server → Open at and pick:
<name>.test(default) — serve only the proxy.testroute.- localhost (HTTP) — also serve
http://localhost:<port>. - localhost (HTTPS) — also serve
https://localhost:<port>andhttps://127.0.0.1:<port>with Corral’s locally-trusted CA, so there’s no certificate warning. This works even if you’ve turned global HTTPS off — choosing it trusts Corral’s CA on demand.
A Localhost port field lets you pin the port (e.g. to match a value your code hard-codes); leave it on Auto to let Corral pick a free one. By default the .test URL stays available (dual-home) — turn off “Also serve <name>.test” for localhost-only.
From the CLI:
corral project set my-app address-mode localhost # or localhost-tls / proxy
corral project set my-app localhost-port 5173 # or "none" to auto-pick
corral project set my-app serve-proxy-route false # single-home (drop .test)
corral project open my-app # opens the canonical URL
Everything Corral provides keeps working in localhost mode — Browser & Network capture, the branded down-page and lazy start, and logs — because the request still flows through Corral. The trade-offs: the URL carries a port (and isn’t HTTPS unless you pick the TLS variant), and an address change applies on the project’s next start.
Managing Infrastructure
Start and stop DNS and proxy services independently:
# Start infrastructure only (without starting projects)
corral infra start
# Check status
corral infra status
# Stop infrastructure
corral infra stop
Or let corral up and corral down handle both infrastructure and projects together.
Cascade Behavior
If DNS or proxy infrastructure fails, Corral does not cascade-fail your running projects. Instead, projects remain Active/Ready but enter a Degraded health state with a DnsDown or ProxyDown signal.
The macOS app’s URL hero shows a localhost fallback toggle when DNS is down — clicking it switches the primary URL from my-app.test to localhost:8787. When DNS recovers, Corral automatically removes the degradation signal and the localhost toggle disappears.
Similarly, if a linked service fails (e.g., a PostgreSQL service that a project depends on), the project stays running but enters Degraded health with a LinkedServiceDown signal. Multiple failed services stack independently — each adds its own signal. The project detail view shows a banner for each failed dependency with a link to that service’s detail page.
The Privileged Helper
DNS and proxy services require elevated privileges on macOS (writing to /etc/resolver/, binding to ports 80/443). Corral handles this through a privileged helper daemon:
- Installed on first launch via the setup wizard, or later from Settings
- Runs only when infrastructure is active — not a persistent background service
- Can be uninstalled at any time from the macOS app’s Settings
The helper manages:
- Writing and removing the resolver file at
/etc/resolver/{tld} - Starting and stopping dnsmasq
- Starting and stopping Caddy
Network Access and .test Domains
The .test domain routing described above only works on the Mac running Corral — other devices on the network can’t resolve .test domains because they don’t have dnsmasq configured.
When you use the --host flag to serve a project on the local network (see Network Access), other devices access the dev server directly via your machine’s LAN IP and port (e.g., http://192.168.1.100:8787), bypassing Caddy entirely. This means:
- The
.testdomain with HTTPS continues working on your Mac - Other devices use the raw HTTP URL at the LAN IP
- No HTTPS or custom domain on other devices
The Resolver File
macOS uses files in /etc/resolver/ to override DNS resolution for specific domains. Corral creates:
# /etc/resolver/test
nameserver 127.0.0.1
port 5353
This tells macOS to send all .test DNS queries to dnsmasq on 127.0.0.1:5353. The file is created when infrastructure starts and removed when it stops.
Troubleshooting
Domain doesn’t resolve
- Check that infrastructure is running:
corral infra status - Verify the resolver file exists:
cat /etc/resolver/test - Check that the privileged helper is installed (Settings → Infrastructure → Background Service)
- Try flushing the DNS cache:
sudo dscacheutil -flushcache; sudo killall -HUP mDNSResponder
Port 80 or 443 in use
If another service (like Apache or nginx) is using ports 80 or 443, Caddy won’t be able to start. Stop the conflicting service before starting Corral’s infrastructure.
”Blocked request” or “This host is not allowed”
This is a framework security feature, not a Corral issue. Corral automatically allows .test domains for Vite-based frameworks. For Next.js, add allowedDevOrigins: ["my-app.test"] to your next.config.js.