Caddy 0.9 beta version available [UPDATED beta 2]

Caddy 0.9 beta 2

Download binaries or build from source (go get -u

Get excited!! Caddy 0.9 is coming, and its beta is available now. This means Caddy 0.9 is not yet considered stable for important production use, but we do want you to use it in production if you have smaller or unimportant sites to try it on.

Add-ons: During the beta period, we’ll be helping to transition existing add-ons into plugins for 0.9. Add-on authors must convert their add-ons into plugins, but it’s easy and I’m willing to help answer questions. If you’re an add-on author, I’ll be reaching out to you individually after we figure out details regarding the updated build server…

Caddy 0.9 is not finished yet and this is not a release announcement. Some more changes and work is going into Caddy before the 0.9 release. Check this thread or GitHub for newer beta versions!

Version 0.9 re-focuses Caddy on serving. That is what it does best. It just happens to serve HTTP by default. And it serves the user by making things easy. And we’ll continue to emphasize serving the user before the Web.

Things to Be Aware Of

  • This is the biggest change yet. I rewrote Caddy from an empty func main() { } and brought over functions or files one at a time from the old code base as needed. Caddy is now much more flexible and, I believe, robust. Its logic is more clearly separated and simplified in many cases.

  • Most of the middleware is unchanged, and how you use Caddy is mostly the same.

  • The markdown directive no longer performs static site generation. But it still renders Markdown on-the-fly, of course!

  • TLS assets stored in $HOME/.caddy/letsencrypt folder have moved to $HOME/.caddy/acme and are reorganized by the hostname portion of the -ca URL. It doesn’t work to mix accounts and keys with different CAs, so now we organize these files by the name of the CA. We also lower-case all the hostnames and email addresses so they are not case-sensitive. Caddy quietly facilitates this migration for you when you first run it. You should not notice anything.

  • Caddy’s main() function moved to, with the actual logic in The build script (build.bash) has also moved down into the caddy subfolder.

  • The caddy package that other Go programs import to use Caddy as a library is now the top-level package, making the import path simply

Things to Try

Make sure everything that used to work (that should work), still works. Then try any of these.

  • Experimental QUIC support. Use -quic to enable it for HTTPS sites. Learn more

  • Paths in site addresses. There is no inheritance; all sites are distinct. These two sites will be treated as separate sites, even though they share a host and port: {
} {
  • Site addresses can now have wildcards in their hostname. Use of wildcards must conform to RFC 6125. In other words, * is valid, but * is not, and neither is example.*. So, * will match but not The reason for this is in case automatic HTTPS is able to get wildcard certificates in the future.

  • DNS challenge. When you need a certificate from Let’s Encrypt but can’t bind to a low port (80 or 443) or that port can’t be accessed from the outside, try the DNS challenge. Make sure you plug in your DNS challenge provider and set credentials in environment variables. See linked repo for docs; we’ll have full documentation coming soon.

tls {
    dns cloudflare
  • Graceful reloads (signal USR1) without spawning a new process. Hurray! Make sure it works well for you.

  • Self-signed certificates generated for you. Use tls self_signed in your Caddyfile for Caddy to generate and use a self-signed (untrusted) certificate for that site. Great for local dev and testing. You can bypass certificate warnings in Chrome for localhost sites by using a Chrome flag.

  • Write a new plugin for Caddy. Plugins can not only inject middleware, they can change how the Caddyfile is loaded, or even implement a server type. Thus, the HTTP server is a plugin. Learn how and give feedback to improve the wiki!

  • See which plugins are plugged in by running caddy -plugins (replaces -directives)

  • The {request} placeholder in log formats. If you want to dump the whole request (minus the body) to a log file.

  • Specify multiple upstreams with port ranges using the upstream keyword in the proxy directive.

  • Change the storage location from $HOME/.caddy to a custom folder of your choosing by setting the $CADDYPATH environment variable.

  • If you found yourself doing this in your proxy directives:

proxy_header Host {host}
proxy_header X-Real-IP {remote}
proxy_header X-Forwarded-Proto {scheme}

try replacing those lines with a single transparent keyword. This preset sets those headers for you.

Beta version updates

There will almost surely be more than one beta version. Check this thread and the GitHub releases for updates! And follow @mholt6 and/or @caddyserver on Twitter.


Be sure to open an issue if you find bugs, or start a new thread in the forums if you have other questions or feedback. Thanks for trying Caddy!


I’ll check it out soon :slight_smile:

1 Like

I love this! It will make putting different services on different paths easier:

  • {}
  • {}
  • {}

I do prefer subdomains for this, but not everyone can/wants to.

Thank you for not giving them any inheritance as well.
I like the rules for wildcard subdomains.

1 Like

Is LE going to allow you to do this? It’d be awesome for putting customers on their own subdomain.
Will there be some kind of check done before getting a cert the first time? I’d hope it wouldn’t register a cert if is a 404. If it did then anyone could DDOS your LE quota. :stuck_out_tongue:

should plugins update their master branch now, or should we push somewhere else for the time being?


I’m not going to deploy it just yet, but I shall keep an eye on this and when I’m next doing maintenance on I will most likely upgrade Caddy too!

I don’t know.

See Automatic HTTPS — Caddy Documentation

Pushing to master is fine; you won’t break the build server, if that’s what you mean.

1 Like

Is there any prelimary performance benchmarks? :slight_smile:

It’s “but * is not”

Tested sigusr1 reload. caddy.log says:

2016/06/08 23:44:40 [INFO] SIGUSR1: Reloading
2016/06/08 23:44:40 [INFO] Reloading
2016/06/08 23:44:40 [INFO] Reloading complete

But change (different access log filename) is ignored? Bug?

Yeah, fixing:

1 Like

Great :slight_smile:

I think is related to a port bind problem in reload?

A “-port=80” or change a “:80” vhost to “127:0.0.1:80” results in an already in use error.

I’m encountering the following error upon start with my Caddyfle:

panic: runtime error: invalid memory address or nil pointer dereference
[signal 0xb code=0x1 addr=0x59 pc=0x4d7721]

goroutine 1 [running]:
panic(0xaf44a0, 0xc82000a0c0)
        /usr/local/go/src/runtime/panic.go:481 +0x3e6, 0x0, 0x0)
        /Users/matt/Dev/src/ +0x211, 0x0)
        /Users/matt/Dev/src/ +0x276, 0xbdcb90, 0x9, 0xf91da0, 0x22, 0x22, 0xc820198700, 0x1, 0x1, 0x0, ...)
        /Users/matt/Dev/src/ +0x9ec, 0xc82019a3c0, 0xc8201b8000, 0x0, 0x0, 0x0)
        /Users/matt/Dev/src/ +0x522, 0xc82019a3c0, 0x7f3635612130, 0x0, 0x0)
        /Users/matt/Dev/src/ +0xc8
        /Users/matt/Dev/src/ +0x924
        /Users/matt/Dev/src/ +0x14


