diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..fbd281d1 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +_gitignore/ diff --git a/Caddyfile b/Caddyfile new file mode 100644 index 00000000..573c2fa3 --- /dev/null +++ b/Caddyfile @@ -0,0 +1,12 @@ +:2015 + +root * src + +file_server +templates + +redir /docs/json /docs/json/ +rewrite /docs/json/* /docs/json/index.html +rewrite /docs/* /docs/index.html + +reverse_proxy /api/* localhost:4444 diff --git a/src/docs/index.html b/src/docs/index.html new file mode 100644 index 00000000..e0ab280c --- /dev/null +++ b/src/docs/index.html @@ -0,0 +1,29 @@ +{{$pathParts := splitList "/" .OriginalReq.URL.Path}} +{{$markdownFilename := default "index" (slice $pathParts 2 | join "/")}} +{{$markdownFilePath := printf "/docs/markdown/%s.md" $markdownFilename}} +{{$markdownFile := (include $markdownFilePath | splitFrontMatter)}} + + +
+
+
+ + +
++ Fulfilled by modules in namespace: +
+ +This property is required because it specifies the module name.
+caddy run
+
+
+
+This blocks forever, but what is it doing? At the moment... nothing. By default, Caddy's configuration ("config") is blank. We can verify this using the [admin API](/docs/api) in another terminal:
+
+curl localhost:2019/config/
+
+We can make Caddy useful by giving it a config. One way to do this is by making a POST request to the [/load](/docs/api#post-load) endpoint. Just like any HTTP request, there are many ways to do this, but in this tutorial we'll use `curl`.
+
+## Your first config
+
+To prepare our request, we need to make a config. Caddy's configuration is simply a [JSON document](/docs/json/) (or [anything that converts to JSON](/docs/config-adapters)).
+
+
+
+Save this to a JSON file:
+
+```json
+{
+ "apps": {
+ "http": {
+ "servers": {
+ "example": {
+ "listen": [":2015"],
+ "routes": [
+ {
+ "handle": [{
+ "handler": "static_response",
+ "body": "Hello, world!"
+ }]
+ }
+ ]
+ }
+ }
+ }
+ }
+}
+```
+
+Then upload it:
+
+curl localhost:2019/load \
+ -X POST \
+ -H "Content-Type: application/json" \
+ -d @caddy.json
+
+
+
+
+We can verify that Caddy applied our new config with another GET request:
+
+curl localhost:2019/config/
+
+Test that it works by going to [localhost:2015](http://localhost:2015) in your browser or using `curl`:
+
+curl localhost:2015
+Hello, world!
+
+
+
+If you see _Hello, world!_, then congrats -- it's working! It's always a good idea to make sure your config works as you expect, especially before deploying into production.
+
+Let's change our welcome message from "Hello world!" to something a little more motivational: "I can do hard things." Make this change in your config file, so that the handler object now looks like this:
+
+```json
+{
+ "handler": "static_response",
+ "body": "I can do hard things."
+}
+```
+
+Save the config file, then update Caddy's active configuration by running the same POST request again:
+
+curl localhost:2019/load \
+ -X POST \
+ -H "Content-Type: application/json" \
+ -d @caddy.json
+
+
+
+
+For good measure, verify that the config was updated:
+
+curl localhost:2019/config/
+
+Test it by refreshing the page in your browser (or running `curl` again), and you will see an inspirational message!
+
+
+## Config traversal
+
+
+
+Instead of uploading the entire config file for a small change, let's use a powerful feature of Caddy's API to make the change without ever touching our config file.
+
+Using the request URI's path, we can traverse into the config structure and update only the message string (be sure to scroll right if clipped):
+
+curl \
+ localhost:2019/config/apps/http/servers/example/routes/0/handle/0/body \
+ -X POST \
+ -H "Content-Type: application/json" \
+ -d '"Work smarter, not harder."'
+
+
+
+
+You can verify that it worked with a similar GET request, for example:
+
+curl localhost:2019/config/apps/http/servers/example/routes
+
+You should see:
+
+```json
+[{"handle":[{"body":"Work smarter, not harder.","handler":"static_response"}]}]
+```
+
+
+
+**Important note:** This should be obvious, but once you use the API to make a change that is not in your original config file, your config file becomes obsolete. There are a few ways to handle this:
+
+- Use the `--resume` of the [caddy run](/docs/command-line#caddy-run) command to use the last active config.
+- [Export Caddy's new configuration](/docs/api#get-configpath) with a subsequent GET request.
+- Don't mix the use of config files with changes via the API; have one source of truth.
+
+
+
+
+
+## Using `@id` in JSON
+
+Config traversal is certainly useful, but the paths are little long, don't you think?
+
+We can give our handler object an [`@id` tag](/docs/api#using-id-in-json) to make it easier to access:
+
+curl \
+ localhost:2019/config/apps/http/servers/example/routes/0/handle/0/@id \
+ -X POST \
+ -H "Content-Type: application/json" \
+ -d '"msg"'
+
+
+This adds a property to our handler object: `"@id": "msg"`, so it now looks like this:
+
+```json
+{
+ "@id": "msg",
+ "body": "Work smarter, not harder.",
+ "handler": "static_response"
+}
+```
+
+
+
+We can then access it directly:
+
+curl localhost:2019/id/msg
+
+And now we can change the message with a shorter path:
+
+curl \
+ localhost:2019/id/msg/body \
+ -X POST \
+ -H "Content-Type: application/json" \
+ -d '"Some shortcuts are good."'
+
+
+And check it again:
+
+curl localhost:2019/id/msg/body
+
+
+
+
diff --git a/src/docs/markdown/api.md b/src/docs/markdown/api.md
new file mode 100644
index 00000000..1c3e3381
--- /dev/null
+++ b/src/docs/markdown/api.md
@@ -0,0 +1,222 @@
+---
+title: "API"
+---
+
+# API
+
+Caddy is configured through an administration endpoint which can be accessed via HTTP using a [REST](https://en.wikipedia.org/wiki/Representational_state_transfer) API. You can [configure this endpoint](/docs/json/admin/) in your Caddy config.
+
+**Default address: `localhost:2019`**
+
+The latest configuration will be saved to disk after any changes (unless [disabled](/docs/json/admin/config/)). You can resume the last working config after a restart with [`caddy run --resume`](/docs/command-line#caddy-run).
+
+---
+
+- **[POST /load](#post-load)**
+ Sets or replaces the active configuration
+
+- **[POST /stop](#post-stop)**
+ Stops the active configuration and exits the process
+
+- **[GET /config/[path]](#get-configpath)**
+ Exports the config at the named path
+
+- **[POST /config/[path]](#post-configpath)**
+ Sets or replaces object; appends to array
+
+- **[PUT /config/[path]](#put-configpath)**
+ Creates new object; inserts into array
+
+- **[PATCH /config/[path]](#patch-configpath)**
+ Replaces an existing object or array element
+
+- **[DELETE /config/[path]](#delete-configpath)**
+ Deletes the value at the named path
+
+- **[Using @id in JSON](#using-id-in-json)**
+ Easily traverse into the config structure
+
+
+
+## POST /load
+
+Sets Caddy's configuration, overriding any previous configuration. It blocks until the reload completes or fails. Configuration changes are lightweight, efficient, and incur zero downtime. If the new config fails for any reason, the old config is rolled back into place without downtime.
+
+This endpoint supports different config formats using config adapters. The request's Content-Type header indicates the config format used in the request body. Usually, this should be `application/json` which represents Caddy's native config format. For another config format, specify the appropriate Content-Type so that the value after the forward slash / is the name of the config adapter to use. For example, when submitting a Caddyfile, use a value like `text/caddyfile`; or for JSON 5, use a value such as `application/json5`; etc.
+
+### Examples
+
+Set a new active configuration:
+
+curl -X POST "http://localhost:2019/load" \
+ -H "Content-Type: application/json" \
+ -d @caddy.json
+
+Note: curl's `-d` flag removes newlines, so if your config format is sensitive to line breaks (e.g. the Caddyfile), use `--data-binary` instead:
+
+curl -X POST "http://localhost:2019/load" \
+ -H "Content-Type: text/caddyfile" \
+ --data-binary @Caddyfile
+
+
+## POST /stop
+
+Gracefully shuts down the server and exits the process. To only stop the running configuration without exiting the process, use [DELETE /config/](#delete-configpath).
+
+### Example
+
+Stop the process:
+
+curl -X POST "http://localhost:2019/stop"
+
+
+## GET /config/[path]
+
+Exports Caddy's current configuration at the named path. Returns a JSON body.
+
+### Examples
+
+Export entire config and pretty-print it:
+
+curl "http://localhost:2019/config/" | jq
+{
+ "apps": {
+ "http": {
+ "servers": {
+ "myserver": {
+ "listen": [
+ ":443"
+ ],
+ "routes": [
+ {
+ "match": [
+ {
+ "host": [
+ "example.com"
+ ]
+ }
+ ],
+ "handle": [
+ {
+ "handler": "file_server"
+ }
+ ]
+ }
+ ]
+ }
+ }
+ }
+ }
+}
+
+Export just the listener addresses:
+
+curl "http://localhost:2019/config/apps/http/servers/myserver/listen"
+[":443"]
+
+
+
+## POST /config/[path]
+
+Changes Caddy's configuration at the named path to the JSON body of the request. If the destination value is an array, POST appends; if an object, it creates or replaces.
+
+As a special case, many items can be added to an array if:
+
+1. the path ends in `/...`
+2. the element of the path before `/...` refers to an array
+3. the payload is an array
+
+In this case, the elements in the payload's array will be expanded, and each one will be appended to the destination array. In Go terms, this would have the same effect as:
+
+```go
+baseSlice = append(baseSlice, newElems...)
+```
+
+### Examples
+
+Add a listener address:
+
+curl -X POST \
+ -H "Content-Type: application/json" \
+ -d '":8080"' \
+ "http://localhost:2019/config/apps/http/servers/myserver/listen"
+
+Add multiple listener addresses:
+
+curl -X POST \
+ -H "Content-Type: application/json" \
+ -d '[":8080", ":5133"]' \
+ "http://localhost:2019/config/apps/http/servers/myserver/listen/..."
+
+## PUT /config/[path]
+
+Changes Caddy's configuration at the named path to the JSON body of the request. If the destination value is a position (index) in an array, PUT inserts; if an object, it strictly creates a new value.
+
+### Example
+
+Add a listener address in the first slot:
+
+curl -X PUT \
+ -H "Content-Type: application/json" \
+ -d '":8080"' \
+ "http://localhost:2019/config/apps/http/servers/myserver/listen/0"
+
+
+## PATCH /config/[path]
+
+Changes Caddy's configuration at the named path to the JSON body of the request. PATCH strictly replaces an existing value or array element.
+
+### Example
+
+Replace the listener addresses:
+
+curl -X PATCH \
+ -H "Content-Type: application/json" \
+ -d '[":8081", ":8082"]' \
+ "http://localhost:2019/config/apps/http/servers/myserver/listen"
+
+
+
+## DELETE /config/[path]
+
+Changes Caddy's configuration at the named path to the JSON body of the request. DELETE deletes the target value.
+
+### Examples
+
+To unload the entire current configuration but leave the process running:
+
+curl -X DELETE "http://localhost:2019/config/"
+
+To stop only one of your HTTP servers:
+
+curl -X DELETE "http://localhost:2019/config/apps/http/servers/myserver"
+
+
+## Using @id in JSON
+
+You can embed IDs in your JSON document for easier direct access to those parts of the JSON.
+
+Simply add a field called "@id" to an object and give it a unique name. For example, if you had a reverse proxy handler that you wanted to access frequently:
+
+```json
+{
+ "@id": "my_proxy",
+ "handler": "reverse_proxy"
+}
+```
+
+To use it, simply make a request to the `/id/` API endpoint in the same way you would to the corresponding `/config/` endpoint, but without the whole path. The ID takes the request directly into that scope of the config for you.
+
+For example, to access the upstreams of the reverse proxy without an ID, the path would be something like
+
+```
+/config/apps/http/servers/myserver/routes/1/handle/0/upstreams
+```
+
+but with an ID, the path becomes
+
+```
+/id/my_proxy/upstreams
+```
+
+which is much easier to remember and write by hand.
diff --git a/src/docs/markdown/automatic-https.md b/src/docs/markdown/automatic-https.md
new file mode 100644
index 00000000..e2488c9b
--- /dev/null
+++ b/src/docs/markdown/automatic-https.md
@@ -0,0 +1,177 @@
+---
+title: "Automatic HTTPS"
+---
+
+# Automatic HTTPS
+
+**Caddy is the first and only web server to use HTTPS automatically _and by default_.**
+
+
+
+Automatic HTTPS provisions TLS certificates for all your sites and keeps them renewed. It also redirects HTTP to HTTPS for you! Caddy uses safe and modern defaults -- no downtime or extra configuration required.
+
+Here's a 28-second video showing how it works:
+
+
+
+
+
+## tl;dr
+
+Here's what you need to know:
+
+
+
+
+- If your domain's A/AAAA records point to your server,
+- ports 80 and 443 are open externally,
+- Caddy can bind to those ports (_or_ those ports are forwarded to Caddy),
+- your `$HOME` folder is writeable and persistent,
+- and your domain name appears somewhere relevant in the config,
+
+then your sites will probably be served over HTTPS automatically and without problems. You won't have to know or do anything else about it. It "just works" most of the time!
+
+
+
+If you are still testing your setup, however, please read on or you risk being rate limited by your CA. The rest of this page goes over the details for advanced use cases and troubleshooting purposes.
+
+
+
+## Activation
+
+Caddy implicitly activates automatic HTTPS when it knows a domain name (i.e. hostname) it is serving. Depending on how you run or configure Caddy, there are various ways to tell Caddy which domain names to use:
+
+- The [Caddyfile](/docs/caddyfile/concepts#addresses)
+- A [host matcher](/docs/json/apps/http/servers/routes/match/host/) in a route
+- Command line flags like [--domain](/command-line#caddy-file-server) or [--from](/docs/command-line#caddy-reverse-proxy)
+- The [automate](/docs/json/apps/tls/certificates/automate/) certificate loader
+
+Any of the following will prevent automatic HTTPS from being activated, either in whole or in part:
+
+- [Explicitly disabling it](/docs/json/apps/http/servers/automatic_https/)
+- Not providing any qualifying hostnames in the config
+- Listening exclusively on the HTTP port
+- Manually loading certificates (unless [this config property](/docs/json/apps/http/servers/automatic_https/ignore_loaded_certificates/) is true)
+
+
+## Effects
+
+When automatic HTTPS is activated, the following occurs:
+
+- Certificates are obtained and renewed for [qualifying domain names](#hostname-requirements)
+- The default port (if any) is changed to the [HTTPS port](/docs/json/apps/http/https_port/) 443
+- HTTP is redirected to HTTPS (this uses [HTTP port](/docs/json/apps/http/http_port/) 80)
+
+Automatic HTTPS never overrides explicit configuration.
+
+You can [customize or disable automatic HTTPS](/docs/json/apps/http/servers/automatic_https/) if necessary.
+
+
+## Hostname requirements
+
+A hostname qualifies for automatic HTTPS if it:
+
+- is not empty
+- is not localhost
+- is not an IP address
+- does not start or end with a dot ([RFC 1034](https://tools.ietf.org/html/rfc1034#section-3.5))
+- consists only of alphanumerics, hyphens, and dots
+ - with the exception of a single wildcard `*` as the left-most label (this requires the DNS challenge if using Let's Encrypt)
+
+
+## Testing
+
+To test or experiment with your Caddy configuration, make sure you [change the ACME endpoint](/docs/json/apps/tls/automation/policies/management/acme/ca/) to a staging or development URL, otherwise you are likely to hit rate limits which can block your access to HTTPS for up to a week, depending on which rate limit you hit.
+
+Caddy's default CA is [Let's Encrypt](https://letsencrypt.org/), which has a [staging endpoint](https://letsencrypt.org/docs/staging-environment/) that is not subject to the same [rate limits](https://letsencrypt.org/docs/rate-limits/):
+
+```
+https://acme-staging-v02.api.letsencrypt.org/directory
+```
+
+
+## ACME challenges
+
+Obtaining a publicly-trusted TLS certificate requires validation from a publicly-trusted, third-party authority. These days, this validation process is automated with the [ACME protocol](https://tools.ietf.org/html/rfc8555), and can be performed one of three ways ("challenge types"), described below.
+
+The first two challenge types are enabled by default. If multiple challenges are enabled, Caddy chooses one at random to avoid accidental dependence on a particular challenge.
+
+
+### HTTP challenge
+
+The HTTP challenge performs an authoritative DNS lookup for the candidate hostname's A/AAAA record, then requests a temporary cryptographic resource over port 80 using HTTP. If the CA sees the expected resource, a certificate is issued.
+
+This challenge requires port 80 to be externally accessible. If Caddy cannot listen on port 80, packets from port 80 must be forwarded to Caddy's [HTTP port](/docs/json/apps/http/http_port/).
+
+This challenge is enabled by default and does not require explicit configuration.
+
+
+### TLS-ALPN challenge
+
+The TLS-ALPN challenge performs an authoritative DNS lookup for the candidate hostname's A/AAAA record, then requests a temporary cryptographic resource over port 443 using a TLS handshake containing special ServerName and ALPN values. If the CA sees the expected resource, a certificate is issued.
+
+This challenge requires port 443 to be externally accessible. If Caddy cannot listen on port 443, packets from port 443 must be forwarded to Caddy's [HTTPS port](/docs/json/apps/http/https_port/).
+
+This challenge is enabled by default and does not require explicit configuration.
+
+
+### DNS challenge
+
+The DNS challenge performs an authoritative DNS lookup for the candidate hostname's TXT records, and looks for a special TXT record with a certain value. If the CA sees the expected value, a certificate is issued.
+
+This challenge does not require any open ports, and the server requesting a certificate does not need to be externally accessible.
+
+However, the DNS challenge requires configuration. Caddy needs to know the credentials to access your domain's DNS provider so it can set (and clear) the special TXT records.
+
+If the DNS challenge is enabled, other challenges are disabled by default.
+
+
+## On-Demand TLS
+
+Caddy pioneers a new technology we call On-Demand TLS, which obtains the certificate for a name during the first TLS handshake that requires it, rather than at config load. You can enable it using the [on_demand](/docs/json/apps/tls/automation/on_demand/) property in your TLS automation config.
+
+This feature can be useful if you do not know all the domain names up front. Certificates managed on-demand will be obtained and renewed in the foreground of TLS handshakes that require it. This process slows down only the initial TLS handshake; all others will not be affected.
+
+To prevent abuse, you should specify rate limits and/or an endpoint that Caddy can query to ask if a certificate is allowed to be obtained for a hostname. Essentially, you still need a way to provide a whitelist, but this can be managed dynamically using your own scripts or programs if you'd rather keep Caddy's config more static.
+
+**Future support:** This feature relies on the CA issuing certificates without delay. If instantaneous issuance becomes uncommon among ACME CAs, we may discontinue this feature in Caddy.
+
+Due to its deferred nature and the possibility that some ACME challenges can take more than a few seconds (especially when using the DNS challenge), we typically recommend using On-Demand TLS only when there are specific technical or operational advantages to you.
+
+
+## Errors
+
+Caddy does its best to continue if errors occur with certificate management.
+
+By default, certificate management is performed in the background. This means it will not block startup or slow down your sites. However, it also means that the server will be running even before all certificates are available. Running in the background allows Caddy to retry with exponential backoff over a long period of time.
+
+Here's what happens if there's an error obtaining or renewing a certificate:
+
+1. Caddy retries once after a brief pause just in case it was a fluke
+2. Caddy pauses briefly, then switches to the next enabled challenge type
+3. After all enabled challenge types have been tried, it backs off exponentially
+ - Maximum of 1 day between attempts
+ - For up to 30 days
+
+ACME challenges take at least a few seconds, and internal rate limiting helps mitigate accidental abuse. Caddy uses internal rate limiting in addition to what you or the CA configure so that you can hand Caddy a platter with a million domain names and it will gradually -- but as fast as it can -- obtain certificates for all of them.
+
+Caddy's internal rate limit is currently 10 attempts per ACME account per minute.
+
+
+## Storage
+
+Caddy will store public certificates, private keys, and other assets in its [configured storage facility](/docs/json/storage/) (or the default one, if not configured -- see link for details).
+
+**The main thing you need to know is that the `$HOME` folder must be writeable and persistent.**
+
+Any Caddy instances that are configured to use the same storage will automatically share those resources and coordinate certificate management as a cluster.
+
+Before attempting any ACME transactions, Caddy will test the configured storage to ensure it is writeable and has sufficient capacity. This helps reduce unnecessary rate limit contention.
+
+
+
+## Wildcard certificates
+
+Caddy can obtain and manage wildcard certificates when it is configured to serve a site with a qualifying wildcard name. A site name qualifies for a wildcard if only its left-most domain label is a wildcard. For example, `*.example.com` qualifies, but these do not: `sub.*.example.com`, `foo*.example.com`, `*bar.example.com`, and `*.*.example.com`.
+
+To get a wildcard, you simply need to enable the [DNS challenge](#dns-challenge) and use a wildcard domain in your config. We recommend using wildcards only when you have so many subdomains that you would encounter CA rate limits trying to obtain certificates for them all.
diff --git a/src/docs/markdown/caddyfile-tutorial.md b/src/docs/markdown/caddyfile-tutorial.md
new file mode 100644
index 00000000..2f0bb45b
--- /dev/null
+++ b/src/docs/markdown/caddyfile-tutorial.md
@@ -0,0 +1,257 @@
+---
+title: Caddyfile Tutorial
+---
+
+# Caddyfile Tutorial
+
+This tutorial will teach you the basics of the HTTP Caddyfile so that you can quickly and easily produce good-looking, functional site configs.
+
+**Objectives:**
+- 🔲 First site
+- 🔲 Static file server
+- 🔲 Templates
+- 🔲 Compression
+- 🔲 Multiple sites
+- 🔲 Matchers
+- 🔲 Environment variables
+- 🔲 Comments
+
+**Prerequisites:**
+- Basic terminal / command line skills
+- Basic text editor skills
+- `caddy` in your PATH
+
+---
+
+Create a new text file named `Caddyfile` (no extension).
+
+The first thing you should type is your site's [address](/docs/caddyfile/concepts#addresses):
+
+```
+localhost
+```
+
+Then hit enter and type what you want it to do. For this tutorial, make your Caddyfile look like this:
+
+```
+localhost
+
+respond "Hello, world!"
+```
+
+Save that and run Caddy:
+
+caddy run
+
+
+
+
+Open [localhost:2015](http://localhost:2015) in your browser and see your web server working!
+
+Why 2015? Caddy's default port is 2015. Since Caddy cannot activate [automatic HTTPS](/docs/automatic-https) with a hostname like "localhost", it leaves the port unchanged.
+
+Stop Caddy by pressing Ctrl+C in your terminal.
+
+That's not particularly exciting, so let's change our static response to a [file server](/docs/caddyfile/directives/file_server) with directory listings enabled:
+
+```
+localhost
+
+file_server browse
+```
+
+Save your Caddyfile and run Caddy again. Then refresh your browser tab. You should either see a list of files or an HTML page if there is an index file in the current directory.
+
+
+
+## Adding functionality
+
+Let's do something interesting with our file server: serve a templated page. Create a new file and paste this into it:
+
+```html
+
+
+
+ export SITE_ADDRESS=localhost:9055
+
+Then you can use it like this in the Caddyfile:
+
+```
+{$SITE_ADDRESS}
+
+file_server
+```
+
+Before the Caddyfile is parsed, it will be expanded to:
+
+```
+localhost:9055
+
+file_server
+```
+
+You can use environment variables anywhere in the Caddyfile, for any number of tokens.
+
+
+
+
+## Comments
+
+One last thing that you will find most helpful: if you want to remark or note anything in your Caddyfile, you can use comments, starting with `#`:
+
+```
+# this starts a comment
+```
+
+
+
+## Further reading
+
+- [Caddyfile concepts](/docs/caddyfile/concepts)
+- [Directives](/docs/caddyfile/directives)
\ No newline at end of file
diff --git a/src/docs/markdown/caddyfile.md b/src/docs/markdown/caddyfile.md
new file mode 100644
index 00000000..ed93f35b
--- /dev/null
+++ b/src/docs/markdown/caddyfile.md
@@ -0,0 +1,35 @@
+---
+title: The Caddyfile
+---
+
+# The Caddyfile
+
+The **Caddyfile** is a convenient Caddy configuration format for humans. It is most people's favorite way to use Caddy because it is easy to write, easy to understand, and expressive enough for most use cases.
+
+It looks like this:
+
+```
+example.com
+
+root * /path/to/public_html
+try_files {path} /index.php?{query}&p={path}
+php_fastcgi unix//run/php/php-fpm.sock
+file_server
+```
+
+(That's a real, production-ready Caddyfile that serves a Craft CMS site with fully-managed HTTPS.)
+
+The basic idea is that you first type the address of your site, then the features or functionality you need your site to have.
+
+## Menu
+
+- #### [Quick start guide](/docs/quick-starts/caddyfile)
+- #### [List of directives](/docs/caddyfile/directives)
+- #### [Caddyfile concepts](/docs/caddyfile/concepts)
+- #### [Global options](/docs/caddyfile/options)
+
+
+
+## Note
+
+The Caddyfile is just a [config adapter](/docs/config-adapters) for Caddy. It is usually preferred when manually crafting configurations by hand, but is not as expressive, flexible, or programmable as Caddy's [native JSON structure](/docs/json/). If you are automating your Caddy configurations/deployments, you may wish to use JSON with [Caddy's API](/docs/api). (You can actually use the Caddyfile with the API too, just to a limited extent.)
diff --git a/src/docs/markdown/caddyfile/concepts.md b/src/docs/markdown/caddyfile/concepts.md
new file mode 100644
index 00000000..c59504c4
--- /dev/null
+++ b/src/docs/markdown/caddyfile/concepts.md
@@ -0,0 +1,354 @@
+---
+title: Caddyfile Concepts
+---
+
+# Caddyfile Concepts
+
+This document will help you learn about the HTTP Caddyfile in detail.
+
+1. [Structure](#structure)
+2. [Addresses](#addresses)
+3. [Matchers](#matchers)
+4. [Placeholders](#placeholders)
+5. [Snippets](#snippets)
+6. [Comments](#comments)
+7. [Environment variables](#environment-variables)
+8. [Global options](#global-options)
+
+
+## Structure
+
+The Caddyfile's structure can be described visually:
+
+
+
+Key points:
+
+- An optional **global options block** can be the very first thing in the file.
+- Otherwise, the first line of the Caddyfile is **always** the address(es) of the site to serve.
+- All directives **must** go in a site block. There is no global scope or inheritence across site blocks.
+- If there is **only one site block**, its curly braces `{ }` are optional.
+
+A Caddyfile consists of at least one or more site blocks, which always starts with one or more [addresses](#addresses) for the site. Any directives appearing before the address will be confusing to the parser.
+
+### Blocks
+
+Opening and closing a **block** is done with curly braces:
+
+```
+... {
+ ...
+}
+```
+
+- The open curly brace `{` must be at the end of its line.
+- The close curly brace `}` must be on its own line.
+
+When there is only one site block, the curly braces (and indentation) are optional. This is for convenience to quickly define a single site, for example, this:
+
+```
+localhost
+
+reverse_proxy /api/* localhost:9001
+file_server
+```
+
+is equivalent to:
+
+```
+localhost {
+ reverse_proxy /api/* localhost:9001
+ file_server
+}
+```
+
+when you have only a single site block; it's a matter of preference.
+
+To configure multiple sites with the same Caddyfile, you **must** use curly braces around each one to separate their configurations:
+
+```
+example1.com {
+ root * /www/example.com
+ file_server
+}
+
+example2.com {
+ reverse_proxy localhost:9000
+}
+```
+
+### Directives
+
+**Directives** are keywords which customize how the site is served. For example, a complete file server config might look like this:
+
+```
+localhost
+
+file_server
+```
+
+Or a reverse proxy:
+
+```
+localhost
+
+reverse_proxy localhost:9000
+```
+
+In these examples, `file_server` and `reverse_proxy` are directives. Directives are the first word on a line in a site block.
+
+In the second example, `localhost:9000` is an **argument** because it appears on the same line after the directive.
+
+**Subdirectives** can appear in directive blocks:
+
+```
+localhost
+
+reverse_proxy localhost:9000 localhost:9001 {
+ lb_policy first
+}
+```
+
+Here, `lb_policy` is a subdirective to `reverse_proxy` (it sets the load balancing policy to use between backends).
+
+
+
+
+## Addresses
+
+An address always appears at the top of the site block, and is usually the first thing in the Caddyfile.
+
+These are examples of valid addresses:
+
+
+
+- `localhost`
+- `example.com`
+- `:443`
+- `http://example.com`
+- `localhost:8080`
+- `127.0.0.1`
+- `[::1]:2015`
+- `example.com/foo`
+
+From the address, Caddy can potentially infer the scheme, host, port, and path prefix of your site. The default port is 2015 unless [automatic HTTPS](/docs/automatic-https#activation) is activated, which changes it to the HTTPS port.
+
+If you specify a hostname, only requests with a matching Host header will be honored. In other words, if the site address is `localhost`, then Caddy will not match requests to `127.0.0.1`.
+
+If multiple sites share the same definition, you can list all of them:
+
+```
+localhost:8080, example.com, www.site.com
+```
+
+or
+
+```
+localhost:8080,
+example.com,
+www.site.com
+```
+
+Notice how the commas indicate the continuation of addresses.
+
+An address must be unique; you cannot specify the same address more than once.
+
+
+## Matchers
+
+By default, a directive that injects an HTTP handler applies to all requests (unless otherwise documented).
+
+**Request matchers** can be used to classify requests by a given criteria. This concept originates in the [underlying JSON](/docs/json/apps/http/servers/routes/match/) structure, and it's important to know how to use them in the Caddyfile. With matchers, you can specify exactly which requests a directive applies to.
+
+To limit a directive's scope, use a **matcher token** immediately after the directive. It can be one of these forms:
+
+1. **`*`** to match all requests (wildcard; default).
+2. **`/path`** start with a forward slash to match a request path.
+3. **`@name`** to specify a _named matcher_.
+
+Matcher tokens are usually optional. If a matcher token is omitted, it is the same as a wildcard matcher (`*`).
+
+### Wildcard matcher
+
+The wildcard matcher `*` matches all requests, and is only needed if a matcher token is required. For example, if the first argument you want to give a directive also happens to be a path, it would look exactly like a path matcher! So you can use a wildcard matcher to disambiguate, for example:
+
+```
+root * /home/www/mysite
+```
+
+Otherwise, this matcher is not often used. It is convenient to omit it when possible; just a matter of preference.
+
+### Path matcher
+
+Because matching by path is so common, a single path matcher can be inlined, like so:
+
+```
+redir /old-article.html /new-article.html
+```
+
+Path matcher tokens must start with a forward slash `/`.
+
+Note that [path matching](/docs/json/apps/http/servers/routes/match/path/) is an exact match by default; you must append a `*` for a fast prefix match.
+
+
+### Named matcher
+
+Defining a matcher with a unique name gives you more flexibility:
+
+```
+@name {
+ ...
+}
+```
+
+Then you can use the matcher like so: `@name`
+
+For example:
+
+```
+@websockets {
+ header_regexp Connection Upgrade
+ header Upgrade websocket
+}
+reverse_proxy @websockets localhost:6001
+```
+
+This example only proxies requests that have a header field named "Connection" containing the word "Upgrade", and another header named "Upgrade" with a value of "websocket".
+
+The following matcher modules are built-in:
+
+```
+@name {
+ file {
+ root ]
+```
+
+- **<to>** is the target location. Becomes the response's Location header.
+- **<code>** is the HTTP status code to use for the redirect. Can be:
+ - A positive integer in the 3xx range
+ - `temporary` for a temporary redirect (302; default)
+ - `permanent` for a permanent redirect (301)
+ - `html` to use an HTML document to perform the redirect (useful for redirecting browsers but not API clients)
+
+
+
+## Examples
+
+Redirect all requests to `https://example.com`:
+
+```
+redir https://example.com
+```
+
+Same, but preserve the existing URI:
+
+```
+redir https://example.com{uri}
+```
+
+Same, but permanent:
+
+```
+redir https://example.com{uri} permanent
+```
\ No newline at end of file
diff --git a/src/docs/markdown/caddyfile/directives/request_header.md b/src/docs/markdown/caddyfile/directives/request_header.md
new file mode 100644
index 00000000..4956e501
--- /dev/null
+++ b/src/docs/markdown/caddyfile/directives/request_header.md
@@ -0,0 +1,28 @@
+---
+title: request_header (Caddyfile directive)
+---
+
+# request_header
+
+Manipulates HTTP header fields on the request. It can set, add, and delete header values, or perform replacements using regular expressions.
+
+
+## Syntax
+
+```
+request_header [] [[+|-] [|] []]
+```
+
+- **<field>** is the name of the header field. By default, will overwrite any existing field of the same name. Prefix with `+` to add the field instead of replace, or prefix with `-` to remove the field.
+- **<value>** is the header field value, if adding or setting a field.
+- **<find>** is the substring or regular expression to search for.
+- **<replace>** is the replacement value; required if performing a search-and-replace.
+
+
+## Examples
+
+Remove the Referer header from the request:
+
+```
+request_header -Referer
+```
diff --git a/src/docs/markdown/caddyfile/directives/respond.md b/src/docs/markdown/caddyfile/directives/respond.md
new file mode 100644
index 00000000..3d92f09b
--- /dev/null
+++ b/src/docs/markdown/caddyfile/directives/respond.md
@@ -0,0 +1,47 @@
+---
+title: respond (Caddyfile directive)
+---
+
+# respond
+
+Writes a hard-coded/static response to the client.
+
+
+## Syntax
+
+```
+respond [] | [] {
+ body
+ close
+}
+```
+
+- **<status>** is the HTTP status code to write. Default 200.
+- **<body>** is the response body to write.
+- **body** is an alternate way to provide a body; convenient if it is multiple lines.
+- **close** will close the client's connection to the server after writing the response.
+
+To clarify, the first non-matcher argument can be either a 3-digit status code or a response body string. If it is a body, the next argument can be the status code.
+
+
+## Examples
+
+Write a 200 status with an empty body to all health checks:
+
+```
+respond /health-check 200
+```
+
+Write a simple response body to all requests:
+
+```
+respond "Hello, world!"
+```
+
+Write an error response and close the connection:
+
+```
+respond /secret/* "Access denied" 403 {
+ close
+}
+```
diff --git a/src/docs/markdown/caddyfile/directives/reverse_proxy.md b/src/docs/markdown/caddyfile/directives/reverse_proxy.md
new file mode 100644
index 00000000..6b076240
--- /dev/null
+++ b/src/docs/markdown/caddyfile/directives/reverse_proxy.md
@@ -0,0 +1,165 @@
+---
+title: reverse_proxy (Caddyfile directive)
+---
+
+# reverse_proxy
+
+Proxies requests to one or more backends with configurable transport, load balancing, health checking, header manipulation, and buffering options.
+
+
+## Syntax
+
+```
+reverse_proxy [] [] {
+ # backends
+ to
+ ...
+
+ # load balancing
+ lb_policy []
+ lb_try_duration
+ lb_try_interval
+
+ # active health checking
+ health_path
+ health_port
+ health_interval
+ health_timeout
+ health_status
+ health_body
+
+ # passive health checking
+ fail_duration
+ max_fails
+ unhealthy_status
+ unhealthy_latency
+ unhealthy_request_count
+
+ # streaming
+ flush_interval
+
+ # header manipulation
+ header_up [+|-] [ []]
+ header_down [+|-] [ []]
+
+ # round trip
+ transport {
+ ...
+ }
+}
+```
+
+- **<upstreams...>** is a list of upstreams (backends) to which to proxy.
+- **to** is an alternate way to specify the list of upstreams, one (or more) per line.
+
+**Load balancing** is used whenever more than one upstream is defined.
+
+- **lb_policy** is the name of the load balancing policy, along with any options. Default: `random`. Can be:
+ - `first` - choose first available upstream
+ - `header` - map request header to sticky upstream
+ - `ip_hash` - map client IP to sticky upstream
+ - `least_conn` - choose upstream with fewest number of current requests
+ - `random` - randomly choose an upstream
+ - `random_choose ` - selects two or more upstreams randomly, then chooses one with least load (`n` is usually 2)
+ - `round_robin` - iterate each upstream in turn
+ - `uri_hash` - map URI to sticky upstream
+
+- **lb_try_duration** is a [duration value](/docs/conventions#durations) that defines how long to try selecting available backends for each request if the next available host is down. By default, this retry is disabled. Clients will wait for up to this long while the load balancer tries to find an available upstream host.
+- **lb_try_interval** is a [duration value](/docs/conventions#durations) that defines how long to wait between selecting the next host from the pool. Default is `250ms`. Only relevant when a request to an upstream host fails. Be aware that setting this to 0 with a non-zero `lb_try_duration` can cause the CPU to spin if all backends are down and latency is very low.
+
+**Active health checks** perform health checking in the background on a timer:
+
+- **health_path** is the URI path for active health checks.
+- **health_port** is the port to use for active health checks, if different from the upstream's port.
+- **health_interval** is a [duration value](/docs/conventions#durations) that defines how often to perform active health checks.
+- **health_timeout** is a [duration value](/docs/conventions#durations) that defines how long to wait for a reply before marking the backend as down.
+- **health_status** is the HTTP status code to expect from a healthy backend. Can be a 3-digit status code or a status code class ending in `xx`, for example: `200` (default) or `2xx`.
+- **health_body** is a substring or regular expression to match on the response body of an active health check. If the backend does not return a matching body, it will be marked as down.
+
+**Passive health checks** happen inline with actual proxied requests:
+
+- **fail_duration** is a [duration value](/docs/conventions#durations) that defines how long to remember a failed request. A duration > 0 enables passive health checking.
+- **max_fails** is the maximum number of failed requests within fail_timeout that are needed before considering a backend to be down; must be >= 1; default is 1.
+- **unhealthy_status** counts a request as failed if the response comes back with one of these status codes. Can be a 3-digit status code or a status code class ending in `xx`, for example: `404` or `5xx`.
+- **unhealthy_latency** is a [duration value](/docs/conventions#durations) that counts a request as failed if it takes this long to get a response.
+- **unhealthy_request_count** is the permissible number of simultaneous requests to a backend before marking it as down.
+
+The proxy **buffers responses** by default for wire efficiency:
+
+- **flush_interval** is a [duration value](/docs/conventions#durations) that defines how often Caddy should flush the buffered response body to the client. Set to -1 to disable buffering.
+
+It can also **manipulate headers** between itself and the backend:
+
+- **header_up** Sets, adds, removes, or performs a replacement in a request header going upstream to the backend.
+- **header_down** Sets, adds, removes, or performs a replacement in a response header coming downstream from the backend.
+
+Caddy's proxy **transport** is pluggable:
+
+- **transport** defines how to communicate with the backend. Default is `http`.
+
+The `http` and `http_ntlm` transports can look like this:
+
+```
+transport http {
+ read_buffer
+ write_buffer
+ dial_timeout
+ tls
+ tls_client_auth
+ tls_insecure_skip_verify
+ tls_timeout
+ tls_trusted_ca_certs
+ keepalive [off|]
+ keepalive_idle_conns
+}
+```
+
+The `http_ntlm` transport is identical to the `http` transport, but the HTTP version is always 1.1, and Keep-Alive is always disabled.
+
+- **read_buffer** is the size of the read buffer in bytes.
+- **write_buffer** is the size of the write buffer in bytes.
+- **dial_timeout** is how long to wait when connecting to the upstream socket.
+- **tls** uses HTTPS with the backend.
+- **tls_client_auth** specifies a certificate and key file to present for TLS client authentication with the backend.
+- **tls_insecure_skip_verify** turns off security. _Do not use in production._
+- **tls_timeout** is a [duration value](/docs/conventions#durations) that specifies how long to wait for the TLS handshake to complete.
+- **tls_trusted_ca_certs** is a list of PEM files that specify CA public keys to trust when connecting to the backend.
+- **keepalive** is either `off` or a [duration value](/docs/conventions#durations) that specifies how long to keep connections open.
+- **keepalive_idle_conns** defines the maximum number of connections to keep alive.
+
+The `fastcgi` transport can look like this:
+
+```
+transport fastcgi {
+ root
+ split
+ env
+}
+```
+
+- **root** is the root of the site. Default: `{http.vars.root}` or current working directory.
+- **split** is where to split the path to get PATH_INFO at the end of the URI.
+- **env** sets custom environment variables.
+
+
+## Examples
+
+Reverse proxy all requests to a local backend:
+
+```
+reverse_proxy localhost:9005
+```
+
+Load-balance all requests between 3 backends:
+
+```
+reverse_proxy node1:80 node2:80 node3:80
+```
+
+Same, but only requests within `/api`, and with header affinity:
+
+```
+reverse_proxy /api/* node1:80 node2:80 node3:80 {
+ lb_policy header X-My-Header
+}
+```
diff --git a/src/docs/markdown/caddyfile/directives/rewrite.md b/src/docs/markdown/caddyfile/directives/rewrite.md
new file mode 100644
index 00000000..3989f5c6
--- /dev/null
+++ b/src/docs/markdown/caddyfile/directives/rewrite.md
@@ -0,0 +1,55 @@
+---
+title: rewrite (Caddyfile directive)
+---
+
+# rewrite
+
+Rewrites the request internally. A rewrite changes some or all of the request URI.
+
+The `rewrite` directive implies the intent to accept the request, but with modifications. It is mutually exclusive to other `rewrite` directives in the same block, so it is safe to define rewrites that would otherwise cascade into each other; only the first matching rewrite will be executed.
+
+
+## Syntax
+
+```
+rewrite []
+```
+
+- **** is the URI to set on the request. Only designated parts will be replaced. The URI path is any substring that comes before `?`. If `?` is omitted, then the whole token is considered to be the path.
+
+
+## Examples
+
+Rewrite all requests to `foo.html`, leaving any query string unchanged:
+
+```
+rewrite * /foo.html
+```
+
+Replace the query string on API requests with `a=b`, leaving the path unchanged:
+
+```
+rewrite /api/* ?a=b
+```
+
+Preserve the existing query string and add a key-value pair:
+
+```
+rewrite /api/* ?{query}&a=b
+```
+
+Change both the path and query string, preserving the original query string while adding the original path as the `p` parameter:
+
+```
+rewrite * /index.php?{query}&p={path}
+```
+
+
+## Similar directives
+
+There are other directives that perform rewrites, but imply a different intent or do the rewrite without a complete replacement of the URI:
+
+- [`strip_prefix`](/docs/caddyfile/directives/strip_prefix) strips a prefix from the request path.
+- [`strip_suffix`](/docs/caddyfile/directives/strip_suffix) strips a suffix from the request path.
+- [`uri_replace`](/docs/caddyfile/directives/uri_replace) performs a substring replacement on the request path.
+- [`try_files`](/docs/caddyfile/directives/try_files) rewrites the request based on the existence of files.
\ No newline at end of file
diff --git a/src/docs/markdown/caddyfile/directives/root.md b/src/docs/markdown/caddyfile/directives/root.md
new file mode 100644
index 00000000..e6f1cc4f
--- /dev/null
+++ b/src/docs/markdown/caddyfile/directives/root.md
@@ -0,0 +1,44 @@
+---
+title: root (Caddyfile directive)
+---
+
+# root
+
+Sets the root path of the site, used by various matchers and directives that access the file system. If unset, the default site root is the current working directory.
+
+Specifically, this directive sets the `{http.vars.root}` placeholder.
+
+
+## Syntax
+
+```
+root []
+```
+
+- **<path>** is the path to use for the site root.
+
+Note that a matcher token is usually required since the first argument is a path, which could look like a path matcher.
+
+## Examples
+
+Set the site root to `/home/user/public_html` for all requests:
+
+```
+root * /home/user/public_html
+```
+
+(A [wildcard matcher](/docs/caddyfile/concepts#wildcard-matcher) is required in this case because the first argument is ambiguous with a [path matcher](/docs/caddyfile/concepts#path-matcher).)
+
+Set the site root to `public_html` (relative to current working directory) for all requests:
+
+```
+root public_html
+```
+
+(No matcher token is required here because our site root is a relative path, so it does not start with a forward slash and thus is not ambiguous.)
+
+Set the site root only for requests in `/foo`:
+
+```
+root /foo/* /home/user/public_html/foo
+```
diff --git a/src/docs/markdown/caddyfile/directives/route.md b/src/docs/markdown/caddyfile/directives/route.md
new file mode 100644
index 00000000..d26507a8
--- /dev/null
+++ b/src/docs/markdown/caddyfile/directives/route.md
@@ -0,0 +1,76 @@
+---
+title: route (Caddyfile directive)
+---
+
+# route
+
+Evaluates a group of directives literally and as a single unit.
+
+Directives contained in a route block will not be reordered internally. Only HTTP handler directives (directives which add handlers or middleware to the chain) can be used in a route block.
+
+This directive is a special case in that its subdirectives are also regular directives.
+
+
+## Syntax
+
+```
+route [] {
+
+}
+```
+
+- **** is a list of directives or directive blocks, one per line, just like outside of a route block; except these directives will not be reordered. Only HTTP handler directives can be used.
+
+
+
+## Utility
+
+The `route` directive is helpful in certain advanced use cases or edge cases to take absolute control over parts of the HTTP handler chain.
+
+Because the order of HTTP middleware evaluation is significant, the Caddyfile will normally reorder directives after parsing to make the Caddyfile easier to use; you don't have to worry about what order you type things.
+
+While the built-in order is compatible with most sites, sometimes you need to take manual control over the order, either for the whole site or just a part of it. That's what the `route` directive is for.
+
+To illustrate, consider the case of two terminating handlers: `redir` and `file_server`. Both write the response to the client and do not call the next handler in the chain, so only one of these will be executed for a certain request. Which comes first? Normally, `redir` is executed before `file_server` because usually you would want to issue a redirect only in specific cases and serve files in the general case.
+
+However, there may be occasions where the second directive (`redir`) has a more specific matcher than the second (`file_server`). In other words, you want to redirect in the general case, and serve only a specific file.
+
+So you might try a Caddyfile like this (but this will not work as expected!):
+
+```
+example.com
+
+file_server /specific.html
+redir https://anothersite.com{uri}
+```
+
+The problem is that, internally, `redir` comes before `file_server`, but in this case the matcher for `redir` is a superset of the matcher for `file_server` (`*` is a superset of `/specific.html`).
+
+Fortunately, the solution is easy: just wrap those two directives in a `route` block:
+
+```
+example.com
+
+route {
+ file_server /specific.html
+ redir https://anothersite.com{uri}
+}
+```
+
+
+
+And now `file_server` will be chained in before `redir` because the order is taken literally.
+
+
+## Examples
+
+Strip `api/` prefix from request path just before proxying all API requests to a backend:
+
+```
+route /api/* {
+ strip_prefix api/
+ reverse_proxy localhost:9000
+}
+```
diff --git a/src/docs/markdown/caddyfile/directives/strip_prefix.md b/src/docs/markdown/caddyfile/directives/strip_prefix.md
new file mode 100644
index 00000000..1d4aaf37
--- /dev/null
+++ b/src/docs/markdown/caddyfile/directives/strip_prefix.md
@@ -0,0 +1,31 @@
+---
+title: strip_prefix (Caddyfile directive)
+---
+
+# strip_prefix
+
+Strips a given prefix from the request URI's path. If a matched request does not have the given path prefix, this directive is a no-op.
+
+
+## Syntax
+
+```
+strip_prefix []
+```
+
+- **<prefix>** is the prefix to strip from the request path. This value may omit the leading forward slash `/` and it will be assumed.
+
+
+## Examples
+
+Strip `api/` from the beginning of all request paths:
+
+```
+strip_prefix api/
+```
+
+An alternate way to describe the same thing, using a matcher:
+
+```
+strip_prefix /api/* /api
+```
\ No newline at end of file
diff --git a/src/docs/markdown/caddyfile/directives/strip_suffix.md b/src/docs/markdown/caddyfile/directives/strip_suffix.md
new file mode 100644
index 00000000..72b7693d
--- /dev/null
+++ b/src/docs/markdown/caddyfile/directives/strip_suffix.md
@@ -0,0 +1,25 @@
+---
+title: strip_suffix (Caddyfile directive)
+---
+
+# strip_suffix
+
+Strips a given suffix from the request URI's path. If a matched request does not have the given path suffix, this directive is a no-op.
+
+
+## Syntax
+
+```
+strip_suffix []
+```
+
+- **<suffix>** is the suffix to strip from the request path.
+
+
+## Examples
+
+Strip `.html` from the end of all request paths:
+
+```
+strip_suffix .html
+```
diff --git a/src/docs/markdown/caddyfile/directives/templates.md b/src/docs/markdown/caddyfile/directives/templates.md
new file mode 100644
index 00000000..70499d8f
--- /dev/null
+++ b/src/docs/markdown/caddyfile/directives/templates.md
@@ -0,0 +1,32 @@
+---
+title: templates (Caddyfile directive)
+---
+
+# templates
+
+Executes the response body as a [template](/docs/json/apps/http/servers/errors/routes/handle/templates/) document. Templates provide functional primitives for making simple dynamic pages. Features include HTTP subrequests, HTML file includes, Markdown rendering, JSON parsing, basic data structures, randomness, time, and more.
+
+
+## Syntax
+
+```
+templates [] {
+ mime
+ between
+ root
+}
+```
+
+- **mime** are the MIME types the templates middleware will act on; any responses that do not have a qualifying Content-Type will not be evaluated as templates. Default: `text/html text/plain`.
+- **between** are the opening and closing delimiters for template actions. Default: `{{printf "{{ }}"}}`. You can change them if they interfere with the rest of your document.
+- **root** is the site root, when using functions that access the file system.
+
+
+## Examples
+
+Enable templates on all requests:
+
+```
+templates
+```
+
diff --git a/src/docs/markdown/caddyfile/directives/tls.md b/src/docs/markdown/caddyfile/directives/tls.md
new file mode 100644
index 00000000..e0db1a96
--- /dev/null
+++ b/src/docs/markdown/caddyfile/directives/tls.md
@@ -0,0 +1,68 @@
+---
+title: tls (Caddyfile directive)
+---
+
+# tls
+
+Configures TLS for the site.
+
+**Caddy's default TLS settings are secure. Only change these settings if you have a good reason and understand the implications.** The most common use of this directive will be to specify an ACME account email address, change the ACME CA endpoint, or to provide your own certificates.
+
+Compatibility note: Due to its sensitive nature as a security protocol, deliberate adjustments to TLS defaults may be made in new minor or patch releases. Old or broken TLS versions, ciphers, features, etc. may be removed at any time. If your deployment is extremely sensitive to changes, you should explicitly specify those values which must remain constant, and be vigilant about upgrades. In almost every case, we recommend using the default settings.
+
+
+## Syntax
+
+```
+tls |[ ] {
+ protocols []
+ ciphers
+ curves
+ alpn
+ load
+ ca
+}
+```
+
+- **<email>** is the email address to use for the ACME account managing the site's certificates.
+- **<cert_file>** and **<key_file>** are the paths to the certificate and private key PEM files. Specifying just one is invalid; specifying both will disable automatic HTTPS.
+- **protocols** specifies the minimum and maximum protocol versions. Default min: `tls1.2`. Default max: `tls1.3`
+- **ciphers** specifies the list of cipher suites in descending preference order. Note that cipher suites are not customizable with TLS 1.3. Supported values are:
+ - TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
+ - TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
+ - TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
+ - TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
+ - TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256
+ - TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256
+ - TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA
+ - TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256
+ - TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA
+ - TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA
+ - TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256
+ - TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA
+ - TLS_RSA_WITH_AES_128_GCM_SHA256
+ - TLS_RSA_WITH_AES_256_GCM_SHA384
+ - TLS_RSA_WITH_AES_256_CBC_SHA
+ - TLS_RSA_WITH_AES_128_CBC_SHA256
+ - TLS_RSA_WITH_AES_128_CBC_SHA
+ - TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA
+ - TLS_RSA_WITH_3DES_EDE_CBC_SHA
+- **curves** specifies the list of EC curves to support. Supported values are:
+ - x25519
+ - p256
+ - p384
+ - p521
+- **alpn** is the list of values to advertise in the ALPN extension of the TLS handshake.
+- **load** specifies a list of folders from which to load PEM files that are certificate+key bundles.
+- **ca** changes the ACME CA endpoint. This is most often used to use [Let's Encrypt's staging endpoint](https://letsencrypt.org/docs/staging-environment/) or an internal ACME server. (To change this value for the whole Caddyfile, use the `acme_ca` [global option](/docs/caddyfile/options) instead.)
+
+
+
+## Examples
+
+Use a custom certificate and key:
+
+```
+tls cert.pem key.pem
+```
+
diff --git a/src/docs/markdown/caddyfile/directives/try_files.md b/src/docs/markdown/caddyfile/directives/try_files.md
new file mode 100644
index 00000000..b23d0aeb
--- /dev/null
+++ b/src/docs/markdown/caddyfile/directives/try_files.md
@@ -0,0 +1,51 @@
+---
+title: try_files (Caddyfile directive)
+---
+
+# try_files
+
+Rewrites the request URI path to the first of the listed files which exists in the site root. If no files match, no rewrite is performed.
+
+
+## Syntax
+
+```
+try_files
+```
+
+- **** is the list of files to try. The URI will be rewritten to the first one that exists. To match directories, append a trailing forward slash `/` to the path. All file paths are relative to the site [root](/docs/caddyfile/directives/root). Each argument may also contain a query string, in which case the query string will also be changed if it matches that particular file.
+
+
+## Expanded form
+
+The `try_files` directive is basically a shortcut for:
+
+```
+@try_files {
+ file {
+ try_files
+ }
+}
+rewrite @try_files {http.matchers.file.relative}
+```
+
+
+## Examples
+
+If the request does not match any static files, rewrite to an index/router file:
+
+```
+try_files {path} /index.php
+```
+
+Same, but adding the original path to the query string:
+
+```
+try_files {path} /index.php?{query}&p={path}
+```
+
+Same, but also match directories:
+
+```
+try_files {path} {path}/ /index.php?{query}&p={path}
+```
diff --git a/src/docs/markdown/caddyfile/directives/uri_replace.md b/src/docs/markdown/caddyfile/directives/uri_replace.md
new file mode 100644
index 00000000..139b6010
--- /dev/null
+++ b/src/docs/markdown/caddyfile/directives/uri_replace.md
@@ -0,0 +1,27 @@
+---
+title: uri_replace (Caddyfile directive)
+---
+
+# uri_replace
+
+Performs a substring or regular expression replacement in the request URI.
+
+
+## Syntax
+
+```
+uri_replace [] []
+```
+
+- **<find>** is the substring or regular expression to search for.
+- **<replace>** is the replacement value.
+- **<limit>** is an optional limit to the number of replacements.
+
+
+## Examples
+
+Replace "/docs/" with "/v1/docs/" in any request URI:
+
+```
+uri_replace * /docs/ /v1/docs/
+```
diff --git a/src/docs/markdown/caddyfile/options.md b/src/docs/markdown/caddyfile/options.md
new file mode 100644
index 00000000..98ed50d8
--- /dev/null
+++ b/src/docs/markdown/caddyfile/options.md
@@ -0,0 +1,43 @@
+---
+title: Global options (Caddyfile)
+---
+
+# Global options
+
+The Caddyfile has a way for you to specify options that apply globally. Some options act as default values, while others customize the behavior of the Caddyfile [adapter](/docs/config-adapters).
+
+The very top of your Caddyfile can be a **global options block**. This is a block that has no keys:
+
+```
+{
+ ...
+}
+```
+
+There can only be one at most, and it must be the first block of the Caddyfile.
+
+Possible options are:
+
+```
+{
+ http_port
+ https_port
+ order first|last|[before|after ]
+ storage {
+
+ }
+ experimental_http3
+ acme_ca
+ email
+ admin
+}
+```
+
+- **http_port** is the port for the server to use for HTTP. For internal use only; does not change the HTTP port for clients. Default: 80
+- **https_port** is the port for the server to use for HTTPS. For internal use only; does not change the HTTPS port for clients. Default: 443
+- **order** sets or changes the standard order of HTTP handler directive(s). Can set directives to be `first` or `last`, or `before` or `after` another directive.
+- **storage** configures Caddy's storage mechanism. Default: `file_system`
+- **experimental_http3** enables experimental draft HTTP/3 support. Note that HTTP/3 is not a finished spec and client support is extremely limited. This option will go away in the future. _This option is not subject to compatibility promises._
+- **acme_ca** specifies the URL to the ACME CA's directory. It is strongly recommended to set this to Let's Encrypt's [staging endpoint](https://letsencrypt.org/docs/staging-environment/) for testing or development. Default: Let's Encrypt's production endpoint.
+- **email** is your email address. Mainly used when creating an ACME account with your CA, and is highly recommended in case there are problems with your certificates.
+- **admin** customizes the admin API endpoint.
diff --git a/src/docs/markdown/caddyfile/spec.md b/src/docs/markdown/caddyfile/spec.md
new file mode 100644
index 00000000..aae85ff9
--- /dev/null
+++ b/src/docs/markdown/caddyfile/spec.md
@@ -0,0 +1,203 @@
+---
+title: Caddyfile Spec
+---
+
+TODO: this page is unfinished
+
+
+
+# Caddyfile Specification
+
+This page describes the syntax of the Caddyfile. If it is your first time writing a Caddyfile, try the Caddyfile primer tutorial instead. This page is not beginner-friendly; it is technical and kind of boring.
+
+Although this article is verbose, the Caddyfile is designed to be easily readable and writable by humans. You will find that it is easy to remember, not cumbersome, and flows off the fingers.
+
+The term "Caddyfile" often refers to a file, but more generally means a blob of Caddy configuration text. A Caddyfile can be used to configure any Caddy server type: HTTP, DNS, etc. The basic structure and syntax of the Caddyfile is the same for all server types, but semantics change. Because of this variability, this document treats the Caddyfile only as the generic configuration syntax as it applies to all server types. Caddyfile documentation for specific types may be found within their respective docs. For instance, the HTTP server documents the semantics of its Caddyfile.
+
+#### Topics
+
+1. [File format & encoding](#)
+2. [Lexical syntax](#)
+3. [Structure](#)
+4. [Labels](#)
+5. [Directives](#)
+6. [Environment variables](#)
+7. [Import](#)
+8. [Reusable snippets](#)
+9. [Examples](#)
+
+
+## File Format & Encoding
+
+The Caddyfile is plain Unicode text encoded with UTF-8. Each code point is distinct; specifically, lowercase and uppercase characters are different. A leading byte order mark (0xFEFF), if present, will be ignored.
+
+
+
+## Lexical Syntax
+
+A token is a sequence of whitespace-delimited characters in the Caddyfile. A token that starts with quotes "
is read literally (including whitespace) until the next instance of quotes "
that is not escaped. Quote literals may be escaped with a backslash like so: \"
. Only quotes are escapable. “Smart quotes” are not valid as quotes.
+
+Lines are delimited with the \n
(newline) character only. Carriage return \r
is discarded unless quoted. Blank, unquoted lines are allowed and ignored.
+
+Comments are discarded by the lexer. Comments begin with an unquoted hash #
and continue to the end of the line. Comments may start a line or appear in the middle of a line as part of an unquoted token. For the purposes of this document, commented and blank lines are no longer considered.
+
+Tokens are then evaluated by the parser for structure.
+
+## Structure
+
+A Caddyfile has no global scope or inheritence between separate blocks. The most global unit of the Caddyfile is an entry. An entry consists of a list of labels and a definition associated with those labels. A label is a string identifier, and a definition is a body (one or more lines) of tokens grouped together in a block:
+
+list of labels
+definition (block)
+
+A Caddyfile with only one entry may consist simply of the label line(s) followed by the definition on the next line(s), as shown above. However, a Caddyfile with more than one entry must enclose each definition in curly braces { }
. The opening curly brace {
must be at the end of the label line, and the closing curly brace }
must be the only token on its line:
+
+list of labels {
+definition (block)
+}
+list of labels {
+definition (block)
+}
+
+Consistent tab indentation is encouraged within blocks enclosed by curly braces.
+
+
+The first line of a Caddyfile is always a label line. Comment lines, empty lines, and import lines are the exceptions.
+
+
+Labels
+
+Labels are the only tokens that appear outside of blocks (with one exception being the import directive). A label line may have just one label:
+
+label
+
+or several labels, separated by spaces:
+
+label1 label2 ...
+
+If many labels are to head a block, the labels may be suffixed with a comma. A comma-suffixed label may be followed by a newline, in which case the next line will be considered part of the same line:
+
+label1,
+label2
+
+Mixing of these patterns is valid (but discouraged), as long as the last label of the line has a comma if the next line is to continue the list of labels:
+
+label1 label2,
+label3, label4,
+label5
+
+A definition with multiple labels is replicated across each label as if they had been defined separately but with the same definition.
+
+
+Directives
+
+The body of the definition follows label lines. The first token of each line in a definition body is a directive. Every token after the directive on the same line is an argument. Arguments are optional:
+
+directive1
+directive2 arg1 arg2
+directive3 arg3
+
+Commas are not acceptable delimiters for arguments; they will be treated as part of the argument value. Arguments are delimited solely by same-line whitespace.
+
+
+Directives may span multiple lines by opening a block. Blocks are enclosed by curly braces { }
. The opening curly brace {
must be at the end of the directive's first line, and the closing curly brace }
must be the only token on its line:
+
+directive {
+...
+}
+
+Within a directive block, the first token of each line may be considered a subdirective or property, depending on how it is used (other terms may be applied). And as before, they can have arguments:
+
+directive arg1 {
+subdir arg2 arg3
+...
+}
+
+Subdirectives cannot open new blocks. In other words, nested directive blocks are not supported. If a directive block is empty, the curly braces should be omitted entirely.
+
+
+Environment Variables
+
+Any token (label, directive, argument, etc.) may contain or consist solely of an environment variable, which takes the Unix form or Windows form, enclosed in curly braces { }
without extra whitespace:
+
+label_{$ENV_VAR_1}
+directive {%ENV_VAR_2%}
+
+Either form works on any OS. A single environment variable does not expand out into multiple tokens, arguments, or values.
+
+
+Import
+
+The import directive is a special case, because it can appear outside a definition block. The consequence of this is that no label can take on the value of "import".
+
+
+Where an import line is, that line will be replaced with the contents of the imported file, unmodified. See the import docs for more information.
+
+
+Reusable Snippets
+
+You can define snippets to be reused later in your Caddyfile by defining a block with a single-token label surrounded by parentheses:
+
+(mysnippet) {
+...
+}
+
+Then you can invoke the snippet with the import
directive:
+
+
+import mysnippet
+
+
Examples
+
+A very simple Caddyfile with only one entry:
+label1
+
+directive1 argument1
+directive2
+
+
+
+Appending the prior example with another entry introduces the need for curly braces:
+label1 {
+directive1 arg1
+directive2
+}
+label2, label3 {
+directive3 arg2
+directive4 arg3 arg4
+}
+
+
+
+
+Some people prefer to always use braces even if there's just one entry; this is fine, but unnecessary:
+label1 {
+directive1 arg1
+directive2
+}
+
+
+
+Example in which a directive opens a block:
+label1
+
+directive arg1 {
+subdir arg2 arg3
+}
+directive arg4
+
+
+
+Similarly, but in an indented definition body, and with a comment:
+label1 {
+directive1 arg1
+directive2 arg2 {
+subdir1 arg3 arg4
+subdir2
+# nested blocks not supported
+}
+directive3
+}
+
diff --git a/src/docs/markdown/command-line.md b/src/docs/markdown/command-line.md
new file mode 100644
index 00000000..743f95a1
--- /dev/null
+++ b/src/docs/markdown/command-line.md
@@ -0,0 +1,278 @@
+---
+title: "Command Line"
+---
+
+# Command Line
+
+Caddy has a standard unix-like command line interface. Basic usage is:
+
+```
+caddy []
+```
+
+The `` indicate parameters that get replaced by your input.
+
+The`[brackets]` indicate optional parameters.
+
+The ellipses `...` indicates a continuation, i.e. one or more parameters.
+
+**Quick start: `caddy help`**
+
+---
+
+- **[caddy adapt](#caddy-adapt)**
+ Adapts a config document to native JSON
+
+- **[caddy environ](#caddy-environ)**
+ Prints the environment
+
+- **[caddy file-server](#caddy-file-server)**
+ A simple but production-ready file server
+
+- **[caddy hash-password](#caddy-hash-password)**
+ Hashes a password and outputs base64
+
+- **[caddy help](#caddy-help)**
+ View help for caddy commands
+
+- **[caddy list-modules](#caddy-list-modules)**
+ Lists the installed Caddy modules
+
+- **[caddy reload](#caddy-reload)**
+ Changes the config of the running Caddy process
+
+- **[caddy reverse-proxy](#caddy-reverse-proxy)**
+ A simple but production-ready reverse proxy
+
+- **[caddy run](#caddy-run)**
+ Starts the Caddy process in the foreground
+
+- **[caddy start](#caddy-start)**
+ Starts the Caddy process in the background
+
+- **[caddy stop](#caddy-stop)**
+ Stops the running Caddy process
+
+- **[caddy validate](#caddy-validate)**
+ Tests whether a config file is valid
+
+- **[caddy version](#caddy-version)**
+ Prints the version
+
+
+
+## Subcommands
+
+
+### `caddy adapt`
+
+caddy adapt
+ --config <path>
+ [--adapter <name>]
+ [--pretty]
+ [--validate]
+
+Adapts a configuration to Caddy's native JSON config structure and writes the output to stdout, along with any warnings to stderr.
+
+`--config` is the path to the config file.
+
+`--adapter` specifies the config adapter to use; default is `caddyfile`.
+
+`--pretty` will format the output with indentation for human readability.
+
+`--validate` will load and provision the adapted configuration to check for validity (but it will not actually start running the config).
+
+Note that a config which is successfully adapted may still fail validation. For an example of this, use this Caddyfile:
+
+```
+localhost
+
+tls cert_notexist.pem key_notexist.pem
+```
+
+Try adapting it:
+
+caddy adapt --config Caddyfile
+
+It succeeds without error. Then try:
+
+caddy adapt --config Caddyfile --validate
+adapt: validation: loading app modules: module name 'tls': provision tls: loading certificates: open cert_notexist.pem: no such file or directory
+
+
+Even though that Caddyfile can be adapted to JSON without errors, the actual certificate and/or key files do not exist, so validation fails because that error arises during the provisioning phase. Thus, validation is a stronger error check than adaptation is.
+
+#### Example
+
+To adapt a Caddyfile to JSON that you can easily read and tweak manually:
+
+caddy adapt --config /path/to/Caddyfile --pretty
+
+
+
+
+### `caddy environ`
+
+caddy environ
+
+Prints the environment as seen by caddy, then exits. Can be useful when debugging init systems or process manager units like systemd.
+
+
+
+
+### `caddy file-server`
+
+caddy file-server
+ [--root <path>]
+ [--listen <addr>]
+ [--domain <example.com>]
+ [--browse]
+
+Spins up a simple but production-ready static file server.
+
+`--root` specifies the root file path. Default is the current working directory.
+
+`--listen` accepts a listener address. Default is `:2015`, unless `--domain` specifies a name that qualifies for HTTPS, then `:443` will be the default.
+
+`--domain` will only serve files through that domain name, and Caddy will attempt to serve it over HTTPS if it qualifies for a certificate (i.e. is a public domain name), so make sure its DNS is configured properly first. If the name qualifies for a certificate, the default port will be changed to 443.
+
+`--browse` will enable directory listings if a directory without an index file is requested.
+
+This command disables the admin API, making it easier to run multiple instances on a local development machine.
+
+
+
+### `caddy hash-password`
+
+caddy hash-password
+ --plaintext <password>
+ [--algorithm <name>]
+ [--salt <string>]
+
+Hashes a password and writes the output to stdout in base64 encoding.
+
+`--plaintext` is the plaintext form of the password.
+
+`--algorithm` may be bcrypt or scrypt. Default is bcrypt.
+
+`--salt` is used only if the algorithm requires an external salt (like scrypt).
+
+
+
+
+### `caddy help`
+
+caddy help [<command>]
+
+Prints CLI help text, optionally for a specific subcommand.
+
+
+
+### `caddy list-modules`
+
+caddy list-modules
+ [--versions]
+
+Prints the Caddy modules that are installed, optionally with version information from their associated Go modules.
+
+NOTE: Due to [a bug in Go](https://github.com/golang/go/issues/29228), version information is only available if Caddy is built as a dependency and not as the main module. TODO: Link to docs that explain how to build Caddy with version info
+
+
+
+### `caddy reload`
+
+caddy reload
+ [--config <path>]
+ [--adapter <name>]
+ [--address <interface>]
+
+Gives the running Caddy instance a new configuration. This has the same effect as POSTing a document to the [/load endpoint](/docs/api#post-load), but this command is convenient for simple workflows revolving around config files.
+
+`--config` is the config file to apply. If not specified, it will try a file called `Caddyfile` in the current working directory and, if it exists, it will adapt it using the `caddyfile` config adapter; otherwise, it is an error if there is no config file to load.
+
+`--adapter` specifies a config adapter to use, if any.
+
+`--address` needs to be used if the admin endpoint is not listening on the default address and if it is different from the address in the provided config file.
+
+
+
+### `caddy reverse-proxy`
+
+caddy reverse-proxy
+ --from <addr>
+ --to <addr>
+
+Spins up a simple but production-ready reverse proxy.
+
+`--from` is the address to proxy from.
+
+`--to` is the address to proxy to.
+
+Both from and to parameters can be URLs, as scheme, domain name, and URI rewrite information will be inferred from the provided URL. Or they can be a simple network address and not a complete URL.
+
+This command disables the admin API, making it easier to run multiple instances on a local development machine.
+
+
+### `caddy run`
+
+caddy run
+ [--config <path>]
+ [--adapter <name>]
+ [--environ]
+ [--resume]
+
+Runs Caddy and blocks indefinitely; i.e. "daemon" mode.
+
+`--config` specifies an initial config file to immediately load and use. If no config is specified, Caddy will run with a blank configuration and use default settings for the [admin API endpoints](/docs/api), which can be used to feed it new configuration. As a special case, if the current working directory has a file called "Caddyfile" and the `caddyfile` config adapter is plugged in (default), then that file will be loaded and used to configure Caddy, even without any command line flags.
+
+`--adapter` is the name of the config adapter to use when loading the initial config, if any. This flag is not necessary if the `--config` filename starts with "Caddyfile" which assumes the `caddyfile` adapter. Otherwise, this flag is required if the provided config file is not in Caddy's native JSON format. Any warnings will be printed to the log, but beware that any adaptation without errors will immediately be used, even if there are warnings. If you want to review the results of the adaptation first, use the [`caddy adapt`](#caddy-adapt) subcommand.
+
+`--environ` prints out the environment before starting. This is the same as the `caddy environ` command, but does not exit after printing.
+
+`--resume` uses the last loaded configuration. This flag is useful primarily in [API](/docs/api)-heavy deployments, and overrides `--config` if a saved config exists.
+
+
+### `caddy start`
+
+caddy start
+ [--config <path>]
+ [--adapter <name>]
+
+Same as `caddy run`, but in the background. This command only blocks until the background process is running successfully (or fails to run), then returns.
+
+Use of this command is discouraged with system services or on Windows. On Windows, the child process will remain attached to the terminal, so closing the window will forcefully stop Caddy, which is not obvious.
+
+Once started, you can use [`caddy stop`](#caddy-stop) or [the /stop API endpoint](/docs/api#post-stop) to exit the background process.
+
+
+
+### `caddy stop`
+
+caddy stop [--address <interface>]
+
+Gracefully stops the running Caddy process (other than the process of the stop command) and causes it to exit. It uses the [/stop endpoint](/docs/api#post-stop) of the admin API to perform a graceful shutdown.
+
+`--address` can be used if the running instance's admin API is not on the default port; an alternate address can be specified here.
+
+If you want to stop the current configuration but do not want to exit the process, use [`caddy reload`](#caddy-reload) with a blank config, or the [`DELETE /config/`](/docs/api#delete-configpath) endpoint.
+
+
+
+### `caddy validate`
+
+caddy validate
+ [--config <path>]
+ [--adapter <name>]
+
+Validates a configuration file. This command deserializes the config, then loads and provisions all of its modules as if to start the config, but the config is not actually started. This exposes errors in a configuration that arise during loading or provisioning phases and is a stronger error check than merely serializing a config as JSON.
+
+`--config` is the config file to validate.
+
+`--adapter` is the name of the config adapter to use, if the config file is not in Caddy's native JSON format.
+
+
+
+### `caddy version`
+caddy version
+
+Prints the version.
diff --git a/src/docs/markdown/config-adapters.md b/src/docs/markdown/config-adapters.md
new file mode 100644
index 00000000..57eb8f9f
--- /dev/null
+++ b/src/docs/markdown/config-adapters.md
@@ -0,0 +1,48 @@
+---
+title: Config Adapters
+---
+
+# Config Adapters
+
+Caddy's native config language is [JSON](https://www.json.org/json-en.html), but writing JSON by hand can be tedious and error-prone. That's why Caddy supports being configured with other languages through **config adapters**. They are Caddy plugins which make it possible to use config in your preferred format by outputting [Caddy JSON](/docs/json/) for you.
+
+For example, a config adapter could [turn your NGINX config into Caddy JSON](https://github.com/caddyserver/nginx-adapter).
+
+## Known config adapters
+
+The following config adapters are currently available (some are third-party projects):
+
+- [**caddyfile**](/docs/caddyfile) (built-in)
+- **jsonc** (built-in for now)
+- **json5** (built-in for now)
+- [**nginx**](https://github.com/caddyserver/nginx-adapter)
+- [**yaml**](https://github.com/iamd3vil/caddy_yaml_adapter)
+
+(This list is the temporary home for known adapters until our new website is finished.)
+
+## Using config adapters
+
+You can use a config adapter by specifying it on the command line by using the `--adapter` flag on most subcommands that take a config:
+
+caddy run --config caddy.yaml --adapter yaml
+
+Or via the API at the [`/load` endpoint](/docs/api#post-load):
+
+curl localhost:2019/load \
+ -X POST \
+ -H "Content-Type: application/yaml" \
+ --data-binary @caddy.yaml
+
+If you only want to get the output JSON without running it, you can use the [`caddy adapt`](/docs/command-line#caddy-adapt) command:
+
+caddy adapt --config caddy.yaml --adapter yaml
+
+## Caveats
+
+Not all config languages are 100% compatible with Caddy; some features or behaviors simply don't translate well or are not yet programmed into the adapter or Caddy itself.
+
+Some adapters do a 1-1 translation, like YAML->JSON or TOML->JSON. Others are designed specifically for Caddy, like the Caddyfile. Generally, these adapters will always work.
+
+However, not all adapters work all of the time. Config adapters do their best to translate your input to Caddy JSON with the highest fidelity and correctness. Because this conversion process is not guaranteed to be complete and correct all the time, we don't call them "converters" or "translators". They are "adapters" since they will at least give you a good starting point to finish crafting your final JSON config.
+
+Config adapters can output the resulting JSON, warnings, and errors. JSON results if no errors occur. Errors occur when something is wrong with the input (for example, syntax errors). Warnings are emitted when something is wrong with the adaptation but which is not necessarily fatal (for example, feature not supported). Caution is advised if using configs that were adapted with warnings.
diff --git a/src/docs/markdown/conventions.md b/src/docs/markdown/conventions.md
new file mode 100644
index 00000000..1ac2b547
--- /dev/null
+++ b/src/docs/markdown/conventions.md
@@ -0,0 +1,163 @@
+---
+title: Conventions
+---
+
+# Conventions
+
+The Caddy ecosystem adheres to a few conventions to make things consistent and intuitive across the platform.
+
+
+## Network addresses
+
+
+
+When specifying a network address to dial or bind, Caddy accepts a string in the following format:
+
+```
+network/address
+```
+
+The network part is optional, and is anything that [Go's `net` package](https://golang.org/pkg/net/) recognizes. The default network is `tcp`. If a network is specified, a single forward slash `/` must separate the network and address portions.
+
+The address part may be any of these forms:
+
+- `host`
+- `host:port`
+- `:port`
+- `/path/to/unix/socket`
+
+The host may be any hostname, resolvable domain name, or IP address.
+
+The port may be a single value (`:8080`) or an inclusive range (`:8080-8085`). A port range will be multiplied into singular addresses. Not all config fields accept port ranges. The special port `:0` means any available port.
+
+A unix socket path is only acceptable when using a unix* network type. The forward slash that separates the network and address is not considered part of the path.
+
+Valid examples:
+
+```
+:8080
+127.0.0.1:8080
+localhost:8080
+localhost:8080-8085
+tcp/localhost:8080
+tcp/localhost:8080-8085
+udp/localhost:9005
+unix//path/to/socket
+```
+
+
+## Placeholders
+
+
+
+Caddy's configuration supports the use of _placeholders_ (variables). Using placeholders is a simple way to inject dynamic values into a static configuration.
+
+Placeholders are bounded on either side by curly braces `{ }` and contain the variable name inside, for example: `{foo.bar}`. Variable names are typically namespaced with dots to avoid collisions across modules.
+
+Which placeholders are available depends on the context. Not all placeholders are available in all parts of the config. For example, [the HTTP app sets placeholders](/docs/json/apps/http/) that are only available in areas of the config related to handling HTTP requests.
+
+The following placeholders are always available:
+
+Placeholder | Description
+------------|-------------
+`{env.*}` | Environment variable (example: `{env.HOME}`)
+`{system.hostname}` | The system's local hostname
+`{system.slash}` | The system's filepath separator
+`{system.os}` | The system's OS
+`{system.arch}` | The system's architecture
+`{time.now.common_log}` | The current timestamp in Common Log Format
+
+Not all config fields support placeholders, but most do where you would expect it.
+
+
+## File locations
+
+This section contains information about where to find various files. File and directory paths described here are defaults at best; some can be overridden.
+
+### Your config files
+
+
+
+There is no single, conventional place for you to put your config files. Put them wherever makes the most sense to you.
+
+Distributions that ship with a default config file should document where this config file is at, even if it might be obvious to the package/distro maintainers.
+
+
+### Data directory
+
+Caddy stores TLS certificates and other important assets in a data directory.
+
+If the `XDG_DATA_HOME` environment variable is set, it is `$XDG_DATA_HOME/caddy`.
+
+Otherwise, its path varies by platform, adhering to OS conventions:
+
+
+
+OS | Data directory path
+---|---------------------
+**Linux, BSD** | `$HOME/.local/share/caddy`
+**Windows** | `%AppData%\Caddy`
+**macOS** | `$HOME/Library/Application Support/Caddy`
+**Plan 9** | `$HOME/lib/caddy`
+**Android** | `$HOME/caddy` (or `/sdcard/caddy`)
+
+The data directory must not be treated like a cache. Its contents are not ephemeral or merely for the sake of performance. Caddy stores TLS certificates, private keys, OCSP staples, and other necessary information to the data directory. It should not be purged without an understanding of the implications.
+
+It is crucial that this directory is persistent and writeable by Caddy.
+
+
+### Configuration directory
+
+
+
+
+
+This is where Caddy may store certain configuration to disk. Most notably, it persists the last active configuration (by default) to this folder for easy resumption later using [`caddy run --resume`](/docs/command-line#caddy-run).
+
+If the `XDG_CONFIG_HOME` environment variable is set, it is `$XDG_CONFIG_HOME/caddy`.
+
+Otherwise, its path varies by platform, adhering to OS conventions:
+
+
+
+OS | Config directory path
+---|---------------------
+**Linux, BSD** | `$HOME/.config/caddy`
+**Windows** | `%AppData%\Caddy`
+**macOS** | `$HOME/Library/Application Support/Caddy`
+**Plan 9** | `$HOME/lib/caddy`
+
+It is crucial that this directory is persistent and writeable by Caddy.
+
+
+## Durations
+
+Duration strings are commonly used throughout Caddy's configuration. They take on the same format as [Go's time.ParseDuration syntax](https://golang.org/pkg/time/#ParseDuration). Valid units are:
+
+- `ns` (nanosecond)
+- `us`/`µs` (microsecond)
+- `ms` (millisecond)
+- `s` (second)
+- `m` (minute)
+- `h` (hour)
+
+Examples:
+
+- `250ms`
+- `5s`
+- `1.5h`
+- `2h45m`
+
+In the [JSON config](/docs/json/), duration values can also be integers which represents nanoseconds.
\ No newline at end of file
diff --git a/src/docs/markdown/extending-caddy.md b/src/docs/markdown/extending-caddy.md
new file mode 100644
index 00000000..d3880a5f
--- /dev/null
+++ b/src/docs/markdown/extending-caddy.md
@@ -0,0 +1,375 @@
+---
+title: "Extending Caddy"
+---
+
+# Extending Caddy
+
+Caddy is easy to extend because of its modular architecture. Most Caddy extensions (or plugins) are called _modules_. To be clear, Caddy modules are distinct from [Go modules](https://github.com/golang/go/wiki/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](https://pkg.go.dev/github.com/caddyserver/caddy/v2@v2.0.0-beta11?tab=doc#Module) interface, which provides its name and a constructor function.
+
+Here's a template you can copy & paste:
+
+```go
+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:
+
+```go
+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](https://pkg.go.dev/github.com/caddyserver/caddy/v2@v2.0.0-beta11?tab=doc#App) interface.
+
+These modules appear in the [`"apps"`](/docs/json/#apps) property of the top-level of Caddy's config:
+
+```json
+{
+ "apps": {}
+}
+```
+
+Example [apps](/docs/json/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:
+
+```go
+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](#provisioning) and [validation](#validating) 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()`](https://pkg.go.dev/github.com/caddyserver/caddy/v2@v2.0.0-beta11?tab=doc#ModuleInfo.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](https://pkg.go.dev/github.com/caddyserver/caddy/v2@v2.0.0-beta11?tab=doc#Provisioner), the `Provision()` method is called.
+4. If the module is a [caddy.Validator](https://pkg.go.dev/github.com/caddyserver/caddy/v2@v2.0.0-beta11?tab=doc#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](https://pkg.go.dev/github.com/caddyserver/caddy/v2@v2.0.0-beta11?tab=doc#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](https://pkg.go.dev/github.com/caddyserver/caddy/v2@v2.0.0-beta11?tab=doc#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](https://pkg.go.dev/github.com/caddyserver/caddy/v2@v2.0.0-beta11?tab=doc#Provisioner) interface:
+
+```go
+// 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](https://github.com/uber-go/zap).
+
+To emit logs, get a logger in your module's Provision method:
+
+```go
+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](https://pkg.go.dev/go.uber.org/zap?tab=doc#Logger) for details.
+
+
+### Validating
+
+Modules which would like to validate their configuration may do so by satisfying the [`caddy.Validator`](https://pkg.go.dev/github.com/caddyserver/caddy/v2@v2.0.0-beta11?tab=doc#Validator) interface:
+
+```go
+// 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:
+
+```go
+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:
+
+```go
+// 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:
+
+```go
+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:
+
+```go
+`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`](https://pkg.go.dev/github.com/caddyserver/caddy/v2@v2.0.0-beta11?tab=doc#ModuleMap) or `[]caddy.ModuleMap`, where the map key is the module name.
+
+
+### Loading guest modules
+
+To load a guest module, call [`ctx.LoadModule()`](https://pkg.go.dev/github.com/caddyserver/caddy/v2@v2.0.0-beta11?tab=doc#Context.LoadModule) during the provision phase:
+
+```go
+// 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:
+
+```go
+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)
+)
+```
\ No newline at end of file
diff --git a/src/docs/markdown/getting-started.md b/src/docs/markdown/getting-started.md
new file mode 100644
index 00000000..623c0b62
--- /dev/null
+++ b/src/docs/markdown/getting-started.md
@@ -0,0 +1,259 @@
+---
+title: "Getting Started"
+---
+
+# Getting Started
+
+Welcome to Caddy! This tutorial will explore the basics of using Caddy and help you get familiar with it at a high level.
+
+**Objectives:**
+- 🔲 Run the daemon
+- 🔲 Try the API
+- 🔲 Give Caddy a config
+- 🔲 Test config
+- 🔲 Make a Caddyfile
+- 🔲 Use the config adapter
+- 🔲 Start with an initial config
+- 🔲 Compare JSON and Caddyfile
+- 🔲 Compare API and config files
+- 🔲 Run in the background
+- 🔲 Zero-downtime config reload
+
+**Prerequisites:**
+- Basic terminal / command line skills
+- Basic text editor skills
+- `caddy` and `curl` in your PATH
+
+---
+
+Let's start by running it:
+
+caddy
+
+Oops; without a subcommand, the `caddy` command only displays help text. You can use this any time you forget what to do.
+
+To start Caddy as a daemon, use the `run` subcommand:
+
+
+caddy run
+
+This blocks forever, but what is it doing? At the moment... nothing. By default, Caddy's configuration ("config") is blank. We can verify this using the [admin API](/docs/api) in another terminal:
+
+
+curl localhost:2019/config/
+
+We can make Caddy useful by giving it a config. This can be done many ways, but we'll start by making a POST request to the [/load](/docs/api#post-load) endpoint using `curl`.
+
+
+
+## Your first config
+
+To prepare our request, we need to make a config. At its core, Caddy's configuration is simply a [JSON document](/docs/json/).
+
+
+
+Save this to a JSON file:
+
+```json
+{
+ "apps": {
+ "http": {
+ "servers": {
+ "example": {
+ "listen": [":2015"],
+ "routes": [
+ {
+ "handle": [{
+ "handler": "static_response",
+ "body": "Hello, world!"
+ }]
+ }
+ ]
+ }
+ }
+ }
+ }
+}
+```
+
+Then upload it:
+
+curl localhost:2019/load \
+ -X POST \
+ -H "Content-Type: application/json" \
+ -d @caddy.json
+
+
+
+
+We can verify that Caddy applied our new config with another GET request:
+
+curl localhost:2019/config/
+
+Test that it works by going to [localhost:2015](http://localhost:2015) in your browser or use `curl`:
+
+curl localhost:2015
+Hello, world!
+
+
+
+If you see _Hello, world!_, then congrats -- it's working! It's always a good idea to make sure your config works as you expect, especially before deploying into production.
+
+
+
+## Your first Caddyfile
+
+That was _kind of a lot of work_ just for Hello World.
+
+Another way to configure Caddy is with the [**Caddyfile**](/docs/caddyfile). The same config we wrote in JSON above can be expressed simply as:
+
+```
+:2015
+
+respond "Hello, world!"
+```
+
+
+
+Save that to a file named `Caddyfile` (no extension) in the current directory.
+
+Stop Caddy if it is already running (Ctrl+C), then run:
+
+caddy adapt
+
+Or if you stored the Caddyfile somewhere else or named it something other than `Caddyfile`:
+
+caddy adapt --config /path/to/Caddyfile
+
+
+
+You will see JSON output! What happened here?
+
+We just used a [_config adapter_](/docs/config-adapters) to convert our Caddyfile to Caddy's native JSON structure.
+
+While we could take that output and make another API request, we can skip all those steps because the `caddy` command can do it for us.
+
+Since the Caddyfile is so common, if there is a file called Caddyfile in the current directory and no other config is specified, Caddy will load the Caddyfile, adapt it for us, and run it right away.
+
+So now that there is a Caddyfile in the current folder, let's do `caddy run` again:
+
+caddy run
+
+Or if your Caddyfile is somewhere else (or named something else):
+
+caddy run \
+ --config /path/to/Caddyfile \
+ --adapter caddyfile
+
+You can now try loading your site again and you will see that it is working!
+
+
+
+As you can see, we can start Caddy with an initial config several ways:
+
+- A file named Caddyfile in the current directory
+- The `--config` flag (optionally with the `--adapter` flag)
+- The `--resume` flag (if a config was loaded previously)
+
+
+## JSON vs. Caddyfile
+
+Now you know that the Caddyfile is just converted to JSON under the hood.
+
+The Caddyfile seems simpler than the JSON, but should you always use it? There are pros and cons to each approach. The answer depends on your requirements and use case.
+
+JSON | Caddyfile
+-----|----------
+Ubiquitous | Niche
+Easy to generate | Easy to craft by hand
+Easily programmable | Difficult to automate
+Full range of Caddy functionality | Most common parts of Caddy functionality
+Extremely expressive | Moderately expressive
+Allows config traversal | Cannot traverse within Caddyfile
+Partial config changes | Whole config changes only
+Can be exported | Cannot be exported
+Compatible with all API endpoints | Compatible with some API endpoints
+Documentation generated automatically | Documentation is hand-written
+More efficient | More computational
+Kind of boring | Kind of fun
+**Learn more: [JSON structure](/docs/json/)** | **Learn more: [Caddyfile docs](/docs/caddyfile)**
+
+You will need to decide which is best for your use case.
+
+
+
+It is important to note that both JSON and the Caddyfile (and [any other supported config adapter](/docs/config-adapters)) can be used with Caddy's API. However, you get the full range of Caddy's functionality and API features if you use JSON. If using a config adapter, the only way to load or change the config with the API is the [/load endpoint](/docs/api#post-load).
+
+
+## API vs. Config files
+
+
+
+You will also want to decide whether your workflow is API-based or CLI-based. (You _can_ use both the API and config files on the same server, but we don't recommend it: best to have one source of truth.)
+
+API | Config files
+----|-------------
+Make config changes with HTTP requests | Make config changes with shell commands
+Easy to scale | Difficult to scale
+Difficult to manage by hand | Easy to manage by hand
+Really fun | Also fun
+**Learn more: [API tutorial](/docs/api-tutorial)** | **Learn more: [Caddyfile tutorial](/docs/caddyfile-tutorial)**
+
+
+
+The choice of API or config file workflow is orthogonal to the use of config adapters: you can use JSON but store it in a file and use the command line interface; conversely, you can also use the Caddyfile (e.g. from a template) with the API.
+
+But most people will use JSON+API or Caddyfile+CLI combinations.
+
+As you can see, Caddy is well-suited for a wide variety of use cases and deployments!
+
+
+
+
+
+## Start, stop, run
+
+Since Caddy is a server, it runs indefinitely. That means your terminal won't unblock (can't be used) after you execute `caddy run` until the process is terminated (usually Ctrl+C).
+
+Although `caddy run` is the most common and is usually recommended (especially when making a system service!), you can alternatively use `caddy start` to start Caddy and have it run in the background:
+
+caddy start
+
+This will let you use your terminal again, which is convenient in some interactive headless environments.
+
+You will then have to stop the process yourself, since Ctrl+C won't stop it for you:
+
+caddy stop
+
+Or use [the /stop endpoint](/docs/api#post-stop) of the API.
+
+
+
+
+## Reloading config
+
+Whether using the API or command line, your server can perform zero-downtime config reloads or changes.
+
+All [API endpoints](/docs/api) that load or change config are already graceful with zero downtime.
+
+When using config files with the CLI, however, it may be tempting to use Ctrl+C to stop your server and restart it again to pick up the new configuration. This is sometimes fine in local dev environments, but is a bad idea on a production server.
+
+Instead, use the [`caddy reload`](/docs/command-line#caddy-reload) command:
+
+
+
+caddy reload
+
+This actually just uses the API under the hood, but it will load and (if necessary) adapt your config file to JSON, then gracefully replace the active configuration without downtime.
+
+If there are any errors loading the new config, Caddy rolls back to the last working config.
+
+
\ No newline at end of file
diff --git a/src/docs/markdown/index.md b/src/docs/markdown/index.md
new file mode 100644
index 00000000..4177623e
--- /dev/null
+++ b/src/docs/markdown/index.md
@@ -0,0 +1,25 @@
+---
+title: Welcome
+---
+
+# Caddy Welcomes You
+
+Caddy is a powerful, extensible platform to serve your sites, services, and apps, written in Go. Although most people use it as a web server or proxy, it is an excellent choice for a:
+
+- web server
+- reverse proxy
+- sidecar proxy
+- load balancer
+- API gateway
+- ingress controller
+- system manager
+- process supervisor
+- task scheduler
+- (any long-running process)
+
+and operates primarily at L4 (transport layer) and L7 (application layer) of the [OSI model](https://en.wikipedia.org/wiki/OSI_model), though it has the ability to work with other layers.
+
+Configuration is both dynamic and exportable with [Caddy's API](/docs/api); no config files required. The format of the config document takes many forms with [config adapters](/docs/config-adapters), but Caddy's native config language is [JSON](/docs/json/).
+
+Caddy compiles for all major platforms and has no dependencies.
+
diff --git a/src/docs/markdown/install.md b/src/docs/markdown/install.md
new file mode 100644
index 00000000..438e1bfa
--- /dev/null
+++ b/src/docs/markdown/install.md
@@ -0,0 +1,86 @@
+---
+title: "Install"
+---
+
+# Install
+
+Caddy is available for every platform as a [static binary](https://github.com/caddyserver/caddy/releases) (it has no dependencies). You can also [build from source](#build-from-source) to customize your build.
+
+
+## Official packages
+
+We maintain [official distributions](https://github.com/caddyserver/dist) for the following platforms:
+
+- **DigitalOcean**
+[Create a Caddy droplet](https://marketplace.digitalocean.com/apps/caddy) and get started in 90 seconds.
+
+- **Fedora, RedHat, CentOS**
+Read how to [install the Caddy COPR](https://copr.fedorainfracloud.org/coprs/g/caddy/caddy/).
+
+- **Docker**
+[docker pull caddy/caddy](https://hub.docker.com/r/caddy/caddy)
+
+
+## Manually installing as a Linux service
+
+Requirements:
+
+- A `caddy` binary that you downloaded or built from source
+- Systemd version 232 or newer
+- Superuser rights
+
+Move the `caddy` binary into your `$PATH`, for example:
+sudo mv caddy /usr/bin/
+
+Test that it worked:
+caddy version
+
+Create a group named `caddy`:
+groupadd --system caddy
+
+Create a user named `caddy`, with a writeable home folder:
+useradd --system \
+ --gid caddy \
+ --create-home \
+ --home-dir /var/lib/caddy \
+ --shell /usr/sbin/nologin \
+ --comment "Caddy web server" \
+ caddy
+
+Next, take [this systemd unit file](https://github.com/caddyserver/dist/blob/master/init/caddy.service) and save it to `/etc/systemd/system/caddy.service`. Double-check the **ExecStart** and **ExecReload** directives---make sure the binary's location and command line arguments are correct for your installation.
+
+Double-check that both your systemd and Caddy configs are correct before continuing.
+
+To start the service for the first time, do the usual systemctl dance:
+
+sudo systemctl daemon-reload
+sudo systemctl enable caddy
+sudo systemctl start caddy
+
+Verify that it is running:
+systemctl status caddy
+
+When running with our official service file, Caddy's output will be redirected to `journalctl`:
+journalctl -u caddy
+
+To gracefully apply any changes to your config file (if using one):
+sudo systemctl reload caddy
+
+You can stop the service with:
+sudo systemctl stop caddy
+
+## Build from source
+
+Requirements:
+
+- [Go](https://golang.org/dl) 1.13 or newer
+- [Go modules](https://github.com/golang/go/wiki/Modules) enabled
+
+Download the `v2` branch source code:
+
+git clone -b v2 "https://github.com/caddyserver/caddy.git"
+
+Build:
+
+cd caddy/cmd/caddy/
+go build
diff --git a/src/docs/markdown/quick-starts.md b/src/docs/markdown/quick-starts.md
new file mode 100644
index 00000000..f386f362
--- /dev/null
+++ b/src/docs/markdown/quick-starts.md
@@ -0,0 +1,18 @@
+---
+title: Quick-starts
+---
+
+# Quick-starts
+
+These tutorials are designed to get you up and running quickly without much explanation to get in the way.
+
+It is **highly recommended** that you follow-up by reading other tutorials and reference documentation to fully understand how your web server works.
+
+
+## Menu
+
+- #### [Using the API](/docs/quick-starts/api)
+- #### [Using a Caddyfile](/docs/quick-starts/caddyfile)
+- #### [File server](/docs/quick-starts/file-server)
+- #### [Reverse proxy](/docs/quick-starts/reverse-proxy)
+- #### [HTTPS](/docs/quick-starts/https)
diff --git a/src/docs/markdown/quick-starts/api.md b/src/docs/markdown/quick-starts/api.md
new file mode 100644
index 00000000..6c35e905
--- /dev/null
+++ b/src/docs/markdown/quick-starts/api.md
@@ -0,0 +1,108 @@
+---
+title: API Quick-start
+---
+
+# API quick-start
+
+**Prerequisites:**
+- Basic terminal / command line skills
+- `caddy` and `curl` in your PATH
+
+---
+
+First start Caddy:
+
+caddy start
+
+Caddy is currently running idle (with a blank configuration). Give it a simple config with `curl`:
+
+curl localhost:2019/load \
+ -X POST \
+ -H "Content-Type: application/json" \
+ -d @- << EOF
+ {
+ "apps": {
+ "http": {
+ "servers": {
+ "hello": {
+ "listen": [":2015"],
+ "routes": [
+ {
+ "handle": [{
+ "handler": "static_response",
+ "body": "Hello, world!"
+ }]
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+EOF
+
+Giving a POST body with [Heredoc](https://en.wikipedia.org/wiki/Here_document#Unix_shells) can be tedious, so if you prefer to use files, save the JSON to a file called `caddy.json` and then use this command instead:
+
+curl localhost:2019/load \
+ -X POST \
+ -H "Content-Type: application/json" \
+ -d @caddy.json
+
+
+Now load [localhost:2015](http://localhost:2015) in your browser or use `curl`:
+
+curl localhost:2015
+Hello, world!
+
+We can also define multiple sites on different interfaces with this JSON:
+
+```json
+{
+ "apps": {
+ "http": {
+ "servers": {
+ "hello": {
+ "listen": [":2015"],
+ "routes": [
+ {
+ "handle": [{
+ "handler": "static_response",
+ "body": "Hello, world!"
+ }]
+ }
+ ]
+ },
+ "bye": {
+ "listen": [":2016"],
+ "routes": [
+ {
+ "handle": [{
+ "handler": "static_response",
+ "body": "Goodbye, world!"
+ }]
+ }
+ ]
+ }
+ }
+ }
+ }
+}
+```
+
+Update your JSON then perform the API request again.
+
+Try out your new "goodbye" endpoint [in your browser](http://localhost:2016) or with `curl` to make sure it works:
+
+curl localhost:2016
+Goodbye, world!
+
+When you are done with Caddy, make sure to stop it:
+
+caddy stop
+
+There's a lot more you can do with the API, including exporting configuration and making fine-grained changes to the config (as opposed to updating the whole thing). Be sure to read the [full API tutorial](/docs/api-tutorial) to learn how!
+
+## Further reading
+
+- [Full API tutorial](/docs/api-tutorial)
+- [API documentation](/docs/api)
diff --git a/src/docs/markdown/quick-starts/caddyfile.md b/src/docs/markdown/quick-starts/caddyfile.md
new file mode 100644
index 00000000..28faa1b9
--- /dev/null
+++ b/src/docs/markdown/quick-starts/caddyfile.md
@@ -0,0 +1,68 @@
+---
+title: Caddyfile Quick-start
+---
+
+# Caddyfile quick-start
+
+Create a new text file named `Caddyfile` (no extension).
+
+The first thing to type in a Caddyfile is your site's address:
+
+```
+localhost
+```
+
+Then hit enter and type what you want it to do, so it looks like this:
+
+```
+localhost
+
+respond "Hello, world!"
+```
+
+Save this and run Caddy from the same folder that contains your Caddyfile:
+
+caddy start
+
+Either open your browser to [localhost:2015](http://localhost:2015) or `curl` it:
+
+curl localhost:2015
+Hello, world!
+
+You can define multiple sites in a Caddyfile by wrapping them in curly braces `{ }`. Change your Caddyfile to be:
+
+```
+localhost {
+ respond "Hello, world!"
+}
+
+localhost:2016 {
+ respond "Goodbye, world!"
+}
+```
+
+You can give Caddy the updated configuration two ways, either with the API directly:
+
+curl localhost:2019/load \
+ -X POST \
+ -H "Content-Type: text/caddyfile" \
+ --data-binary @Caddyfile
+
+
+or with the reload command, which does the same API request for you:
+
+caddy reload
+
+Try out your new "goodbye" endpoint [in your browser](http://localhost:2016) or with `curl` to make sure it works:
+
+curl localhost:2016
+Goodbye, world!
+
+When you are done with Caddy, make sure to stop it:
+
+caddy stop
+
+## Further reading
+
+- [Caddyfile concepts](/docs/caddyfile/concepts)
+- [Directives](/docs/caddyfile/directives)
\ No newline at end of file
diff --git a/src/docs/markdown/quick-starts/file-server.md b/src/docs/markdown/quick-starts/file-server.md
new file mode 100644
index 00000000..ae330e50
--- /dev/null
+++ b/src/docs/markdown/quick-starts/file-server.md
@@ -0,0 +1,82 @@
+---
+title: File server quick-start
+---
+
+# File server quick-start
+
+This guide will show you how to get a production-ready static file server up and running quickly.
+
+**Prerequisites:**
+- Basic terminal / command line skills
+- `caddy` in your PATH
+- A folder containing your website
+
+---
+
+There are two easy ways to get a quick file server up and running. We'll show you two equivalent ways to do the same thing.
+
+## Command line
+
+In your terminal, change to the root directory of your site and run:
+
+caddy file-server
+
+The default address is :2015, so load [localhost:2015](http://localhost:2015) in your browser to see your site!
+
+If you don't have an index file but you want to display a file listing, use the `--browse` option:
+
+caddy file-server --browse
+
+You can also listen on port 80 easily enough:
+
+caddy file-server --listen :80
+
+Or set use another folder as the site root:
+
+caddy file-server --root ~/mysite
+
+
+
+## Caddyfile
+
+In the root of your site, create a file called `Caddyfile` with these contents:
+
+```
+localhost
+
+file_server
+```
+
+Then, from the same directory, run:
+
+caddy run
+
+You can then load [localhost:2015](http://localhost:2015) to see your site!
+
+The [`file_server` directive](/docs/caddyfile/directives/file_server) has more options for you to customize your site. Make sure to [reload](/docs/command-line#caddy-reload) Caddy (or stop and start it again) when you change the Caddyfile!
+
+If you don't have an index file but you want to display a file listing, use the `browse` argument:
+
+```
+localhost
+
+file_server browse
+```
+
+You can also listen on port 80 easily enough:
+
+```
+:80
+
+file_server
+```
+
+Or set use another folder as the site root:
+
+```
+localhost
+
+root /home/me/mysite
+file_server
+```
+
diff --git a/src/docs/markdown/quick-starts/https.md b/src/docs/markdown/quick-starts/https.md
new file mode 100644
index 00000000..82bacff2
--- /dev/null
+++ b/src/docs/markdown/quick-starts/https.md
@@ -0,0 +1,99 @@
+---
+title: HTTPS quick-start
+---
+
+# HTTPS quick-start
+
+This guide will show you how to get up and running with [fully-managed HTTPS](/docs/automatic-https) in no time.
+
+**Prerequisites:**
+- Basic terminal / command line skills
+- Basic understanding of DNS
+- A registered public domain name
+- External access to ports 80 and 443
+- `caddy` and `curl` in your PATH
+
+---
+
+In this tutorial, replace `example.com` with your actual domain name.
+
+Set your domain's A/AAAA records point to your server. You can do this by logging into your DNS provider and managing your domain name.
+
+Before continuing, verify correct records with an authoritative lookup. Replace `example.com` with your domain name, and if you are using IPv6 replace `type=A` with `type=AAAA`:
+
+curl "https://cloudflare-dns.com/dns-query?name=example.com&type=A" \
+ -H "accept: application/dns-json"
+
+
+
+Also make sure your server is externally reachable on ports 80 and 443 from a public interface.
+
+All we have to do is start Caddy with your domain name in the config. There are several ways to do this.
+
+## Caddyfile
+
+This is the most common way to get HTTPS; it works for almost any kind of site.
+
+Create a file called `Caddyfile` (no extension) where the first line is your domain name, for example:
+
+```
+example.com
+
+respond "Hello, privacy!"
+```
+
+Then from the same directory, run:
+
+caddy run
+
+You will see Caddy provision a TLS certificate and serve your site over HTTPS.
+
+
+## The `file-server` command
+
+If all you need is a file server over HTTPS, run this command (replacing your domain name):
+
+caddy file-server --domain example.com
+
+You will see Caddy provision a TLS certificate and serve your site over HTTPS.
+
+
+## The `reverse-proxy` command
+
+If all you need is a simple reverse proxy over HTTPS (as a TLS terminator), run this command (replacing your domain name and actual backend address):
+
+caddy reverse-proxy --from example.com --to localhost:9000
+
+You will see Caddy provision a TLS certificate and serve your site over HTTPS.
+
+
+## JSON config
+
+The general rule of thumb is that any [host matcher](/docs/json/apps/http/servers/routes/match/host/) with a host that looks like a domain name will trigger automatic HTTPS.
+
+Thus, a JSON config such as the following will enable production-ready [automatic HTTPS](/docs/automatic-https):
+
+```json
+{
+ "apps": {
+ "http": {
+ "servers": {
+ "hello": {
+ "listen": [":443"],
+ "routes": [
+ {
+ "match": [{
+ "host": ["example.com"]
+ }],
+ "handle": [{
+ "handler": "static_response",
+ "body": "Hello, privacy!"
+ }]
+ }
+ ]
+ }
+ }
+ }
+ }
+}
+```
diff --git a/src/docs/markdown/quick-starts/reverse-proxy.md b/src/docs/markdown/quick-starts/reverse-proxy.md
new file mode 100644
index 00000000..b27ba6a7
--- /dev/null
+++ b/src/docs/markdown/quick-starts/reverse-proxy.md
@@ -0,0 +1,65 @@
+---
+title: Reverse proxy quick-start
+---
+
+# Reverse proxy quick-start
+
+This guide will show you how to get a production-ready reverse proxy up and running quickly.
+
+**Prerequisites:**
+- Basic terminal / command line skills
+- `caddy` in your PATH
+- A running backend process to proxy to
+
+---
+
+There are two easy ways to get a quick reverse proxy up and running. We'll show you two equivalent ways to do the same thing.
+
+This tutorial assumes you have a backend HTTP service running on `127.0.0.1:9000`.
+
+
+## Command line
+
+In your terminal, run this command:
+
+caddy reverse-proxy --to 127.0.0.1:9000
+
+Caddy's default address is :2015, so make a request to [localhost:2015](http://localhost:2015) to see it working!
+
+It's easy to change the proxy's address:
+
+caddy reverse-proxy --from :2016 --to 127.0.0.1:9000
+
+Now you can access the proxy at [localhost:2016](http://localhost:2016).
+
+
+
+## Caddyfile
+
+In the current working directory, create a file called `Caddyfile` with these contents:
+
+```
+localhost
+
+reverse_proxy 127.0.0.1:9000
+```
+
+Then, from the same directory, run:
+
+caddy run
+
+You can then make a request to [localhost:2015](http://localhost:2015) to see it working!
+
+It's easy to change the proxy's address:
+
+```
+:2016
+
+reverse_proxy 127.0.0.1:9000
+```
+
+Make sure to [reload](/docs/command-line#caddy-reload) Caddy (or stop and start it again) when you change the Caddyfile.
+
+Now you can access the proxy at [localhost:2016](http://localhost:2016).
+
+There is a lot more you can do with the [`reverse_proxy` directive](/docs/caddyfile/directives/reverse_proxy).
\ No newline at end of file
diff --git a/src/includes/docs-head.html b/src/includes/docs-head.html
new file mode 100644
index 00000000..dd727503
--- /dev/null
+++ b/src/includes/docs-head.html
@@ -0,0 +1,4 @@
+{{include "/includes/head.html"}}
+
+
+
\ No newline at end of file
diff --git a/src/includes/docs-header.html b/src/includes/docs-header.html
new file mode 100644
index 00000000..c0b252c2
--- /dev/null
+++ b/src/includes/docs-header.html
@@ -0,0 +1,8 @@
+{{include "/includes/v1-banner.html"}}
++ Caddy simplifies your infrastructure. It takes care of TLS certificate renewals, OCSP stapling, static file serving, reverse proxying, Kubernetes ingress, and more. +
++ Its modular architecture means you can do more with a single, static binary that compiles for any platform. +
++ Caddy runs great in containers because it has no dependencies—not even libc. Run Caddy practically anywhere. +
+ ++ Caddy is the only web server to use HTTPS automatically and by default. +
++ Caddy obtains and renew TLS certificates for your sites automatically. It even staples OCSP responses. Its novel certificate management features are the most mature and reliable in its class. +
++ Written in Go, Caddy offers greater memory safety than servers written in C. A hardened TLS stack powered by the Go standard library serves a significant portion of all Internet traffic. +
+ + ++ Ardan Labs is the trusted partner of the Caddy Web Server open source project, providing enterprise-grade support to our clients. +
++ Together, we consult and train, as well as develop, install, and maintain Caddy and its plugins to ensure your infrastructure runs smoothly and efficiently. Contact us to get started! +
+ ++ Caddy is both a flexible, efficient static file server and a powerful, scalable reverse proxy. +
++ Use it to serve your static site with compression, template evaluation, Markdown rendering, and more. +
++ Or use it as a dynamic reverse proxy to any number of backends, complete with active and passive health checks, load balancing, circuit breaking, caching, and more. +
+ +$ caddy file-server
+
+ $ caddy file-server --domain example.com
+
+ $ caddy reverse-proxy --from example.com --to localhost:9000
+
+ $ caddy run
+
+
+ localhost
+
+templates
+file_server
+
+ example.com # Your site's domain name
+
+# Load balance between three backends with custom health checks
+reverse_proxy 10.0.0.1:9000 10.0.0.2:9000 10.0.0.3:9000 {
+ lb_policy random_choose 2
+ health_path /ok
+ health_interval 10s
+}
+
+ example.com
+
+# Templates give static sites some dynamic features
+templates
+
+# Compress responses according to Accept-Encoding headers
+encode gzip zstd
+
+# Make HTML file extension optional
+try_files {path}.html {path}
+
+# Send API requests to backend
+reverse_proxy /api/* localhost:9005
+
+# Serve everything else from the file system
+file_server
+
+ POST /config/
+
+{
+ "apps": {
+ "http": {
+ "servers": {
+ "example": {
+ "listen": ["127.0.0.1:2080"],
+ "routes": [{
+ "@id": "demo",
+ "handle": [{
+ "handler": "file_server",
+ "browse": {}
+ }]
+ }]
+ }
+ }
+ }
+ }
+}
+
+ GET /config/
+
+ PUT /id/demo/handle/0
+
+{"handler": "templates"}
+
+
+ + Caddy is the only web server that uses HTTPS by default. A hardened TLS stack with modern protocols preserves privacy and exposes MITM attacks. +
++ As its primary mode of configuration, Caddy's REST API makes it easy to automate and integrate with your apps. +
++ Because Caddy is written in Go, its binaries are entirely self-contained and run on every platform, including containers without libc. +
++ Take back control over your compute edge. Caddy can be extended with everything you need using plugins. +
++ Caddy 2 was boldly engineered to simplify your infrastructure and give you control over the edge of your compute platform. +
+ + ++ Caddy can embed any Go application as a plugin, and has first-class support for plugins of plugins. +
++ Global state is common in servers, but tends to be error-prone and a bottleneck, so Caddy 2 uses a novel design that limits global state. +
++ For all its features, Caddy runs lightly and efficiently with relatively low memory footprint and high throughput. +
++ When the going gets tough, Caddy gets going on more CPUs. Go's scheduler understands Go code, and goroutines are more lightweight than system threads. +
++ Caddy is a single executable file with no dependencies, not even libc. Literally just needs some metal and a kernel. Put Caddy in your PATH and run it. Done. +
++ Caddy runs on Windows, macOS, Linux, BSD, Android, Solaris, 32-bit, amd64, ARM, aarch64, mips64... almost anything to which Go compiles. +
++ Caddy's native config format is JSON, so it is familiar and highly interoperable with exising systems and tools. +
++ Caddy's configuration is received through a REST endpoint as a single JSON document, making it highly programmable. +
++ You can use config files with Caddy's CLI, which converts them to API requests for you under the hood. +
++ Bring your own config! Config adapters translate various config formats (Caddyfile, TOML, NGINX, etc.) into Caddy's native JSON. +
++ An easy, intuitive way to configure your site. It's not scripting, and not hard to memorize. Rolls off the fingers. You'll really like it. +
++ All configuration is contained within a single JSON document so there are fewer hidden factors affecting your config. +
++ When you have just small changes to make, Caddy's API lets you update just the relevant parts of its config. +
++ Caddy's native JSON exposes the actual fields allocated in memory by the running server to give you more control. +
++ You can export a live copy of Caddy's current configuration with a GET request to its API. +
++ Config updates are finely tuned for efficiency so you can reload config dozens of times per second. +
++ Config changes take effect without downtime or closing sockets—even on Windows. +
++ You can use Caddy's CLI to preview and validate configurations before applying them. +
++ An easy, intuitive way to configure your site. It's not scripting, and not hard to memorize. Rolls off the fingers. You'll really like it. +
++ By default, Caddy will serve static files in the current working directory. It's so brilliantly simple and works fast. +
++ Caddy can also be used to serve dynamic sites with templates, proxying, FastCGI, and by the use of plugins. +
++ Customize how Caddy runs with its simple, cross-platform command line interface; especially great for quick, one-off server instances. +
++ Caddy can be extended with plugins. All apps, Caddyfile directives, HTTP handlers, and other features are plugins! They're easy to write and get compiled in directly. +
++ When the going gets tough, Caddy gets going on more CPUs. Go's scheduler understands Go code, and goroutines are more lightweight than system threads. So yeah, it's fast. +
++ Writing another program or web service that could use a powerful web server or reverse proxy? Caddy can be used like a library in your Go program. +
++ Caddy can parse and verify your Caddyfile without actually running it. +
++ Caddy can write a log of all its significant events, especially errors. Log to a file, stdout/stderr, or a local or remote system log! +
++ When log files get large, Caddy will automatically rotate them to conserve disk space. +
++ Caddy's flagship features are security and privacy. Caddy is the first and only web server to enable HTTPS automatically and by default. +
+ ++ TLS 1.3 is the newest standard for transport security, which is faster and more secure than its predecessors. +
++ Caddy uses the best crypto technologies including AES-GCM, ChaCha, and ECC by default, balancing security and compatibility. You can customize which ciphers are allowed. +
++ Caddy is the only web server in its class that is impervious to bugs like Heartbleed and buffer overflows because it is written in the memory-safe language of Go. +
++ With TLS client auth, you can configure Caddy to allow only certain clients to connect to your service. +
++ Caddy is proudly written in Go, and its TLS stack is powered by the robust crypto/tls package in the Go standard library, trusted by the world's largest content distributors. +
++ Companies choose Caddy because its TLS configuration is PCI-compliant by default. It has even saved some companies hours before losing certification! +
++ TLS assets are stored on disk, but the storage mechanism can be swapped out for custom implementations so you can deploy and coordinate a fleet of Caddy instances. +
++ Caddy is cited as the only web server to rotate TLS session ticket keys by default. This helps preserve forward secrecy, i.e. visitor privacy. +
++ Caddy uses the TLS extension Server Name Indication (SNI) to be able to host multiple sites on a single interface. Like most features, this just works. +
++ Caddy's automatic HTTPS feature includes redirecting HTTP to HTTPS for you by default. +
++ Caddy obtains certificates for you automatically using Let's Encrypt. Any ACME-compatible CA can be used. Caddy was the first web server to implement this technology. +
++ Never deal with certificates again! Certificates are automatically renewed in the background before they get close to expiring. +
++ Caddy is the only web server that can obtain certificates during a TLS handshake and use it right away. +
++ If you still prefer to manage certificates yourself, you can give Caddy your certificate and key files (PEM format) like you're used to. +
++ If you manage many certificates yourself, you can give Caddy an entire folder to load certificates from. +
++ For easy local development and testing, Caddy can generate and manage self-signed certificates for you without any hassle. +
++ Caddy fully accepts SAN certificates for times when you may be managing your own SAN certificates and wish to use those instead. +
++ Caddy can share managed certificates stored on disk with other instances and synchronize renewals in fleet deployments. +
++ Caddy's certificate management scales well up to tens of thousands of sites and tens of thousands of certificates per instance. +
++ When needed, Caddy can obtain and renew wildcard certificates for you when you have many related subdomains to serve. +
++ Caddy staples OCSP responses to every qualifying certificate by default. Caddy's OCSP stapling is more robust against network failure than other web servers. +
++ Every OCSP response is cached on disk to preserve integrity through restarts, in case the responder goes down or the network link is being attacked. +
++ Caddy can be configured to obtain Must-Staple certificates, which requires that certificate to always have the OCSP response stapled. +
++ Unlike other web servers, Caddy updates OCSP responses in the background, asynchronously of any requests, well before their expiration. +
++ An OCSP response will not be stapled unless it checks out for validity first, to make sure it's something clients will accept. +
++ If a managed certificate is discovered by OCSP to be revoked, Caddy will automatically try to replace the certificate. +
++ Caddy can solve the HTTP challenge to obtain certificates. You can also configure Caddy to proxy these challenges to other processes. +
++ Caddy solves the TLS-ALPN challenge which happens on port 443 and does not require opening port 80 at all. +
++ Caddy coordinates the obtaining and renewing of certificates in cluster configurations for both HTTP and TLS-ALPN challenges! +
++ Caddy solves the DNS challenge which does not involve opening any ports on the machine. There are integrations for all major DNS providers! +
++ If one of your private keys becomes compromised, you can use Caddy to easily revoke the affected certificates. +
++ Caddy is designed to be used with any ACME-compatible certificate authority, which you can customize with a single command line flag. +
++ Caddy is the only web server and only major ACME client that was not disrupted by CA changes and outages, or OCSP responder hiccups. +
++ Caddy's HTTP server has a wide array of modern features, high performance, and is easy to deploy. +
+ ++ List files and folders with Caddy's attractive, practical design or according to your own custom template. +
++ Serve multiple sites from the same IP address with the Caddyfile. +
++ You can select which network interfaces to which you bind the listener, giving you more access control over your site. +
++ Let Caddy render your Markdown files as HTML on-the-fly. You can embed your Markdown in a template and parse out front matter. +
++ A powerful and improved alternative to Server-Side Includes, templates allow you to make semi-dynamic sites quickly and easily. +
++ Show user-friendly error pages when things go wrong, or write the error details to the browser for dev environments. +
++ Caddy takes copious notes according to your favorite log format. Log errors and requests to a file, stdout/stderr, or a local or remote system log. +
++ You can limit the size of request bodies that go through Caddy to prevent abuse of your network bandwidth. +
++ Enabling timeouts can be a good idea when your server may be prone to slowloris attacks or you want to free up resources from slow networks. +
++ Still commonly used in plaintext, development, and debug environments, Caddy has solid support for HTTP/1.1. +
++ It's time for a faster web. Caddy uses HTTP/2 right out of the box. No thought required. HTTP/1.1 is still used when clients don't support HTTP/2. +
++ With the IETF-standard-draft version of QUIC, sites load faster and connections aren't dropped when switching networks. +
++ Caddy supports making WebSocket connections directly to local programs' stdin/stdout streams that work a little bit like CGI. +
++ Caddy supports both IPv4 and IPv6. In fact, Caddy runs full well in an IPv6 environment without extra configuration. +
++ Serve your PHP site behind Caddy securely with just one simple line of configuration. You can even specify multiple backends. +
++ Protect areas of your site with HTTP basic auth. It's simple to use and secure over HTTPS for most purposes. +
+
+ Caddy can issue HTTP redirects with any 3xx status code, including redirects using <meta>
tags if you prefer.
+
+ Customize the response headers so that some headers are removed or others are added. +
++ Caddy can act as a reverse proxy for HTTP requests. You can also proxy transparently (preserve the original Host header) with one line of config. +
++ Proxy to multiple backends using a load balancing policy of your choice: random, least connections, round robin, IP hash, or header. +
++ Caddy is frequently used as a TLS terminator because of its powerful TLS features. +
++ Caddy's proxy middleware is capable of proxying websocket connections to backends as well. +
++ Caddy marks backends in trouble as unhealthy, and you can configure health check paths, intervals, and timeouts for optimal performance. +
++ When a request to a backend fails to connect, Caddy will try the request with other backends until one that is online accepts the connection. +
++ By default, most headers will be carried through, but you can control which headers flow upstream and downstream. +
++ Proxy to arbitrary backends based on request parameters such as parts of the domain name or header values. +
++ Elegantly serve files without needing the extension present in the URL. These look nicer to visitors and are easy to configure. +
++ Caddy has powerful request URI rewriting capabilities that support regular expressions, conditionals, and dynamic values. +
++ Send a certain status code for certain requests. +
++ Compress content on-the-fly using gzip, Zstandard, or brotli. +
+There are no docs for this property.
'; + return; + } + $('#hovercard-docs').html(markdown(elemDocs)).show(); + $('#hovercard-inline-link').html('View docs below ↓').show(); + } + + // show hoverbox for this link + var height = $(this).height(); + var linkWidth = $(this).width(); + var boxWidth = $hovercard.width(); + $hovercard.css({ + 'top': pos.top + height*1.5 + 10, // '+10' counters 'translateY(-10px)' + 'left': pos.left + (linkWidth/2) - (boxWidth/2) + }).addClass('popup'); + }, + mouseleave: function() { + // don't hide the hoverbox right away; user might need a + // few milliseconds to get the cursor over the hovercard + hoverTimeout = setTimeout(function() { + $hovercard.removeClass('popup'); + }, 200); + } + }, '.has-popup'); + + var pathComponents = configPath.split('/'); + + // load the docs for this path + $.get("/api/docs/config"+configPath, function(json) { + pageData = json; + if (pageData.structure.doc) { + // for most types, just render their docs + $('#top-doc').html(markdown(pageData.structure.doc)); + } else if (pageData.structure.elems) { + // for maps or arrays, fall through to the underlying type + $('#top-doc').html(markdown(pageData.structure.elems.doc)); + } + $('#top-doc').append(makeSubmoduleList("", pageData.structure)); + + console.log("DATA:", pageData); + renderData(pageData.structure, 0, "", $('')); + console.log("DOCS:", pageDocs); + + if ($('#field-list').html().trim()) { + $('#field-list-header').show(); + } + + // establish the breadcrumb + var $bc = $('.breadcrumbs'); + $('JSON Config Structure ›').appendTo($bc); + for (var i = 1; i < pathComponents.length-1; i++) { + var bcPath = pathComponents.slice(0, i+1).join('/'); + var bcSiblingPath = pathComponents.slice(1, i).join('/'); + + // prefixing with is a hack so jQuery treats this as a HTML DOM object + $(' › '+pathComponents[i]+'').appendTo($bc); + } + }); + + function renderData(data, nesting, path, $group) { + switch (data.type) { + case "struct": + $group.append('{▾'); + nesting++; + + var $fieldGroup = $(''); + renderModuleInlineKey(data, nesting, $fieldGroup); + $group.append($fieldGroup); + + if (data.struct_fields) { + // TODO: Not sure if sorting the struct fields is a good idea... + // data.struct_fields.sort(function(a, b) { + // if (a.key > b.key) return 1; + // if (b.key > a.key) return -1; + // return 0; + // }); + for (var i = 0; i < data.struct_fields.length; i++) { + var field = data.struct_fields[i]; + var fieldPath = path+"/"+field.key; + var cleanFieldPath = fieldPath.slice(1); // trim leading slash + + // store the docs for this path + let linkClass = "documented"; + if (field.doc) { + pageDocs[fieldPath] = field.doc; + linkClass += " has-popup"; + } + + // render the docs to the page + var fieldDoc = markdown(field.doc) || 'There are no docs for this property.
'; + fieldDoc += makeSubmoduleList(fieldPath, field.value); + appendToFieldDocs(cleanFieldPath, fieldDoc); + + // render the field to the JSON box + var $fieldGroup = $(''); + indent(nesting, $fieldGroup); + $fieldGroup.append('"'+field.key+'": '); + renderData(field.value, nesting, fieldPath, $fieldGroup); + if (i < data.struct_fields.length-1) { + $fieldGroup.append(','); + } + $group.append($fieldGroup); + } + } + nesting--; + indent(nesting, $group); + $group.append('}'); + break; + + case "bool": + $group.append('false'); // TODO: default value? + break; + + case "int": + case "uint": + case "float": + case "complex": + $group.append('0'); // TODO: default value? + break; + + case "string": + $group.append('""'); // TODO: default value? + break; + + case "array": + $group.append('['); + if (data.elems.type == "module_map") { + $group.append('{•••}'); + } else { + renderData(data.elems, nesting, path, $group); + } + $group.append(']'); + break; + + case "map": + $group.append('{\n') + nesting++; + renderModuleInlineKey(data, nesting, $group); + indent(nesting, $group); + renderData(data.map_keys, nesting, path, $group); + $group.append(': '); + renderData(data.elems, nesting, path, $group); + $group.append('\n'); + nesting--; + indent(nesting, $group); + $group.append('}'); + break; + + case "module": + // TODO: + $group.append('{•••}'); + break; + + case "module_map": + // TODO: + $group.append('{•••}'); + break; + } + + $renderbox.append($group); + } + + function renderModuleInlineKey(data, nesting, $group) { + if (!data.module_inline_key) { + return + } + var moduleName = pathComponents[pathComponents.length-2]; + indent(nesting, $group); + $group.append('"'+data.module_inline_key+'": "'+moduleName+'"'); + if (data.struct_fields && data.struct_fields.length > 0) { + $group.append(','); + } + $group.append('\n'); + + appendToFieldDocs(data.module_inline_key, $('#hovercard-inline-key').html()); + } + + function appendToFieldDocs(cleanFieldPath, fieldDoc) { + $('#field-list').append('Fulfilled by modules in namespace: '+value.module_namespace+'
'+submodList+'