This page is about Caddy 2, which is currently in beta. Click here for the old Caddy 1 site. Thank you for your patience as we transition!
Documentation
Caddy 2 is beta software. It is ready for production, but some things will change between releases; check the release notes before upgrading.

Extending Caddy

Caddy is easy to extend because of its modular architecture. Most kinds of Caddy extensions (or plugins) are known as modules if they extend or plug into Caddy's configuration structure. To be clear, Caddy modules are distinct from Go modules (but they are also Go modules). On this page, we refer to Caddy modules.

Module Basics

A Caddy module is any named type that registers itself as a Caddy module when its package is imported.

Crucially, a module always implements the caddy.Module interface, which provides its name and a constructor function.

Here's a template you can copy & paste:

package mymodule

import "github.com/caddyserver/caddy/v2"

func init() {
	err := caddy.RegisterModule(Gizmo{})
	if err != nil {
		log.Fatal(err)
	}
}

// Gizmo is an example; put your own type here.
type Gizmo struct {
}

// CaddyModule returns the Caddy module information.
func (Gizmo) CaddyModule() caddy.ModuleInfo {
	return caddy.ModuleInfo{
		ID:  "foo.gizmo",
		New: func() caddy.Module { return new(Gizmo) },
	}
}

A module is "plugged in" by adding an import to the program:

import _ "github.com/example/mymodule"

Module Requirements

Caddy modules:

  1. Implement the caddy.Module interface to provide an ID and constructor
  2. Have a unique name in the proper namespace
  3. Usually satisfy some interface(s) that are meaningful to the host module for that namespace

The next sections explain how to satisfy these properties!

Module IDs

Each Caddy module has a unique ID, consisting of a namespace and name:

  • A complete ID looks like foo.bar.module_name
  • The namespace would be foo.bar
  • The name would be module_name which must be unique in its namespace

Host modules (or parent modules) are modules which load/initialize other modules. They typically define namespaces for guest modules.

Guest modules (or child modules) are modules which get loaded or initialized. All modules are guest modules.

Module IDs must use snake_case convention.

Namespaces

Namespaces are like classes, i.e. a namespace defines some functionality that is common among all modules within it. For example, we can expect that all modules within the http.handlers namespace are HTTP handlers. It follows that a host module may type-assert guest modules in that namespace from interface{} types into a more specific, useful type such as http.Handler.

A guest module must be properly namespaced in order for it to be recognized by a host module, because host modules will often ask Caddy core for a list of all modules within a certain namespace for a specific functionality it requires.

Usually, but not always, a Caddy module namespaces correlates with a Go interface type that the modules in that namespace are expected to implement.

For example, if you were to write an HTTP handler module called gizmo, your module's name would be http.handlers.gizmo, because the http app will look for handlers in the http.handlers namespace.

The caddy and admin namespaces are reserved.

To write modules which plug into 3rd-party host modules, consult those modules for their namespace documentation.

Names

The name within a namespace is not particularly important, as long as it is unique, concise, and makes sense for what it does.

Apps

Apps are modules with an empty namespace, and which conventionally become their own top-level namespace. App modules implement the caddy.App interface.

These modules appear in the "apps" property of the top-level of Caddy's config:

{
	"apps": {}
}

Example apps are http and tls. Theirs is the empty namespace.

Guest modules written for these apps should be in a namespace derived from the app name. For example, HTTP handlers use the http.handlers namespace and TLS certificate loaders use the tls.certificates namespace.

Module Implementation

A module can be virtually any type, but structs are the most common because they can hold user configuration.

Configuration

Most modules require some configuration. Caddy takes care of this automatically, as long as your type is compatible with JSON. Thus, if a module is a struct type, it will need struct tags on its fields, which should use snake_casing according to Caddy convention:

type Gizmo struct {
	MyField string `json:"my_field,omitempty"`
	Number  int    `json:"number,omitempty"`
}

Using struct tags in this way will ensure that config properties are consisently named across all of Caddy.

When a module is initialized, it will already have its configuration filled out. It is also possible to perform additional provisioning and validation steps after a module is initialized.

Module Lifecycle

