Caddyfile Support

Although the native configuration language of Caddy is JSON, Caddy is known for its Caddyfile configuration format, which is more human-friendly. An example of a production-ready Caddyfile is the one I use for my own blog (sans the logging configuration):

www.example.com, example.com, localhost {
	root * /var/www/public
	file_server
	encode gzip
}

Another Caddyfile is:

www.example.com, example.com, localhost {
	@health {
		path /health
	}
	respond @health {
		body "ok"
	}

	@notbrowser {
		not {
			header_regexp (?i)mozilla/(?P<version>\d+\.\d+)
		}
	}

	respond @notbrowser {
		body "Go away, bot!"
		status 403
	}

	file_server /blog*
}

Thus supporting Caddyfile for your module is a great UX addition and likely increases adoption. Inspired by the idiomatic Go-way of supporting format marshalling, the best practice for supporting Caddyfile is done by implementing the caddyfile.Unmarshaler 1 interface and calling the respective function:

  • httpcaddyfile.RegisterDirective(dir string, setupFunc UnmarshalFunc) 2
  • httpcaddyfile.RegisterGlobalOption(opt string, setupFunc UnmarshalGlobalFunc) 2
  • httpcaddyfile.RegisterHandlerDirective(dir string, setupFunc UnmarshalHandlerFunc) 2
type Unmarshaler interface {
	UnmarshalCaddyfile(d *Dispenser) error
}

Merely implementing the interface allows other modules that host your module and know your full module ID to be able to load your module. If your module is an HTTP handler, it’s best to call httpcaddyfile.RegisterHandlerDirective 2 to have support of matchers automagically added to your directive rather than merely calling httpcaddyfile.RegisterDirective 2.

Exercise

Add Caddyfile support to your module, including support for matchers

Remember: Use xcaddy list-modules and xcaddy run --config <config_file> --adapter caddyfile to test your implementation

Use this Caddyfile to test the implementation:

localhost {
	route {
		dump_headers /debug*
		respond "Hello, world!"
	}
}