Use this file to discover all available pages before exploring further.
Caddy’s module system is the foundation of its extensibility. Every component in Caddy—from HTTP handlers to TLS certificate issuers—is implemented as a module. This architecture allows you to extend Caddy with custom functionality or use third-party modules.
A module is any Go type that implements the Module interface:
modules.go:54-60
type Module interface { // This method indicates that the type is a Caddy module. // The returned ModuleInfo must have both a name and a constructor function. CaddyModule() ModuleInfo}
type ModuleInfo struct { // ID is the "full name" of the module. // It must be unique and properly namespaced. ID ModuleID // New returns a pointer to a new, empty instance of the module's type. // This method must not have any side-effects. New func() Module}
Module IDs follow a hierarchical naming convention:
modules.go:79-98
// ModuleID is a string that uniquely identifies a Caddy module.// It consists of dot-separated labels which form a simple hierarchy.//// Examples of valid IDs:// - http// - http.handlers.file_server// - caddy.logging.encoders.jsontype ModuleID string
Namespace structure:<namespace>.<name>Top-level modules (apps) have no namespace, just a name like http or tls.
Modules must be registered before Caddy can use them:
modules.go:130-161
func RegisterModule(instance Module) { mod := instance.CaddyModule() if mod.ID == "" { panic("module ID missing") } if mod.ID == "caddy" || mod.ID == "admin" { panic(fmt.Sprintf("module ID '%s' is reserved", mod.ID)) } if mod.New == nil { panic("missing ModuleInfo.New") } if val := mod.New(); val == nil { panic("ModuleInfo.New must return a non-nil module instance") } modulesMu.Lock() defer modulesMu.Unlock() if _, ok := modules[string(mod.ID)]; ok { panic(fmt.Sprintf("module already registered: %s", mod.ID)) } modules[string(mod.ID)] = mod}
Module registration typically happens in init() functions and will panic if:
If the module implements Provisioner, its Provision() method is called:
modules.go:288-298
type Provisioner interface { Provision(Context) error}
context.go:418-430
if prov, ok := val.(Provisioner); ok { err = prov.Provision(ctx) if err != nil { // Cleanup on error if cleanerUpper, ok := val.(CleanerUpper); ok { cleanerUpper.Cleanup() } return nil, fmt.Errorf("provision %s: %v", modInfo, err) }}
4
Validation
If the module implements Validator, its Validate() method is called:
modules.go:300-307
type Validator interface { Validate() error}
context.go:433-444
if validator, ok := val.(Validator); ok { err = validator.Validate() if err != nil { // Cleanup on error if cleanerUpper, ok := val.(CleanerUpper); ok { cleanerUpper.Cleanup() } return nil, fmt.Errorf("%s: invalid configuration: %v", modInfo, err) }}
5
Usage
The module is now ready to be used. It’s typically type-asserted to a specific interface expected by the host module.
6
Cleanup
When the config is unloaded, if the module implements CleanerUpper, its Cleanup() method is called:
modules.go:309-317
type CleanerUpper interface { Cleanup() error}
context.go:75-83
for modName, modInstances := range newCtx.moduleInstances { for _, inst := range modInstances { if cu, ok := inst.(CleanerUpper); ok { err := cu.Cleanup() if err != nil { log.Printf("[ERROR] %s (%p): cleanup: %v", modName, inst, err) } } }}
Caddy provides functions to discover registered modules:
modules.go:195-242
// GetModules returns all modules in the given scope/namespacefunc GetModules(scope string) []ModuleInfo { modulesMu.RLock() defer modulesMu.RUnlock() scopeParts := strings.Split(scope, ".") if scope == "" { scopeParts = []string{} } var mods []ModuleInfoiterateModules: for id, m := range modules { modParts := strings.Split(id, ".") // match only the next level of nesting if len(modParts) != len(scopeParts)+1 { continue } // specified parts must be exact matches for i := range scopeParts { if modParts[i] != scopeParts[i] { continue iterateModules } } mods = append(mods, m) } // make return value deterministic sort.Slice(mods, func(i, j int) bool { return mods[i].ID < mods[j].ID }) return mods}
Examples:
// Get all HTTP handler moduleshandlers := caddy.GetModules("http.handlers")// Get all top-level app modules apps := caddy.GetModules("")// Get all TLS certificate issuersissuers := caddy.GetModules("tls.issuance")