<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Vasyl Herman</title>
    <description>The latest articles on DEV Community by Vasyl Herman (@vasyl_herman).</description>
    <link>https://dev.to/vasyl_herman</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1712353%2Fd38390e2-663e-4ed5-8b9b-24ec68c4b477.jpg</url>
      <title>DEV Community: Vasyl Herman</title>
      <link>https://dev.to/vasyl_herman</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/vasyl_herman"/>
    <language>en</language>
    <item>
      <title>Setting Up a Custom Caddy Reverse Proxy for OpenClaw on macOS</title>
      <dc:creator>Vasyl Herman</dc:creator>
      <pubDate>Sun, 12 Apr 2026 15:24:35 +0000</pubDate>
      <link>https://dev.to/vasyl_herman/setting-up-a-custom-caddy-reverse-proxy-for-openclaw-on-macos-524p</link>
      <guid>https://dev.to/vasyl_herman/setting-up-a-custom-caddy-reverse-proxy-for-openclaw-on-macos-524p</guid>
      <description>&lt;h1&gt;
  
  
  Setting Up a Custom Caddy Reverse Proxy for OpenClaw on macOS
&lt;/h1&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Installing mise
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://mise.jdx.dev/" rel="noopener noreferrer"&gt;mise&lt;/a&gt; (formerly rtx) is a polyglot runtime manager. Install it and activate it in your shell:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl https://mise.run | sh
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/.local/bin:&lt;/span&gt;&lt;span class="nv"&gt;$PATH&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nb"&gt;eval&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;mise activate bash&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For other shells, see the &lt;a href="https://mise.jdx.dev/" rel="noopener noreferrer"&gt;mise documentation&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Installing xcaddy with mise
&lt;/h2&gt;

&lt;p&gt;xcaddy is the tool for building Caddy with custom modules (GeoIP, rate limiting, etc.). You can install it via mise's &lt;strong&gt;github backend&lt;/strong&gt;, which pulls binaries directly from GitHub releases.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Pin to a specific version&lt;/span&gt;
mise use &lt;span class="nt"&gt;-g&lt;/span&gt; github:caddyserver/xcaddy@0.4.5

&lt;span class="c"&gt;# Or use the latest version&lt;/span&gt;
mise use &lt;span class="nt"&gt;-g&lt;/span&gt; github:caddyserver/xcaddy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;github&lt;/code&gt; backend offers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Provenance verification&lt;/strong&gt; — validates release authenticity&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Download progress reports&lt;/strong&gt; — visual feedback during install&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Verify the installation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;xcaddy version
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  3. Finding Custom Caddy Plugins
&lt;/h2&gt;

&lt;p&gt;Before building, you need to know which plugins are available. Caddy provides two official resources:&lt;/p&gt;

&lt;h3&gt;
  
  
  Modules Directory
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://caddyserver.com/docs/modules/" rel="noopener noreferrer"&gt;https://caddyserver.com/docs/modules/&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;h3&gt;
  
  
  Download Page
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://caddyserver.com/download" rel="noopener noreferrer"&gt;https://caddyserver.com/download&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;An interactive build page that lists all available plugins with checkboxes. You can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Browse and search&lt;/strong&gt; all available plugins by name or category&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Select plugins&lt;/strong&gt; you need and download a pre-built binary directly&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Copy the &lt;code&gt;xcaddy build&lt;/code&gt; command&lt;/strong&gt; with all your selected &lt;code&gt;--with&lt;/code&gt; flags&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; Use the &lt;a href="https://caddyserver.com/docs/modules/" rel="noopener noreferrer"&gt;modules directory&lt;/a&gt; to research what's available, then use the &lt;a href="https://caddyserver.com/download" rel="noopener noreferrer"&gt;download page&lt;/a&gt; to either grab a pre-built binary or copy the exact &lt;code&gt;xcaddy build&lt;/code&gt; command.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  4. Getting the GeoLite2 Database
&lt;/h2&gt;

