Caddy’s import directive is a feature that lets you reuse configuration snippets across different Caddyfiles, effectively creating a form of "configuration as code" for your web server.
Imagine you have a common set of directives you want to apply to multiple sites, like security headers, specific logging formats, or even entire proxy configurations. Instead of copying and pasting these blocks into every Caddyfile, you can define them once in a separate file and import them.
Let’s see it in action. Suppose we have a common_security.caddy file with our standard security headers:
# common_security.caddy
header {
Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
X-Frame-Options "DENY"
X-Content-Type-Options "nosniff"
Referrer-Policy "strict-origin-when-cross-origin"
Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self'; img-src 'self'; font-src 'self';"
}
Now, in our main Caddyfile, we can import this snippet for one or more sites:
site1.example.com {
reverse_proxy localhost:8080
import common_security.caddy
}
site2.example.com {
root * /var/www/site2
file_server
import common_security.caddy
}
When Caddy loads this configuration, it will effectively substitute the contents of common_security.caddy into the import directives. So, site1.example.com’s effective configuration becomes:
site1.example.com {
reverse_proxy localhost:8080
header {
Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
X-Frame-Options "DENY"
X-Content-Type-Options "nosniff"
Referrer-Policy "strict-origin-when-cross-origin"
Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self'; img-src 'self'; font-src 'self';"
}
}
And similarly for site2.example.com. This is incredibly useful for maintaining consistency and reducing errors across multiple services hosted on the same Caddy instance.
The import directive can be used anywhere a block of directives is expected. This means you can import snippets within other snippets, allowing for hierarchical organization of your configuration. You can also import multiple snippets into a single site or even into another snippet.
Consider a more complex scenario where you have a base snippet for all proxy configurations and then specific overrides or additions for individual services.
base_proxy.caddy:
# base_proxy.caddy
reverse_proxy {
to {args.target}
health_check {
interval 10s
timeout 5s
}
}
api_proxy.caddy:
# api_proxy.caddy
import base_proxy.caddy target=localhost:5000
header {
X-API-Version "v1"
}
frontend_proxy.caddy:
# frontend_proxy.caddy
import base_proxy.caddy target=localhost:3000
Then in your Caddyfile:
api.example.com {
import api_proxy.caddy
}
www.example.com {
import frontend_proxy.caddy
}
Notice how api_proxy.caddy passes an argument target=localhost:5000 to the base_proxy.caddy import. This is a powerful feature for parameterizing your snippets. The {args.target} placeholder in base_proxy.caddy will be replaced by the value provided during the import.
When you use import, Caddy resolves paths relative to the directory of the Caddyfile being loaded. If you need to import from a different location, you can provide an absolute path or a path relative to the current working directory.
The import directive parses the imported file and inserts its contents as if they were written directly in the Caddyfile. This means that directives within the imported snippet are evaluated in the context of the site they are imported into. For example, if a snippet contains a route directive, that route will be associated with the site it’s imported into, not with the snippet file itself.
One subtlety that trips many people up is how Caddy handles relative paths within imported snippets. If common_security.caddy itself contained an import statement, say import /etc/caddy/security_extras.caddy, that path /etc/caddy/security_extras.caddy would be resolved relative to the location of common_security.caddy, not the Caddyfile that imported it. This behavior allows for self-contained, portable configuration snippets.
This feature is not just for sharing common settings; it’s fundamental to managing larger, more complex Caddy deployments in a structured and maintainable way.
The next logical step after mastering import is to explore Caddy’s JSON configuration API for even more programmatic control.