A module's life begins when it is loaded by a host module. The following happens:

  1. New() is called to get an instance of the module's value.
  2. The module's configuration is unmarshaled into that instance.
  3. If the module is a caddy.Provisioner, the Provision() method is called.
  4. If the module is a caddy.Validator, the Validate() method is called.
  5. At this point, the host module is given the loaded guest module as an interface{} value, so the host module will usually type-assert the guest module into a more useful type. Check the documentation for the host module to know what is required of a guest module in its namespace, e.g. what methods need to be implemented.
  6. When a module is no longer needed, and if it is a caddy.CleanerUpper, the Cleanup() method is called.

Note that multiple loaded instances of your module may overlap at a given time! During config changes, new modules are started before the old ones are stopped. Be sure to use global state carefully. Use the caddy.UsagePool type to help manage global state across module loads.

Provisioning

A module's configuration will be unmarshaled into its value automatically. This means, for example, that struct fields will be filled out for you.

However, if your module requires additional provisioning steps, you can implement the caddy.Provisioner interface:

// Provision sets up the module.
func (g *Gizmo) Provision(ctx caddy.Context) error {
	// TODO: set up the module
	return nil
}

This is typically where host modules will load their guest/child modules, but it can be used for pretty much anything.

Provisioning MUST NOT depend on other apps, since provisioning apps is done in an arbitrary order. To rely on other app modules, you must wait until after the Provision phase.

Additionally, you should avoid performing expensive operations in Provision, since provisioning is performed even if a config is only being validated. When in the provisioning phase, do not expect that the module will actually be used.

Logs

If your module needs logging, do not use log.Print*() from the Go standard library. In other words, do not use Go's global logger. Caddy uses high-performance, highly flexible, structured logging with zap.

To emit logs, get a logger in your module's Provision method:

func (g *Gizmo) Provision(ctx caddy.Context) error {
	g.logger = ctx.Logger(g) // g.logger is a *zap.Logger
}

Then you can emit structured, leveled logs using g.logger. See zap's godoc for details.

Validating

Modules which would like to validate their configuration may do so by satisfying the caddy.Validator interface:

// Validate validates that the module has a usable config.
func (g Gizmo) Validate() error {
	// TODO: validate the module's setup
	return nil
}

Validate should be a read-only function. It is run during the provisioning phase right after the Provision() method.

Interface guards

Caddy module behavior is implicit because Go interfaces are satisfied implicitly. Simply adding the right methods to your module's type is all it takes to make or break your module's correctness. Thus, making a typo or getting the method signature wrong can lead to unexpected (lack of) behavior.

Fortunately, there is an easy, no-overhead, compile-time check you can add to your code to ensure you've added the right methods. These are called interface guards:

var _ InterfaceName = (*YourType)(nil)

Replace InterfaceName with the interface you intend to satisfy, and YourType with the name of your module's type.

For example, an HTTP handler such as the static file server might satisfy multiple interfaces:

// Interface guards
var (
	_ caddy.Provisioner           = (*FileServer)(nil)
	_ caddyhttp.MiddlewareHandler = (*FileServer)(nil)
)

This prevents the program from compiling if *FileServer does not satisfy those interfaces.

Without interface guards, confusing bugs can slip in. For example, if your module must provision itself before being used but your Provision() method has a mistake (e.g. misspelled or wrong signature), provisioning will never happen, leading to head-scratching. Interface guards are super easy and can prevent that. They usually go at the bottom of the file.

Host Modules

A module becomes a host module when it loads its own guest modules. This is useful if a piece of the module's functionality can be implemented in different ways.

A host module is almost always a struct. Normally, supporting a guest module requires two struct fields: one to hold its raw JSON, and another to hold its decoded value:

type Gizmo struct {
	GadgetRaw json.RawMessage `json:"gadget,omitempty" caddy:"namespace=foo.gizmo.gadgets inline_key=gadgeter"`

	Gadget Gadgeter `json:"-"`
}

The first field (GadgetRaw in this example) is where the raw, unprovisioned JSON form of the guest module can be found.

The second field (Gadget) is where the final, provisioned value will eventually be stored. Since the second field is not user-facing, we exclude it from JSON with a struct tag. (You could also unexport it if it is not needed by other packages, and then no struct tag is needed.)

Caddy struct tags

The caddy struct tag on the raw module field helps Caddy to know the namespace and name (comprising the complete ID) of the module to load. It is also used for generating documentation.

The struct tag has a very simple format: key1=val1 key2=val2 ...

For module fields, the struct tag will look like:

`caddy:"namespace=foo.bar inline_key=baz"`

The namespace= part is required. It defines the namespace in which to look for the module.