&lt;p&gt;The GeoIP filtering requires a MaxMind GeoLite2 ASN database file. Sign up for a free account at &lt;a href="https://www.maxmind.com/en/geolite2/signup" rel="noopener noreferrer"&gt;maxmind.com&lt;/a&gt;, then download the &lt;code&gt;GeoLite2-ASN.mmdb&lt;/code&gt; file from your account dashboard under &lt;strong&gt;Download Databases&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Place the database where your Caddyfile can reference it&lt;/span&gt;
&lt;span class="nb"&gt;mv &lt;/span&gt;GeoLite2-ASN.mmdb ~/GeoLite2-ASN.mmdb
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; MaxMind also provides &lt;a href="https://github.com/maxmind/geoipupdate" rel="noopener noreferrer"&gt;&lt;code&gt;geoipupdate&lt;/code&gt;&lt;/a&gt; — a tool that keeps your database files up to date automatically. Install with &lt;code&gt;brew install geoipupdate&lt;/code&gt; and configure it with your MaxMind license key.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  5. Building Caddy with Custom Modules
&lt;/h2&gt;

&lt;p&gt;With xcaddy installed, build a custom Caddy binary with the modules you need:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;xcaddy build v2.11.2 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--with&lt;/span&gt; github.com/porech/caddy-maxmind-geolocation@v1.0.3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This produces a &lt;code&gt;caddy&lt;/code&gt; binary in the current directory with MaxMind GeoIP support baked in.&lt;/p&gt;

&lt;p&gt;Move it to your PATH:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mv &lt;/span&gt;caddy /usr/local/bin/caddy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  6. Running Caddy as a macOS Service
&lt;/h2&gt;

&lt;p&gt;Create a launchd plist to run Caddy automatically on boot:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;tee&lt;/span&gt; ~/Library/LaunchAgents/com.caddyserver.caddy.plist &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;'
&amp;lt;?xml version="1.0" encoding="UTF-8"?&amp;gt;
&amp;lt;!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"&amp;gt;
&amp;lt;plist version="1.0"&amp;gt;
&amp;lt;dict&amp;gt;
    &amp;lt;key&amp;gt;Label&amp;lt;/key&amp;gt;
    &amp;lt;string&amp;gt;com.caddyserver.caddy&amp;lt;/string&amp;gt;
    &amp;lt;key&amp;gt;ProgramArguments&amp;lt;/key&amp;gt;
    &amp;lt;array&amp;gt;
        &amp;lt;string&amp;gt;/usr/local/bin/caddy&amp;lt;/string&amp;gt;
        &amp;lt;string&amp;gt;run&amp;lt;/string&amp;gt;
        &amp;lt;string&amp;gt;--config&amp;lt;/string&amp;gt;
        &amp;lt;string&amp;gt;/Users/username/.config/caddy/Caddyfile&amp;lt;/string&amp;gt;
    &amp;lt;/array&amp;gt;
    &amp;lt;key&amp;gt;RunAtLoad&amp;lt;/key&amp;gt;
    &amp;lt;true/&amp;gt;
    &amp;lt;key&amp;gt;KeepAlive&amp;lt;/key&amp;gt;
    &amp;lt;true/&amp;gt;
    &amp;lt;key&amp;gt;StandardOutPath&amp;lt;/key&amp;gt;
    &amp;lt;string&amp;gt;/Users/username/.config/caddy/caddy-stdout.log&amp;lt;/string&amp;gt;
    &amp;lt;key&amp;gt;StandardErrorPath&amp;lt;/key&amp;gt;
    &amp;lt;string&amp;gt;/Users/username/.config/caddy/caddy-stderr.log&amp;lt;/string&amp;gt;
&amp;lt;/dict&amp;gt;
&amp;lt;/plist&amp;gt;
&lt;/span&gt;&lt;span class="no"&gt;EOF
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Managing the Service
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Start Caddy service&lt;/span&gt;
launchctl load &lt;span class="nt"&gt;-w&lt;/span&gt; ~/Library/LaunchAgents/com.caddyserver.caddy.plist

&lt;span class="c"&gt;# Stop Caddy service&lt;/span&gt;
launchctl unload &lt;span class="nt"&gt;-w&lt;/span&gt; ~/Library/LaunchAgents/com.caddyserver.caddy.plist

&lt;span class="c"&gt;# Reload config without restarting the service&lt;/span&gt;
caddy reload &lt;span class="nt"&gt;--config&lt;/span&gt; ~/.config/caddy/Caddyfile
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  7. Production Caddyfile: GeoIP, Basic Auth, and Reverse Proxy
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Generating a Password Hash
&lt;/h3&gt;

