Juggling multiple Vite apps and can't remember which port is which?
Stop playing the localhost lottery. This plugin automatically assigns memorable domains to each of your projects — derived from the folder name or package.json — so localhost:5173, localhost:5174, and localhost:5175 become frontend.local, admin.local, and api.local.
The plugin automatically:
- Configures a Caddy reverse proxy with HTTPS (via the internal issuer)
- Routes your domain to whatever port Vite picks
- Generates domain names from your folder or package.json
- Shares one Caddy instance across all your projects
pnpm add -D vite-plugin-domain- Install Caddy for your platform
- Trust Caddy's local CA (one-time setup):
sudo caddy trust
- Start Caddy with the admin API enabled:
The admin API runs on
caddy run
http://127.0.0.1:2019by default. The plugin uses this API to configure domains dynamically.
Add the plugin to your vite.config.ts:
import { defineConfig } from 'vite'
import domain from 'vite-plugin-domain'
export default defineConfig({
plugins: [
domain({
// All options are optional with sensible defaults:
adminUrl: 'http://127.0.0.1:2019', // Caddy admin API endpoint
serverId: 'vite-dev', // Caddy server identifier
listen: [':443', ':80'], // Ports Caddy should listen on
nameSource: 'folder', // Use folder name for domain ('folder' | 'pkg')
tld: 'local', // Top-level domain suffix
// domain: 'myapp.local', // Explicit domain (overrides nameSource+tld)
failOnActiveDomain: true, // Fail if domain already has an active route
insertFirst: true, // Insert new route at top of route list
verbose: false, // Enable detailed logging
})
],
server: {
// Required for .local domains:
allowedHosts: ['.local'],
}
})When you start your Vite dev server:
- The plugin connects to Caddy's admin API
- Creates or updates a Caddy server configuration
- Adds a route from your domain to Vite's dev server port
- Prints the URL where you can access your app
By default, the plugin generates a domain based on:
- Folder name (
nameSource: 'folder') — Uses the current directory name - Package name (
nameSource: 'pkg') — Uses thenamefield frompackage.json
The generated domain follows the pattern: {name}.{tld}
Override automatic naming by specifying an explicit domain:
domain({ domain: 'my-custom-app.local' })Shorter and cleaner, but requires one-time setup:
-
Add to Vite's allowed hosts:
server: { allowedHosts: ['.local'] }
-
Add an entry to
/etc/hosts:sudo bash -c "echo '127.0.0.1 myapp.local' >> /etc/hosts"Note: Some networks use
.localfor mDNS. The explicit hosts entry ensures local resolution.
Works without additional setup in most browsers:
domain({ tld: 'localhost' })Browsers typically resolve *.localhost to 127.0.0.1 automatically. If Vite blocks it, add to allowed hosts:
server: { allowedHosts: ['.localhost'] }Run several Vite projects simultaneously with different domains:
// Project A: vite.config.ts
domain({ domain: 'frontend.local' })
// Project B: vite.config.ts
domain({ domain: 'admin.local' })
// Project C: vite.config.ts
domain({ domain: 'api.local' })All three projects can run concurrently, each accessible via its own domain, all routing through the same Caddy instance.
If you need different Caddy server settings per project:
domain({
serverId: 'my-project-server',
listen: [':8443', ':8080'], // Custom ports
adminUrl: 'http://127.0.0.1:2019'
})Enable verbose logging to troubleshoot issues:
domain({ verbose: true })- Ensure Caddy is running:
caddy run - Check the domain resolves:
ping myapp.local - Verify
/etc/hostsentry exists for.localdomains
- Run
sudo caddy trustto install Caddy's local CA - Restart your browser after trusting the certificate
- Another project is using this domain
- Either stop the other project or use a different domain
- Or set
failOnActiveDomain: falseto override (use with caution)
- Add your TLD to Vite's allowed hosts:
server: { allowedHosts: ['.local'] }
MIT
