Setting Up a Custom Caddy Reverse Proxy for OpenClaw on macOS
A step-by-step guide for macOS users running OpenClaw to set up a custom Caddy server as a reverse proxy — covering mise installation, building Caddy with xcaddy and custom plugins, configuring GeoIP filtering with basic auth, and running it as a launchd service.
1. Installing mise
mise (formerly rtx) is a polyglot runtime manager. Install it and activate it in your shell:
curl https://mise.run | sh
export PATH="$HOME/.local/bin:$PATH"
eval "$(mise activate bash)"
For other shells, see the mise documentation.
2. Installing xcaddy with mise
xcaddy is the tool for building Caddy with custom modules (GeoIP, rate limiting, etc.). You can install it via mise's github backend, which pulls binaries directly from GitHub releases.
# Pin to a specific version
mise use -g github:caddyserver/xcaddy@0.4.5
# Or use the latest version
mise use -g github:caddyserver/xcaddy
The github backend offers:
- Provenance verification — validates release authenticity
- Download progress reports — visual feedback during install
Verify the installation:
xcaddy version
3. Finding Custom Caddy Plugins
Before building, you need to know which plugins are available. Caddy provides two official resources:
Modules Directory
https://caddyserver.com/docs/modules/
The complete registry of all registered Caddy modules. Each entry shows the module ID, description, and links to its source repository. Modules marked as non-standard are community plugins that need to be added via xcaddy. Use your browser's "Find in page" for quick lookups.
Download Page
https://caddyserver.com/download
An interactive build page that lists all available plugins with checkboxes. You can:
- Browse and search all available plugins by name or category
- Select plugins you need and download a pre-built binary directly
-
Copy the
xcaddy buildcommand with all your selected--withflags
Tip: Use the modules directory to research what's available, then use the download page to either grab a pre-built binary or copy the exact
xcaddy buildcommand.
4. Getting the GeoLite2 Database
The GeoIP filtering requires a MaxMind GeoLite2 ASN database file. Sign up for a free account at maxmind.com, then download the GeoLite2-ASN.mmdb file from your account dashboard under Download Databases.
# Place the database where your Caddyfile can reference it
mv GeoLite2-ASN.mmdb ~/GeoLite2-ASN.mmdb
Tip: MaxMind also provides
geoipupdate— a tool that keeps your database files up to date automatically. Install withbrew install geoipupdateand configure it with your MaxMind license key.
5. Building Caddy with Custom Modules
With xcaddy installed, build a custom Caddy binary with the modules you need:
xcaddy build v2.11.2 \
--with github.com/porech/caddy-maxmind-geolocation@v1.0.3
This produces a caddy binary in the current directory with MaxMind GeoIP support baked in.
Move it to your PATH:
mv caddy /usr/local/bin/caddy
6. Running Caddy as a macOS Service
Create a launchd plist to run Caddy automatically on boot:
tee ~/Library/LaunchAgents/com.caddyserver.caddy.plist << 'EOF'
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.caddyserver.caddy</string>
<key>ProgramArguments</key>
<array>
<string>/usr/local/bin/caddy</string>
<string>run</string>
<string>--config</string>
<string>/Users/username/.config/caddy/Caddyfile</string>
</array>
<key>RunAtLoad</key>
<true/>
<key>KeepAlive</key>
<true/>
<key>StandardOutPath</key>
<string>/Users/username/.config/caddy/caddy-stdout.log</string>
<key>StandardErrorPath</key>
<string>/Users/username/.config/caddy/caddy-stderr.log</string>
</dict>
</plist>
EOF
Managing the Service
# Start Caddy service
launchctl load -w ~/Library/LaunchAgents/com.caddyserver.caddy.plist
# Stop Caddy service
launchctl unload -w ~/Library/LaunchAgents/com.caddyserver.caddy.plist
# Reload config without restarting the service
caddy reload --config ~/.config/caddy/Caddyfile
7. Production Caddyfile: GeoIP, Basic Auth, and Reverse Proxy
Generating a Password Hash
Caddy's basic_auth directive requires a bcrypt-hashed password. Generate one with:
caddy hash-password
This will prompt you to enter and confirm a password, then output a bcrypt hash like:
$2a$14$Mpdg/pw/CBar4/YMFRcwbuN2gWsOQgaCowWDHPHsc402vF18TRGRK
Use this hash in your Caddyfile's basic_auth block.
Example Caddyfile
Here's a real-world Caddyfile serving two sites with:
- MaxMind GeoIP ASN filtering — only allow traffic from specific ISPs/networks
- Basic authentication — password-protect the proxy
- Reverse proxying — forward traffic to backend services
- Separate handling for webhook endpoints (no auth required)
site1.example.com {
log {
output file /Users/username/.config/caddy/site1.example.com-caddy.log
}
@allowed {
maxmind_geolocation {
db_path "/Users/username/GeoLite2-ASN.mmdb"
allow_asn 48323 21497 59497 58309
}
}
handle /googlechat {
reverse_proxy 192.168.111.98:18789
}
handle {
basic_auth @allowed {
admin $2a$14$...
}
reverse_proxy @allowed 192.168.111.98:18789
respond 501
}
}
site2.example.com {
log {
output file /Users/username/.config/caddy/site2.example.com-caddy.log
}
@allowed {
maxmind_geolocation {
db_path "/Users/username/GeoLite2-ASN.mmdb"
allow_asn 48323 21497 59497 58309
}
}
basic_auth @allowed {
admin $2a$14$...
}
reverse_proxy @allowed 127.0.0.1:3644
respond 501
}
How the Request Flow Works
-
GeoIP check (
@allowedmatcher) — only ASNs 48323, 21497, 59497, and 58309 can proceed past basic auth. All others get therespond 501. -
Basic auth — only requests matching
@allowedare prompted for credentials. - Reverse proxy — authenticated + allowed requests are forwarded to the backend.
- Fallback — anything that doesn't match gets a 501 response.
The Webhook Exception
The /googlechat path is handled separately with its own handle block, bypassing auth. This is essential for incoming webhooks that can't provide credentials.
Quick Reference
# Install mise
curl https://mise.run | sh
export PATH="$HOME/.local/bin:$PATH"
eval "$(mise activate bash)"
# Install xcaddy
mise use -g github:caddyserver/xcaddy@0.4.5
# Build custom Caddy
xcaddy build v2.11.2 \
--with github.com/porech/caddy-maxmind-geolocation@v1.0.3
# Install the binary
mv caddy /usr/local/bin/caddy
# Generate password hash for basic_auth
caddy hash-password
# macOS service management
launchctl load -w ~/Library/LaunchAgents/com.caddyserver.caddy.plist
launchctl unload -w ~/Library/LaunchAgents/com.caddyserver.caddy.plist
# Manage Caddyfile
caddy validate --config ~/.config/caddy/Caddyfile
caddy reload --config ~/.config/caddy/Caddyfile
caddy fmt --overwrite ~/.config/caddy/Caddyfile
Useful links:
Top comments (0)