&lt;p&gt;Caddy's &lt;code&gt;basic_auth&lt;/code&gt; directive requires a bcrypt-hashed password. Generate one with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;caddy hash-password
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will prompt you to enter and confirm a password, then output a bcrypt hash like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$2a$14$Mpdg/pw/CBar4/YMFRcwbuN2gWsOQgaCowWDHPHsc402vF18TRGRK
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use this hash in your Caddyfile's &lt;code&gt;basic_auth&lt;/code&gt; block.&lt;/p&gt;

&lt;h3&gt;
  
  
  Example Caddyfile
&lt;/h3&gt;

&lt;p&gt;Here's a real-world Caddyfile serving two sites with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;MaxMind GeoIP ASN filtering&lt;/strong&gt; — only allow traffic from specific ISPs/networks&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Basic authentication&lt;/strong&gt; — password-protect the proxy&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reverse proxying&lt;/strong&gt; — forward traffic to backend services&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Separate handling&lt;/strong&gt; for webhook endpoints (no auth required)
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nginx"&gt;&lt;code&gt;&lt;span class="k"&gt;site1.example.com&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kn"&gt;log&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kn"&gt;output&lt;/span&gt; &lt;span class="s"&gt;file&lt;/span&gt; &lt;span class="n"&gt;/Users/username/.config/caddy/site1.example.com-caddy.log&lt;/span&gt;
    &lt;span class="err"&gt;}&lt;/span&gt;

    &lt;span class="s"&gt;@allowed&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kn"&gt;maxmind_geolocation&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kn"&gt;db_path&lt;/span&gt; &lt;span class="s"&gt;"/Users/username/GeoLite2-ASN.mmdb"&lt;/span&gt;
            &lt;span class="s"&gt;allow_asn&lt;/span&gt; &lt;span class="mi"&gt;48323&lt;/span&gt; &lt;span class="mi"&gt;21497&lt;/span&gt; &lt;span class="mi"&gt;59497&lt;/span&gt; &lt;span class="mi"&gt;58309&lt;/span&gt;
        &lt;span class="err"&gt;}&lt;/span&gt;
    &lt;span class="err"&gt;}&lt;/span&gt;

    &lt;span class="s"&gt;handle&lt;/span&gt; &lt;span class="n"&gt;/googlechat&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kn"&gt;reverse_proxy&lt;/span&gt; &lt;span class="nf"&gt;192.168.111.98&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;18789&lt;/span&gt;
    &lt;span class="err"&gt;}&lt;/span&gt;

    &lt;span class="s"&gt;handle&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kn"&gt;basic_auth&lt;/span&gt; &lt;span class="s"&gt;@allowed&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kn"&gt;admin&lt;/span&gt; &lt;span class="nv"&gt;$2a$14&lt;/span&gt;$&lt;span class="s"&gt;...&lt;/span&gt;
        &lt;span class="err"&gt;}&lt;/span&gt;
        &lt;span class="s"&gt;reverse_proxy&lt;/span&gt; &lt;span class="s"&gt;@allowed&lt;/span&gt; &lt;span class="nf"&gt;192.168.111.98&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;18789&lt;/span&gt;
        &lt;span class="s"&gt;respond&lt;/span&gt; &lt;span class="mi"&gt;501&lt;/span&gt;
    &lt;span class="err"&gt;}&lt;/span&gt;
&lt;span class="err"&gt;}&lt;/span&gt;

