Getting started
In here, I assume that iocaine has already been configured and deployed, using a custom request handler. Lets 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
}
Delegating decisions to iocaine
The modern way of running iocaine is to let it do the decision: pipe everything through to it, and let it decide what to do with the request. It will either serve it garbage or a challenge (and respond to the reverse proxy with a HTTP 200), or it will signal the reverse proxy to serve the real deal (by sending it a HTTP 421 response).
As such, we’ll have to wrap the above to match this expectation:
blog.example.com {
reverse_proxy 127.0.0.1:42069 {
@fallback status 421
handle_response @fallback {
reverse_proxy [::1]:8080
}
}
}
Delegating some decisions to iocaine
While the above method works for static sites, proxying request bodies is expensive, and most of the time, pointless. As such, to make the setup both more efficient, and more reliable, we should only send GET or HEAD requests to iocaine.
blog.example.com {
@read method GET HEAD
@not-read not {
method GET HEAD
}
reverse_proxy @read 127.0.0.1:42069 {
@fallback status 421
handle_response @fallback {
reverse_proxy [::1]:8080
}
}
handle @not-read {
reverse_proxy [::1]:8080
}
}
Of course, if you’re putting this in front of a static site where only GET and HEAD make sense in the first place, then there’s no need for the second reverse_proxy: we can tell Caddy to return HTTP 405 “Method Not Allowed”:
blog.example.com {
@read method GET HEAD
@not-read not {
method GET HEAD
}
reverse_proxy @read 127.0.0.1:42069 {
@fallback status 421
handle_response @fallback {
reverse_proxy [::1]:8080
}
}
handle @not-read {
respond 405
}
}
A reusable snippet
Important
The Caddy configuration shown here requires Caddy 2.9.x or newer.
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 mind numbingly boring, entirely pointless, and also completely unnecessary, because Caddy lets us set up reusable snippets. Like this:
(iocaine) {
@read method GET HEAD
@not-read not {
method GET HEAD
}
reverse_proxy @read 127.0.0.1:42069 {
@fallback status 421
handle_response @fallback {
{blocks.handler}
}
}
handle @not-read {
{blocks.default}
}
}
Rewriting the blog.example.com site’s declaration to use this makes it look like so:
blog.example.com {
import iocaine {
handler {
reverse_proxy [::1]:8000
}
default {
respond 405
}
}
}
Still more verbose than a single reverse_proxy line, but only marginally, and this setup is flexible enough to allow all kinds of configurations. You can put any directive you wish inside those handler and default blocks.