implementing geoIP filtering with Caddy

it is becoming increasingly common nowadays for governments to implement poorly-conceived policies requiring websites to obtain a copy of the user's government-provided ID card in order to access them, if the content contained is of a pornographic or otherwise explicit nature. of course, it is practically impossible to do this without severely violating people's privacy, not to mention the costs involved in running, maintaining, or otherwise accessing such a system. if you yourself as webmaster do not live in one of the places with such legislation, nor host your server in one, it is easier to simply block traffic instead, and avoid the risk of lawsuits or fines.

it is, of course, worth remembering that even matching an IP address to a location is a terrible hack at best, and little more than educated guesses at the worst; although in most cases they are accurate enough to pinpoint a user's legal jurisdiction. still, it is the only way to roughly estimate where a user might be without having to ask them any invasive questions, and functions as the industry standard method for doing so.


caddy has a plugin system, but it requires compiling from source to use it. to use it, install xCaddy and make sure you can build with it. if building doesn't work at first, make sure you have the latest version of Go installed; some distros package older versions which can't build xCaddy correctly. once you're ready, compile Caddy with the caddy-maxmind-geolocation plugin as follows:

xcaddy build --with

you'll then need to install your custom Caddy build. as taken from the official documentation:

dpkg-divert --divert /usr/bin/caddy.default --rename /usr/bin/caddy
mv ./caddy /usr/bin/caddy.custom
update-alternatives --install /usr/bin/caddy caddy /usr/bin/caddy.default 10
update-alternatives --install /usr/bin/caddy caddy /usr/bin/caddy.custom 50
systemctl restart caddy

those commands will allow you to switch between your custom build and the distro's build using update-alternatives, and set your build as default. don't forget to run them as root!


you will, of course, need a copy of MaxMind's GeoLite2 City database in order for this to work. simply register for a free account on their website, log in, and then go to the downloads section to get it. while I would prefer a fully open solution, this is the only practical option I know of as of writing this post.

note that you will need the City database to block by US state, as the Country database does not have those subdivisions.


to actually block the right places, we need to tell Caddy how to do it.

@geoip {
	path /nsfw*
	maxmind_geolocation {
		db_path "/opt/geoip/GeoLite2-City.mmdb"
		allow_subdivisions AR LA MS MT TX NC UT VA
handle @geoip {
	error 451

the provided snippet sets up a matcher block for anything which both access the /nsfw* section, and is in the states listed by allow_subdivisions.1 note that allow_subdivisions is a positive matcher, which in this case is the one we want, despite that we are using it for blocking. that is because we use the handle directive to direct all matching requests to an 451 error page.

once everything has been put into place, make sure to reload Caddy's configuration. and as a bonus tip, to test if the block is working, simply place place the state you live in on the list of provided states to block. just make sure to remove it once you're done!

1. the states listed (Arkansas, Louisiana, Mississippi, Montana, Texas, North Carolina, Utah, and Virginia) were picked, as at the time of this writing — to the best of my knowledge, at least — they all have such age verification laws for explicit content online.

topics annoyance

your browser is stalking you.

this website is best viewed in browsers which do not implement the Topics API, such as Firefox.