&lt;span class="s"&gt;site2.example.com&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kn"&gt;log&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kn"&gt;output&lt;/span&gt; &lt;span class="s"&gt;file&lt;/span&gt; &lt;span class="n"&gt;/Users/username/.config/caddy/site2.example.com-caddy.log&lt;/span&gt;
    &lt;span class="err"&gt;}&lt;/span&gt;

    &lt;span class="s"&gt;@allowed&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kn"&gt;maxmind_geolocation&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kn"&gt;db_path&lt;/span&gt; &lt;span class="s"&gt;"/Users/username/GeoLite2-ASN.mmdb"&lt;/span&gt;
            &lt;span class="s"&gt;allow_asn&lt;/span&gt; &lt;span class="mi"&gt;48323&lt;/span&gt; &lt;span class="mi"&gt;21497&lt;/span&gt; &lt;span class="mi"&gt;59497&lt;/span&gt; &lt;span class="mi"&gt;58309&lt;/span&gt;
        &lt;span class="err"&gt;}&lt;/span&gt;
    &lt;span class="err"&gt;}&lt;/span&gt;

    &lt;span class="s"&gt;basic_auth&lt;/span&gt; &lt;span class="s"&gt;@allowed&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kn"&gt;admin&lt;/span&gt; &lt;span class="nv"&gt;$2a$14&lt;/span&gt;$&lt;span class="s"&gt;...&lt;/span&gt;
    &lt;span class="err"&gt;}&lt;/span&gt;
    &lt;span class="s"&gt;reverse_proxy&lt;/span&gt; &lt;span class="s"&gt;@allowed&lt;/span&gt; &lt;span class="nf"&gt;127.0.0.1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;3644&lt;/span&gt;
    &lt;span class="s"&gt;respond&lt;/span&gt; &lt;span class="mi"&gt;501&lt;/span&gt;
&lt;span class="err"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  How the Request Flow Works
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;GeoIP check&lt;/strong&gt; (&lt;code&gt;@allowed&lt;/code&gt; matcher) — only ASNs 48323, 21497, 59497, and 58309 can proceed past basic auth. All others get the &lt;code&gt;respond 501&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Basic auth&lt;/strong&gt; — only requests matching &lt;code&gt;@allowed&lt;/code&gt; are prompted for credentials.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reverse proxy&lt;/strong&gt; — authenticated + allowed requests are forwarded to the backend.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fallback&lt;/strong&gt; — anything that doesn't match gets a 501 response.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  The Webhook Exception
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;/googlechat&lt;/code&gt; path is handled separately with its own &lt;code&gt;handle&lt;/code&gt; block, bypassing auth. This is essential for incoming webhooks that can't provide credentials.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quick Reference
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Install mise&lt;/span&gt;
curl https://mise.run | sh
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/.local/bin:&lt;/span&gt;&lt;span class="nv"&gt;$PATH&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nb"&gt;eval&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;mise activate bash&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="c"&gt;# Install xcaddy&lt;/span&gt;
mise use &lt;span class="nt"&gt;-g&lt;/span&gt; github:caddyserver/xcaddy@0.4.5

&lt;span class="c"&gt;# Build custom Caddy&lt;/span&gt;
xcaddy build v2.11.2 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--with&lt;/span&gt; github.com/porech/caddy-maxmind-geolocation@v1.0.3

&lt;span class="c"&gt;# Install the binary&lt;/span&gt;
&lt;span class="nb"&gt;mv &lt;/span&gt;caddy /usr/local/bin/caddy

&lt;span class="c"&gt;# Generate password hash for basic_auth&lt;/span&gt;
caddy hash-password

&lt;span class="c"&gt;# macOS service management&lt;/span&gt;
launchctl load &lt;span class="nt"&gt;-w&lt;/span&gt; ~/Library/LaunchAgents/com.caddyserver.caddy.plist
launchctl unload &lt;span class="nt"&gt;-w&lt;/span&gt; ~/Library/LaunchAgents/com.caddyserver.caddy.plist

&lt;span class="c"&gt;# Manage Caddyfile&lt;/span&gt;
caddy validate &lt;span class="nt"&gt;--config&lt;/span&gt; ~/.config/caddy/Caddyfile
caddy reload &lt;span class="nt"&gt;--config&lt;/span&gt; ~/.config/caddy/Caddyfile
caddy &lt;span class="nb"&gt;fmt&lt;/span&gt; &lt;span class="nt"&gt;--overwrite&lt;/span&gt; ~/.config/caddy/Caddyfile
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Useful links:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://mise.jdx.dev/" rel="noopener noreferrer"&gt;mise documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://caddyserver.com/docs/modules/" rel="noopener noreferrer"&gt;Caddy modules directory&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://caddyserver.com/download" rel="noopener noreferrer"&gt;Caddy download / plugin picker&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>caddy</category>
      <category>macos</category>
      <category>openclaw</category>
      <category>devops</category>
    </item>
  </channel>
</rss>
