Using Caddy in front of iocaine
Last updated: 2025-11-16

# Getting started

In here, we’ll assume that `iocaine` has already been set up, and is accepting HTTP connections on port 42069 of `127.0.0.1`. See the Getting Started guides to get there quickly!

Lets also assume that we have a site running at `[::1]:8080`, and we want to serve that with `Caddy`. Normally, that would look something like this:

    blog.example.com {
      reverse_proxy [::1]:8080
    }

Or perhaps even:

    blog.example.com {
      root * /srv/www/blog.example.com
      file_server
    }

# Integrating iocaine

`iocaine` expects us to pipe everything through it, and it will send back a result: either a HTTP 421 Misdirected Request response to signal that we should serve real contents, or any other status, which we should serve as is.

As such, we’ll have to wrap the above example to match this expectation:

    blog.example.com {
      @read method GET HEAD
      reverse_proxy @read 127.0.0.1:42069 {
        @fallback status 421
        handle_response @fallback
      }
      reverse_proxy [::1]:8080
    }

The `handle_response` directive is essentially a fallthrough, so we can conveniently place the `iocaine` filter before any handlers that respond, and we’re good to go. If serving files directly, we’ll have to adjust this wrapping accordingly:

    blog.example.com {
      @read method GET HEAD
      reverse_proxy @read 127.0.0.1:42069 {
        @fallback status 421
        handle_response @fallback
      }
      root * /srv/www/blog.example.com
      file_server
    }

As you can see, we’re only proxying `GET` and `HEAD` requests to `iocaine` - most unwanted bots don’t send a body anyway, nor requests other than `GET` and `HEAD`, so for now, this will do.

# A reusable snippet

Chances are, there’s more than one site we wish to protect with iocaine. Repeating the above boilerplate over and over again would be both mindnumbingly boring, entirely pointless, and also completely unnecessary, because Caddy lets us set up reusable snippets. Like this:

    (iocaine) {
      @read method GET HEAD
      reverse_proxy @read 127.0.0.1:42069 {
        @fallback status 421
        handle_response @fallback
      }
    }

Rewriting the `blog.example.com` site’s declaration to use this makes it look like so:

    blog.example.com {
      import iocaine
      reverse_proxy [::1]:8000
    }

Similarly, for a static blog:

    blog.example.com {
      import iocaine
      root * /srv/www/blog.example.com
      file_server
    }

The only thing you need to pay attention to is directive ordering: some directives, such as `respond` are ordered before `reverse_proxy` by default. If you use those directives, they will not go through `iocaine`, unless you reorder them:

    {
      order respond after reverse_proxy
    }