The inline_key= part is only used if the module's name will be found inline with the module itself; this implies that the value is an object where one of the keys is the inline key, and its value is the name of the module. If omitted, then the field type must be a caddy.ModuleMap or []caddy.ModuleMap, where the map key is the module name.

Loading guest modules

To load a guest module, call ctx.LoadModule() during the provision phase:

// Provision sets up g and loads its gadget.
func (g *Gizmo) Provision(ctx caddy.Context) error {
	if g.GadgetRaw != nil {
		val, err := ctx.LoadModule(g, "GadgetRaw")
		if err != nil {
			return fmt.Errorf("loading gadget module: %v", err)
		}
		g.Gadget = val.(Gadgeter)
	}
	return nil
}

Note that the LoadModule() call takes a pointer to the struct and the field name as a string. Weird, right? Why not just pass the struct field directly? It's because there are a few different ways to load modules depending on the layout of the config. This method signature allows Caddy to use reflection to figure out the best way to load the module and, most importantly, read its struct tags.

If a guest module must explicitly be set by the user, you should return an error if the Raw field is nil or empty before trying to load it.

Complete Example

Let's suppose we want to write an HTTP handler module. This will be a contrived middleware for demonstration purposes which prints the visitor's IP address to a stream on every HTTP request.

We also want it to be configurable via the Caddyfile, because most people prefer to use the Caddyfile in non-automated situations. We do this by registering a Caddyfile handler directive, which is a kind of directive that can add a handler to the HTTP route. We also implement the caddyfile.Unmarshaler interface. By adding these few lines of code, this module can be configured with the Caddyfile! For example: visitor_ip stdout.

Here is the code for such a module, with explanatory comments:

package visitorip

import (
	"fmt"
	"io"
	"net/http"
	"os"

	"github.com/caddyserver/caddy/v2"
	"github.com/caddyserver/caddy/v2/config/caddyfile"
	"github.com/caddyserver/caddy/v2/config/httpcaddyfile"
	"github.com/caddyserver/caddy/v2/modules/caddyhttp"
)

func init() {
	caddy.RegisterModule(Middleware{})
	httpcaddyfile.RegisterHandlerDirective("visitor_ip", parseCaddyfile)
}

// Middleware implements an HTTP handler that writes the
// visitor's IP address to a file or stream.
type Middleware struct {
	// The file or stream to write to. Can be "stdout"
	// or "stderr".
	Output string `json:"output,omitempty"`

	w io.Writer
}

// CaddyModule returns the Caddy module information.
func (Middleware) CaddyModule() caddy.ModuleInfo {
	return caddy.ModuleInfo{
		ID:  "http.handlers.visitor_ip",
		New: func() caddy.Module { return new(Middleware) },
	}
}

// Provision implements caddy.Provisioner.
func (m *Middleware) Provision(ctx caddy.Context) error {
	switch m.Output {
	case "stdout":
		m.w = os.Stdout
	case "stderr":
		m.w = os.Stderr
	default:
		return fmt.Errorf("an output stream is required")
	}
	return nil
}

// Validate implements caddy.Validator.
func (m *Middleware) Validate() error {
	if m.w == nil {
		return fmt.Errorf("no writer")
	}
	return nil
}

// ServeHTTP implements caddyhttp.MiddlewareHandler.
func (m Middleware) ServeHTTP(w http.ResponseWriter, r *http.Request, next caddyhttp.Handler) error {
	m.w.Write([]byte(r.RemoteAddr))
	return next.ServeHTTP(w, r)
}

// UnmarshalCaddyfile implements caddyfile.Unmarshaler.
func (m *Middleware) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
	for d.Next() {
		if !d.Args(&m.Output) {
			return d.ArgErr()
		}
	}
	return nil
}

// parseCaddyfile unmarshals tokens from h into a new Middleware.
func parseCaddyfile(h httpcaddyfile.Helper) (caddyhttp.MiddlewareHandler, error) {
	var m Middleware
	err := m.UnmarshalCaddyfile(h.Dispenser)
	return m, err
}

// Interface guards
var (
	_ caddy.Provisioner           = (*Middleware)(nil)
	_ caddy.Validator             = (*Middleware)(nil)
	_ caddyhttp.MiddlewareHandler = (*Middleware)(nil)
	_ caddyfile.Unmarshaler       = (*Middleware)(nil)
)