log / /var/log/caddy/access.log "{when} {proto} Request from {remote} type {method} to {path} proxy to {upstream} and status {status}" {
    rotate {
        size 100
        age 14
        keep 10

errors {
    log /var/log/caddy/errors.log {
        size 100
        age 14
        keep 10

tls /etc/caddy/cert-VPd3Ea /etc/caddy/cert-VPd3Ea

header / {
    Strict-Transport-Security "max-age=31536000"
    X-Frame-Options "SAMEORIGIN"
    X-Content-Type-Options "nosniff"
    X-XSS-Protection "1; mode=block"
    Access-Control-Allow-Origin "{scheme}://{host}"

proxy / {
    proxy_header Host {host}
    proxy_header X-Real-IP {remote}
    proxy_header X-Forwarded-For {remote}
    proxy_header X-Forwarded-Proto {scheme}

Distributor ID: Debian
Description: Debian GNU/Linux 8.5 (jessie)
Release: 8.5
Codename: jessie

Hey Chris, thanks for the report! I recently found out this happens when you bring your own certificates (a recent change made it sneak by a test I did; working on that) – but the fix has been committed already: - and will be available in beta 2.


Great, thank you!

I love the update so far. I just wanted to mention that the rewrite directive/plugin does not rewrite to sites that are pages. For example: {
	rewrite / /page
} {
	browse /

Accessing will just return a 404.

Is this intentional or can/will it be changed?

I think you may want {
	rewrite / /page
    browse /page

Yes I know that’s an option, I’m just reporting a possibly unintended behaviour, as some people may expect the configuration I posted above to actually show the file browser page.

Oh okay my bad.

I do believe the most specific site is supposed to match first. But I think right now you have to order them with the most specific first in the Caddyfile (though this is supposed to be changing.)

What happens when you use this? {
	browse /
} {
	rewrite / /page