Updated v2: Documentation (markdown)

Matt Holt 2019-07-03 14:45:48 -06:00
parent 21497d9397
commit 4e7ab4eabb

@ -1,5 +1,7 @@
(This documentation is very much a work in progress!) (This documentation is very much a work in progress!)
Features which are available in Caddy Enterprise are indicated with   🏢 _Enterprise_.
# Contents # Contents
- [Command-line interface]() - [Command-line interface]()
@ -11,12 +13,16 @@
- [environ]() - [environ]()
- [Admin endpoint]() - [Admin endpoint]()
- [POST /load]() - [POST /load]()
- [GET /config/]() - [GET /config/[scope]]()
- [POST /config/]() - [POST /config/[scope]]()
- [PUT /config/]() - [PUT /config/[scope]]()
- [PATCH /config/]() - [PATCH /config/[scope]]()
- [DELETE /config/]() - [DELETE /config/[scope]]()
- [Caddy modules]() - [Caddy modules]()
- [Config intro]()
- [Making one from scratch]()
- [Duration values]()
- [Placeholders (variables)]()
- [Config structure]() - [Config structure]()
- [admin]() - [admin]()
- [storage]() - [storage]()
@ -91,9 +97,9 @@ Default address: `localhost:2019`
## POST /load ## POST /load
Sets Caddy's configuration to the JSON body. The `Content-Type` header must indicate a JSON payload, e.g. `application/json`. Sets Caddy's configuration to the JSON body. The `Content-Type` header must indicate a JSON payload, e.g. `application/json`. Configuration changes are very lightweight and efficient.
_Enterprise_: If you are using the `/config` endpoint to modify configuration instead, you MUST NOT use `/load` because it lacks the capabilities for partial configuration updates. _Enterprise_: If you are using the `/config` endpoint to modify configuration instead, you MUST NOT use `/load` because it lacks the capabilities for partial configuration changes.
### Example ### Example
@ -203,6 +209,46 @@ _Host modules_ (or _parent modules_) are modules which load/initialize other mod
_Guest modules_ (or _child modules_) are modules which get loaded or initialized. All modules are at least guest modules. _Guest modules_ (or _child modules_) are modules which get loaded or initialized. All modules are at least guest modules.
# Config intro
Caddy 2's config can be a little overwhelming, but once we finish the config adapters (Caddyfile and NGINX for starters), you won't always need to use the low-level, raw JSON config.
This section covers just a few things you will find helpful as you craft a Caddy config.
## Making one from scratch
We have a [separate page](https://github.com/caddyserver/caddy/wiki/v2:-Config-from-Scratch) which walks you through an example of building up a config of a working static file server with automatic HTTPS from scratch.
## Duration values
Any properties which expect a duration value are given as a string in the same format as [Go's time.Duration type](https://golang.org/pkg/time/#ParseDuration):
> A duration string is a possibly signed sequence of decimal numbers, each with optional fraction and a unit suffix, such as "300ms", "-1.5h" or "2h45m". Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h".
Valid examples: `"30s"`, `"15m"`, `"1h25m30s"`, and `"30us"`.
## Placeholders (variables)
You can inject dynamic values into Caddy 2's configuration by using placeholders (aka variables). Placeholders can be used in properties which accept placeholder values; not all properties execute placeholders. Only string values can use placeholders.
Placeholders are enclosed in `{ }` and namespaced to avoid collisions. Different placeholders are available in different contexts.
All contexts support the global placeholders:
```
{system.hostname}
The system's hostname
{system.os}
The system's OS
{system.arch}
The system's architecture
{system.slash}
The system's filepath separator
{env.variable_name}
Replace variable_name with any env variable (lowercase)
```
# Config structure # Config structure
@ -230,11 +276,11 @@ Configures the administration endpoint.
} }
``` ```
- `listen`: the address to which the admin endpoint's listener should bind itself - `listen`: the address to which the admin endpoint's listener should bind itself.
## storage ## storage
Configures Caddy's default storage module. Configures Caddy's default storage module. A storage module defines how and where Caddy stores assets (such as TLS certificates).
Default: The local file system (`caddy.storage.file_system`). If `XDG_DATA_HOME` is set, then `$XDG_DATA_HOME/caddy` is the folder. Otherwise, `$HOME/.local/share/caddy` is the folder. Default: The local file system (`caddy.storage.file_system`). If `XDG_DATA_HOME` is set, then `$XDG_DATA_HOME/caddy` is the folder. Otherwise, `$HOME/.local/share/caddy` is the folder.
@ -247,25 +293,186 @@ Default: The local file system (`caddy.storage.file_system`). If `XDG_DATA_HOME`
} }
``` ```
- `module`: The ID of the storage module - `module`: The ID of the storage module.
- `root`: The base path in which things should be stored - `root`: The base path in which things should be stored.
## apps ## apps
Each app to initialize should be keyed by its name (i.e. module ID). Each app to initialize should be keyed by its name (i.e. module ID).
Example which initializes the `tls` and `http` apps:
```json
{
"apps": {
"tls": { ... },
"http": { ... }
}
}
```
### http ### http
The `http` app implements an HTTP server with automatic HTTPS. It consists of servers which you name and describe. Each server has listeners and routes. The `http` app implements an HTTP server with automatic HTTPS:
Routes are not your traditional notion of routes (i.e. "GET /foo/bar" -> someFunc). Routes in Caddy 2 are much more powerful. They are given in an ordered list, and each route has three parts: match, apply, respond. All parts are optional. Match is the "if" statement of each route. Each route takes a list of matcher sets. A matcher set comprises matchers of various types. A matcher may comprise multiple values. ```json
{
"http_port": 80,
"https_port": 443,
"grace_period": "",
"servers": {}
}
```
- `http_port`: The port to use for HTTP (optional; used for automatic HTTPS).
- `https_port`: The port to use for HTTPS (optional; used for automatic HTTPS).
- `grace_period`: How long to allow servers to shut down gracefully before forcing them to stop. Duration values follow Go's `time.Duration` format, e.g. `"10s"` or `"1m30s"`.
- `servers`: Server configurations, keyed by unique names you choose. A server is a set of listeners and routes which make sense to group together. At this time, servers cannot have overlapping listeners.
#### http/servers
A map of server ID (of your choice) to its configuration object. Each server is an object with the following structure:
```json
{
"listen": [],
"read_timeout": "",
"read_header_timeout": "",
"write_timeout": "",
"idle_timeout": "",
"max_header_bytes": 0,
"routes": [],
"errors": {},
"tls_conn_policies": [],
"auto_https": {},
"max_rehandles": 3,
"strict_sni_host": false
}
```
- `listen`: The list of listener addresses to bind to.
- `read_timeout`: How long to allow reading an HTTP request.
- `read_header_timeout`: How long to allow reading an HTTP request header.
- `write_timeout`: How long to allow writing an HTTP response.
- `idle_timeout`: How long to keep idle connections open.
- `max_header_bytes`: How many bytes to allow an HTTP request header to be read.
- `routes`: The list of routes.
- `errors`: Configures how to handle errors during HTTP requests.
- `tls_conn_policies`: List of TLS connection policies.
- `auto_https`: Customize or disable automatic HTTPS.
- `max_rehandles`: How many rehandles to allow; prevents infinite looping.
- `strict_sni_host`: If true, enforce that an HTTP Host header matches the connection's ServerName (SNI) value from the TLS handshake. Important when using TLS client authentication.
#### http/servers/listen
Listener addresses take on the following form:
network/host:port-range
For example:
:8080
127.0.0.1:8080
localhost:8080
localhost:8080-8085
tcp/localhost:8080
udp/localhost:9005
unix//path/to/socket
#### http/servers/routes
Routes are not your typical notion of routes (i.e. "GET /foo/bar" -> someFunc). Routes in Caddy 2 are much more powerful. They are given in an ordered list, and each route has three (optional) parts:
1. match
2. apply
3. respond
For each request, routes are evaluated in the order they are given in the list. Each route that matches the request is compiled into the _composite route_, which ends up becoming the HTTP handler for the request. (Don't worry, this process is normally quite speedy.)
Unlike Caddy 1, middleware in Caddy 2 are chained in the order you specify, rather than a hard-coded order. (So be careful!) A responder, if defined, is what actually originates the response. **A good rule of thumb for building routes: keep middleware that deal with the request near the beginning, and middleware that deal with the response near the end.** Generally, this will help ensure you put things in the right order (e.g. the `encode` middleware must wrap the response writer, but you wouldn't want to execute templates on a compressed bitstream, so you'd put a `templates` middleware later in the chain).
In some cases, a request can be _rehandled_, meaning that it is virtually processed through the top-level handler again. This is useful if changes were made to a request (such as rewriting part of it) and you want to process it as if it originally came in like that. Other web servers call this an internal redirect.
The structure for a route is:
```json
{
"group": "",
"match": [],
"apply": [],
"respond": {},
"terminal": false
}
```
- `group`: If this route belongs to a group, specify its name here. Groups are used for mutual exclusion. Only the first matching route from a group is compiled into the composite route. Think like radio buttons on an HTML form.
- `match`: The "if" statement of the route. This specifies an ordered list of matcher sets.
- `apply`: The middleware modules to apply, in the order they are listed. All matching middleware are applied before the matched responder, even if the middleware appear in routes after the responder.
- `respond`: The responder module to use to reply to the request. The responder is the origin of the content. Only the first matching responder will be used; any more will be ignored. The matched responder is always invoked after all the middleware are applied, even if matching middleware appear in later routes.
- `terminal`: If true, no additional routes will be considered.
##### HTTP Placeholders
Within an HTTP route, you can use additional placeholders:
```
{http.request.hostport}
The Host header of the request (including port, if given)
{http.request.host}
Same as hostport but with port stripped
{http.request.host.labels.N}
N is the number; i.e. for "foo.example.com":
0 = "com"
1 = "example"
2 = "foo"
{http.request.port}
The port of the request.
{http.request.scheme}
The scheme of the request (http/https usually)
{http.request.uri}
The full request URI
{http.request.uri.path}
The path component of the request URI
{http.request.uri.path.file}
The filename in the path, excluding directory
{http.request.uri.path.dir}
The directory, excluding leaf filename
{http.request.uri.query.PARAM}
A specific query string parameter given by PARAM
{http.request.header.FIELD-NAME}
Request header field name (lower-cased)
{http.request.cookie.COOKIE_NAME}
Request cookie name (lower-cased)
{http.response.header.FIELD-NAME}
Response header field name (lower-cased)
```
If you are using regexp matchers, capture groups (both named and numeric) are available as well:
{http.matchers.path_regexp.PATTERN_NAME.CAPTURE_GROUP_NAME}
Replacing:
- path_regexp with the name of the matcher that has the regular expression
- PATTERN_NAME with the lower-cased name you gave the pattern
- CAPTURE_GROUP_NAME with the name or index number of the capture group in the regular expression.
This will allow you to access capture groups from anywhere in your HTTP route.
#### http/servers/routes/match
Each route takes a list of matcher sets. A matcher set is comprised of matchers of various types. A matcher may be comprised of multiple values.
The boolean logic of request matching goes like this: The boolean logic of request matching goes like this:
- Matcher sets are OR'ed (first matching matcher set is sufficient) - Matcher sets are OR'ed (first matching matcher set is sufficient).
- Matchers within a site are AND'ed (all matchers in the set must match) - Matchers within a set are AND'ed (all matchers in the set must match).
- Values within a specific matcher are OR'ed (but this could vary depending on the matcher; some don't allow multiple values) - Values within a specific matcher are OR'ed.
This design enables moderately complex logic such as: This design enables moderately complex logic such as:
@ -274,27 +481,286 @@ IF (Host = "example.com")
OR (Host = "sub.example.com" AND Path = "/foo/bar") OR (Host = "sub.example.com" AND Path = "/foo/bar")
``` ```
The expressions in parentheses are matcher sets. Even more advanced logic can be expressed through the Starlark expression matcher. The expressions in parentheses are matcher sets. This expression has 2 matcher sets. This expression uses two matchers: Host and Path, which are AND'ed together within their set.
If a request matches a route, the route's middleware are applied to the request. Unlike Caddy 1, middleware in Caddy 2 are chained in the order you specify, rather than a hard-coded order. (So be careful!) Then a responder, if defined, is what actually writes the response. Even more advanced logic can be expressed through the Starlark expression matcher at virtually no loss in performance.
All matching routes cascade on top of each other to create a "composite route" that is customized for each request. Crucially, if multiple responders match a request, only the first responder is used; the rest are ignored. This way it is impossible to corrupt the response with multiple writes solely by configuration (a common bug in Caddy 1). Omit matchers entirely to match all requests.
A good rule of thumb for building routes: keep middleware that deal with the request near the beginning, and middleware that deal with the response near the end. Generally, this will help ensure you put things in the right order (e.g. the encode middleware must wrap the response writer, but you wouldn't want to execute templates on a compressed bitstream, so you'd put the templates middleware later in the chain). #### http/servers/apply
If a route returns an error, the error along with its recommended status code are bubbled back to the HTTP server which executes a separate error route, if specified. The error routes work exactly like the normal routes, making error handling very powerful and expressive. The list of middleware to apply to the request.
There is more to routing such as grouping routes for exclusivity (i.e. radio buttons instead of checkboxes); making routes terminal so they don't match any more later in the list; and rehandling, which is like an internal redirect that restarts handling the (likely modified) request. You can also omit matchers in a route to have it match all requests. Phew! Lots to know. ##### http.middleware.headers
Modifies request or response headers.
Changes to headers are applied immediately, except for the response headers when `deferred` is true or when `required` is set. In those cases, the changes are applied when the headers are written to the response. Note that deferred changes do not take effect if an error occurs later in the middleware chain.
Properties in this module accept placeholders.
Response header operations can be conditioned upon response status code and/or other header values.
```json ```json
{ {
"http_port": 80, "middleware": "headers",
"https_port": 443, "request": {
"grace_period": "10s", "set": {
"servers": { "Field": ["overwrites"]
"my_server": { },
"listen": [":8080"], "add": {
"routes": [ "Field": ["appends"]
},
"delete": ["Goodbye-Field"]
},
"response": {
"set": {
"Field": ["overwrites"]
},
"add": {
"Field": ["appends"]
},
"delete": ["Goodbye-Field"],
"deferred": true,
"require": {
"status_code": [2, 301],
"headers": {
"Foo": ["bar"]
}
}
}
}
```
- `request`: Request headers to set, add, or delete.
- `response`: Response headers to set, add, or delete.
- `response.deferred`: If true, changes will be applied when the response headers are written instead of as the request is being processed (immediately). Not necessary if `require` is set.
- `response.require`: Defer response header operations until they are written, and only if these criteria are met.
- `response.require.status_code`: Apply response header changes if the status code matches one of these values. Can be a single digit to apply to all statuses in that class (i.e. `2` means all `2xx` status codes) or a full status code (e.g. `404`).
- `response.require.headers`: Apply response header changes if the given response headers have the given value(s).
##### http.middleware.rewrite
Changes the request's URI or method.
Properties in this module accept placeholders.
```json
{
"middleware": "rewrite",
"method": "FOO",
"uri": "/new/path?param=val",
"rehandle": false
}
```
- `method`: Changes the request's HTTP verb.
- `uri`: Changes the request's URI.
- `rehandle`: If true, the request will sent for rehandling after rewriting.
##### http.middleware.markdown
Currently, does almost nothing: very plain/simple markdown rendering
using Blackfriday. Will be configurable. But unlike Caddy 1, this already allows rendering *any* response body as Markdown, whether it be from a proxied upstream or a static file server. This needs a lot more testing and development.
```json
{
"middleware": "markdown"
}
```
##### http.middleware.request_body
Change or limit the request body.
```json
{
"middleware": "request_body",
"max_size": 0
}
```
- `max_size`: The maximum number of bytes to allow reading from the body by a later handler.
##### http.middleware.encode
Compresses responses on-the-fly.
```json
{
"middleware": "encode",
"encodings": {
"gzip": {"level": 5},
"zstd": {},
"brotli": {"quality": 6}
},
"prefer": "",
"minimum_length": 512
}
```
- `encodings`: Selection of compression algorithms to choose from. The best one will be chosen based on the client's Accept-Encoding header. Available options are gzip, zstd, and brotli. Note that brotli is currently very slow and it is recommended to pre-compress static content instead.
- `prefer`: If the client has no strong preference, choose this encoding. TODO: Not yet implemented
- `minimum_length`: Only encode responses that are at least this many bytes long.
##### http.middleware.templates
Interprets the response as a template body, then executes the template and writes the response to the client. Template functions will be documented soon. There are functions to include other files,
make sub-requests (virtual HTTP requests), render Markdown, manipulate headers, access the request fields, manipulate strings, do math, work with data structures, and more.
```json
{
"middleware": "templates",
"file_root": "",
"mime_types": ["text/html", "text/plain", "text/markdown"],
"delimiters": ["{{", "}}"]
}
```
- `file_root`: The root path from which to load files. Required if template functions accessing the file system are used (such as .Include).
- `mime_types`: The MIME types for which to render templates. It is important to use this if the route matchers do not exclude images or other binary files...
- `delimiters`: The template action delimiters.
#### http/servers/respond
The responder to use to reply to the request. The responder is the origin of the content or essense of the response. Each composite route will only have one responder.
##### http.responders.static
Responds to the request with a static (hard-coded) response.
For instance, this is a great way to do HTTP redirects.
```json
{
"responder": "static",
"status_code": 307,
"status_code_str": "{http.error.status_code}",
"headers": {
"Location": ["https://example.com/foo"]
},
"body": "Response body",
"close": true
}
```
- `status_code`: The numeric HTTP status code to respond with.
- `status_code_str`: A string form of the status code to respond with, allowing use of placeholders. TODO: We might just consolidate these two fields and make it only a string.
- `headers`: Header fields to set on the response.
- `body`: The response body.
- `close`: If true, the client's connection will be closed after writing the response.
##### http.responders.file_server
A powerful, flexible static file server.
In general, the request URI's path will be joined to the file server's root to get the file path to serve.
This module executes placeholders in most of its properties.
```
{
"responder": "file_server",
"root": "",
"hide": [],
"index_names": [],
"files": [],
"selection_policy": "first_existing",
"rehandle": false,
"fallback": [],
"browse": {
"template_file": ""
}
}
```
- `root`: The path to the root of the site.
- `hide`: A list of files or folders to hide; the file server will pretend as if they don't exist. Accepts globular patterns like "\*.hidden" or "/foo/*/bar".
- `index_names`: The names of files to try as index files if a folder is requested.
- `files`: A list of files or file paths to try, similar to NGINX's try_files (TODO: Might rename to try or try_files).
- `selection_policy`: When trying files listed in `files`, use this policy to choose one.
- `first_existing` (default): Use the first file that exists.
- `smallest_size`: Choose the file with the smallest size.
- `largest_size`: Choose the file with the largest size.
- `most_recently_modified`: Choose the file that was most recently modified.
- `rehandle`: If true, and if the request was mapped to a different file than the URI path originally pointed to, the request will be sent for rehandling. This includes if an index file is chosen when a directory was requested.
- `fallback`: If no files were found or chosen to satisfy the request, the routes in this list will be compiled and executed instead. It's like an "else" clause.
- `browse`: Enables browsing if a directory was requested.
- `browse.template_file`: Use this template file instead of the default browse template.
##### http.responders.reverse_proxy
Reverse proxy. Still a WIP.
Example:
```
{
"responder": "reverse_proxy",
"try_interval": "20s",
"load_balance_type": "round_robin",
"upstreams": [
{
"host": "http://localhost:8080",
"fast_health_check_dur": "100ms",
"health_check_dur": "10s"
},
{
"host": "http://localhost:8081",
"health_check_dur": "2s"
},
{
"host": "http://localhost:8082",
"health_check_path": "health"
},
{
"host": "http://localhost:8083",
"circuit_breaker": {
"type": "status_ratio",
"threshold": 0.5
}
}
]
}
```
#### http/servers/errors
If a route returns an error, the error along with its recommended status code are bubbled back to the HTTP server which executes a separate error route, if specified. The error routes work exactly like the normal routes, making error handling very powerful and expressive.
#### http/servers/tls_conn_policies
TODO.
#### http/servers/auto_https
TODO.
--------------
TODO:
```json
{
"listen": [":8080"],
"routes": [
{ {
"match": [{ "match": [{
"host": ["example.com"], "host": ["example.com"],
@ -383,394 +849,8 @@ The rest of this document is a WIP. It takes hours and hours to polish the docum
What follows is the original ad-hoc documentation that we sent some early testers. We'll replace it with more polished docs very soon. What follows is the original ad-hoc documentation that we sent some early testers. We'll replace it with more polished docs very soon.
HTTP App
=========
...
Now then. The contents of caddy.json are up to you. Here's a contrived example to
demonstrate the fields you can use:
{
"apps": {
"http": {
"http_port": 80,
"https_port": 443,
"grace_period": "10s",
"servers": {
"myserver": {
"listen": [":8080"],
"routes": [
{
"match": [{
"host": ["example.com"],
"path": ["/foo/bar", "*.ext"],
"path_regexp": {
"name": "myre",
"pattern": "/foo/(.*)/bar"
},
"method": ["GET"],
"query": {"param": ["value"]},
"header": {"Field": ["foo"]},
"header_regexp": {
"Field": {
"pattern": "foo(.*)-bar",
"name": "other"
},
},
"protocol": "grpc",
"not": {
"path": ["/foo/bar"],
"...": "(any matchers in here will be negated)
},
"remote_ip": {
"ranges": ["127.0.0.1", "192.168.0.1/24"]
},
"starlark_expr": "req.host == 'foo.com' || (req.host != 'example.com' && req.host != 'sub.example.com')"
}],
"apply": [
{
"middleware": "rewrite",
"method": "FOO",
"uri": "/test/abc"
},
{
"middleware": "headers",
"response": {
"set": {
"Foo": ["bar"],
"Regexp": ["{http.matchers.path_regexp.myre.0}"]
}
}
}
],
"respond": {
"responder": "static",
"body": "Booyah: {http.request.method} {http.request.uri} Foo: {http.response.header.foo}"
},
"group": "exclusive",
"terminal": true
}
],
"errors": {
"routes": [ ... ]
},
"tls_connection_policies": [
{
"match": {
"host": ["example.com"]
},
"alpn": ["..."],
"cipher_suites": ["..."],
"certificate_selection": {
"policy": "enterprise",
"subject.organization": "O1",
"tag": "company1"
}
}
],
"automatic_https": {
"disabled": false,
"disable_redirects": false,
"skip": ["exclude", "these", "domains"],
"skip_certificates": ["doesn't provision certs for these domains but still does redirects"]
},
"max_rehandles": 3
}
}
}
}
}
You can update the config any time by POSTing the updated payload to that endpoint. Try it!
Fun fact: the enterprise version allows GET/POST/PUT/PATCH/DELETE to any path within your
config structure to mutate (or get) only that part. For example:
PUT /config/apps/http/servers/myserver/routes/0/match/hosts "foo.example.com"
would add "foo.example.com" to the host matcher for the first route in myserver. This makes
Caddy's config truly dynamic, even for hand-crafted changes on-the-fly.
Here is some makeshift docs for what you can do. In general, we are showing all parameters
that are available, but you can often omit parameters that you don't want or need to use.
Middleware:
- headers
{
"middleware": "headers",
"request": {
"set": {
"Field": ["overwrites"]
},
"add": {
"Field": ["appends"]
},
"delete": ["Goodbye-Field"]
},
"response": {
"set": {
"Field": ["overwrites"]
},
"add": {
"Field": ["appends"]
},
"delete": ["Goodbye-Field"],
"deferred": true,
"require": {
"status_code": [2, 301],
"headers": {
"Foo": ["bar"]
}
}
}
}
Changes to headers are applied immediately, except for the
response headers when "deferred" is true or when "required"
is set. In those cases, the changes are applied when the
headers are written to the response. Note that deferred
changes do not take effect if an error occurs later in the
middleware chain. The "require" property allows you to
conditionally manipulate response headers based on the
response to be written.
- rewrite
{
"middleware": "rewrite",
"method": "FOO",
"uri": "/new/path?param=val",
"rehandle": true
}
Rewrites the request URI or method. If "rehandle" is true,
the request is rehandled after the rewrite (as if it had
originally been received like that).
- markdown
{
"middleware": "markdown"
}
Does nothing so far: very plain/simple markdown rendering
using Blackfriday. Will be configurable. But unlike Caddy 1,
this already allows rendering *any* response body as
Markdown, whether it be from a proxied upstream or a static
file server. This needs a lot more testing and development.
- request_body
{
"middleware": "request_body",
"max_size": 1048576
}
Limits the size of the request body if read by a later
handler.
- encode
{
"middleware": "encode",
"encodings": {
"gzip": {"level": 5},
"zstd": {}
},
"minimum_length": 512
}
Compresses responses on-the-fly.
- templates
{
"middleware": "templates",
"file_root": "/var/www/mysite",
"mime_types": ["text/html", "text/plain", "text/markdown"],
"delimiters": ["{{", "}}"]
}
Interprets the response as a template body, then
executes the template and writes the response to
the client. Template functions will be documented
soon. There are functions to include other files,
make sub-requests (virtual HTTP requests), render
Markdown, manipulate headers, access the request
fields, manipulate strings, do math, work with
data structures, and more.
Responders:
- reverse_proxy
"respond": {
"responder": "reverse_proxy",
"try_interval": "20s",
"load_balance_type": "round_robin",
"upstreams": [
{
"host": "http://localhost:8080",
"fast_health_check_dur": "100ms",
"health_check_dur": "10s"
},
{
"host": "http://localhost:8081",
"health_check_dur": "2s"
},
{
"host": "http://localhost:8082",
"health_check_path": "health"
},
{
"host": "http://localhost:8083",
"circuit_breaker": {
"type": "status_ratio",
"threshold": 0.5
}
}
]
}
- file_server
"respond": {
"responder": "file_server",
"root": "/path/to/site/root",
"hide": ["/pretend/these/don't/exist"],
"index_names": ["index.html", "index.txt"],
"files": ["try", "these", "files"],
"selection_policy": "largest_size",
"rehandle": true,
"fallback": [
// more routes!
],
"browse": {
"template_file": "optional.tpl"
}
}
The file server uses the request URI to build the target file
path by appending it to the root path.
The "files" parameter is like nginx's "try_files" except
you can specify how to select one from that list. The default
is "first_existing" but there is also "largest_size",
"smallest_size", and "most_recently_modified".
If "rehandle" is true and the request was mapped to a different
file than the URI path originally pointed to, the request will
be sent for rehandling (internal redirect). This includes using
an index file when a directory was requested or using "files" to
try a file different than the URI path.
If no files were found to handle the request, "fallback" is
compiled and executed. These are routes just like what you're
used to defining.
If "browse" is specified, directory browsing will be enabled. It
should honor the "hide" list. To use the default template, just
leave it empty {}.
The "hide" list can also use globular patterns like "*.hidden"
or "/foo/*/bar".
- static
"respond": {
"responder": "static",
"status_code": 307,
"status_code_str": "{http.error.status_code}",
"headers": {
"Location": ["https://example.com/foo"]
},
"body": "Response body",
"close": true
}
Responds to the request with a static/hard-coded response.
The status code can be expressed either as an integer or
a string. Expressing it as a string allows you to use
a placeholder (variable). TODO: Should we just consolidate
it so it's always a string (we can convert "301" to an int)?
Only one representation should be used, not both.
You can set response headers.
You can also specify the response body.
And if "close" is true, the connection with the client will
be closed after responding.
This is a great way to do HTTP redirects.
You can use placeholders (variables) in many values, as well.
Depending on the context, these may be available:
{system.hostname}
{system.os}
{system.arch}
{system.slash}
{http.request.hostport}
{http.request.host}
{http.request.host.labels.N} (N is the number; i.e. with foo.example.com, 2 is "foo" and 1 is "example")
{http.request.port}
{http.request.scheme}
{http.request.uri}
{http.request.uri.path}
{http.request.uri.path.file}
{http.request.uri.path.dir}
{http.request.uri.query.param}
{http.request.header.field-name} (lower-cased field name)
{http.request.cookie.cookie_name}
{http.response.header.field-name} (lower-cased field name)
{http.request.host}
{http.request.host}
If using regexp matchers, capture groups (both named and numeric) are available as well:
{http.matchers.path_regexp.pattern_name.capture_group_name}
(replace pattern_name with the name you gave the pattern, and replace capture_group_name
with the name or index number of the capture group).
Placeholder performance needs to be improved. Looking into this.
Listeners can be defined as follows:
network/host:port-range
For example:
:8080
127.0.0.1:8080
localhost:8080
localhost:8080-8085
tcp/localhost:8080
udp/localhost:9005
unix//path/to/socket
Oh! That reminds me: if you use a "host" matcher in your HTTP routes, Caddy 2
will use that to enable automatic HTTPS. Tada!
--------------------------------