Web Server - Caddy

哺 育

Caddy, sometimes clarified as the Caddy web server, is an open source, HTTP/2-enabled web server written in Go. It uses the Go standard library for its HTTP functionality.

One of Caddy’s most notable features is enabling HTTPS by default.

Other web servers were designed for the Web, but Caddy was designed for humans.

Download and Install

Download Caddy from the Caddy Download Page and put it in your PATH. You can get Caddy for nearly any OS and architecture. Caddy’s download page is unique from other web servers: it lets you customize your build with plugins.

In linux, you install it from one command.

1
:~$ curl https://getcaddy.com | bash -s personal http.cache,http.cgi,http.filter,http.geoip,http.ipfilter,http.locale,http.login,http.minify,http.ratelimit,http.realip,http.webdav

Run

  1. By default, Caddy will use the current directory (the directory it is being executed from, not the folder where the binary lives) as the root of the site. This makes it easy to work on sites locally!
    1
    2
    3
    :~$ cd /var/www
    :/var/www$ caddy &
    :/var/www$ firefox 127.0.0.1
  2. run with systemd.service
    1
    :~$ sudo systemctl start caddy

Caddy.service

Github-Download-Caddy.service

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
:~$ sudo cat > /lib/systemd/system/caddy.service << EOF
[Unit]
Description=Caddy HTTP/2 web server
Documentation=https://caddyserver.com/docs
After=network-online.target
Wants=network-online.target systemd-networkd-wait-online.service

[Service]
Restart=on-abnormal

# User and group the process will run as.
User=www-data
Group=www-data

# Letsencrypt-issued certificates will be written to this directory.
Environment=CADDYPATH=/etc/ssl/caddy

# Always set "-root" to something safe in case it gets forgotten in the Caddyfile.
ExecStart=/usr/local/bin/caddy -log stdout -agree=true -conf=/etc/caddy/Caddyfile -root=/var/tmp
ExecReload=/bin/kill -USR1 $MAINPID

# Use graceful shutdown with a reasonable timeout
KillMode=mixed
KillSignal=SIGQUIT
TimeoutStopSec=5s

# Limit the number of file descriptors; see `man systemd.exec` for more limit settings.
LimitNOFILE=1048576
# Unmodified caddy is not expected to use more than that.
LimitNPROC=512

# Use private /tmp and /var/tmp, which are discarded after caddy stops.
PrivateTmp=true
# Use a minimal /dev (May bring additional security if switched to 'true', but it may not work on Raspberry Pi's or other devices, so it has been disabled in this dist.)
PrivateDevices=false
# Hide /home, /root, and /run/user. Nobody will steal your SSH-keys.
ProtectHome=true
# Make /usr, /boot, /etc and possibly some more folders read-only.
ProtectSystem=full
# … except /etc/ssl/caddy, because we want Letsencrypt-certificates there.
# This merely retains r/w access rights, it does not add any new. Must still be writable on the host!
ReadWritePaths=/etc/ssl/caddy

# The following additional security directives only work with systemd v229 or later.
# They further restrict privileges that can be gained by caddy. Uncomment if you like.
# Note that you may have to add capabilities required by any plugins in use.
CapabilityBoundingSet=CAP_NET_BIND_SERVICE
AmbientCapabilities=CAP_NET_BIND_SERVICE
NoNewPrivileges=true

[Install]
WantedBy=multi-user.target
EOF
:~$ sudo systemctl daemon-reload
:~$ sudo systemctl start caddy

Example of Configure

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
http://www.example.com {
redir / https://{host}{uri}
}

https://www.example.com {
log /var/log/caddy/www.example.com.log {
rotate_size 50 # Rotate after 50 MB
rotate_age 90 # Keep rotated files for 90 days
rotate_keep 20 # Keep at most 20 log files
rotate_compress # Compress rotated log files in gzip format
}
gzip {
not /images
}
cache
tls example@gmail.com {
#only accept tls1.3, default: protocols tls1.2 tls1.3
protocols tls1.3
}
root /var/www/public
proxy / localhost:50443 {
websocket
header_upstream -Origin
}
basicauth /webdav username passwd
basicauth /webdav username2 passwd2
webdav /webdav {
scope /var/www/media
modify true
block ./public
block ./*.sh
}
rewrite {
# If the client browser supports images in webp format
# automatically send webp format images to the client
if {>Accept} has image/webp
r ^(.+)\.(jpg|jpeg|png)$
to {1}.webp {path} {path}/
}
}

# h5ai -- file manage server
# because our website uses cloudflare CDN to speed up,
# the plugin tls.dns.cloudflare is only way to apply for Let's Encrypt SSL certificates.
media.example.com:8080, videos.example.com:8000 {
log /var/log/caddy/media.example.com.log
tls {
dns cloudflare
}
root /var/www/h5ai
fastcgi / /run/php/php7.3-fpm.sock php
rewrite {
if {path} ends_with /
to /_h5ai/public/index.php
}
redir 301 {
# It works only if the request header contains the X-Forwarded-Proto attribute.
if {>X-Forwarded-Proto} is http
/ https://{host}{uri}
}
}

Caddy Docs

Caddy-Docs

http.log

Caddy-Docs-log

log enables request logging. The request log is also known from some vernaculars as an access log.

Syntax

With no arguments, an access log is written to access.log in the common log format for all requests:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
www.example.com {
log file
...
}

www.example.com {
log path file [format] {
rotate_size 10 # 10mb
rotate_age 9 # 9 days
rotate_keep 5 # 5 count
rotate_compress
ipmask ipv4_mask [ipv6_mask]
except /images /api /fonts
}
...
}
  • path is the base request path to match in order to be logged.
  • file is the log file to create (or append to), relative to current working directory.
  • format is the log format to use; default is Common Log Format.
  • rotate_size is the size in megabytes a log file must reach before rolling it.
  • rotate_age is how long in days to keep rotated log files.
  • rotate_keep is the maximum number of rotated log files to keep; older rotated log files get pruned.
  • rotate_compress is the option to compress rotated log files. gzip is the only format supported.
  • ipmask enables masking IP addresses to comply with corporate or legal restrictions. The first argument is a mask for IPv4 addresses, and the second argument is a mask for IPv6 addresses. The IPv6 mask is optional; and if only IPv6 is to be masked, the IPv4 mask can be an empty string token.
  • except exempts requests by path from being logged. More than one path can be specified per line (space-separated), if desired, or this subdirective can be used multiple times.

Log Format

You can specify a custom log format with any placeholder values.
Log supports both request and response placeholders.

Currently there are two predefined formats.

  • {common} (default)
    1
    {remote} - {user} [{when}] \"{method} {uri} {proto}\" {status} {size}
  • {combined}
    1
    {remote} - {user} [{when}] \"{method} {uri} {proto}\" {status} {size} \"{>Referer}\" \"{>User-Agent}\"

Custmos log format

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
:~$ cat > /etc/caddy/Caddyfile <<EOF
www.example.com {
log / /var/log/caddy.log {combined}
...
}
EOF
:~$ cat /var/log/caddy.log
1.1.1.1-- [14/Sep/2019:11:09:13 +0800] "GET /sitemap.xml HTTP/1.1" 200 849 "https://www.baidu.com" "Mozilla/5.0 (X11; Linux x86_64; rv:69.0) Gecko/20100101 Firefox/69.0"
2.2.2.2-- [14/Sep/2019:14:02:06 +0800] "GET /index.html HTTP/1.1" 200 1024 "https://www.bing.com" "Mozilla/5.0 (Android 9; Mobile; rv:67.0.3) Gecko/67.0.3 Firefox/67.0.3"
:~$
:~$ cat > /etc/caddy/Caddyfile <<EOF
www.example.com {
log / /var/log/caddy.log "{remote}-{user} [{when}] \"{tls_protocol} {method} {uri} {proto}\" {status} {size}"
...
}
EOF
:~$ cat /var/log/caddy.log
1.1.1.1-- [14/Sep/2019:11:09:13 +0800] "tls1.2 GET /sitemap.xml HTTP/1.1" 200 849
2.2.2.2-- [14/Sep/2019:14:02:06 +0800] "tls1.2 GET /index.html HTTP/1.1" 304 0

http.gzip

Caddy-Docs-gzip

gzip enables gzip compression if the client supports it. By default, responses are not gzipped. If enabled, the default settings will ensure that images, videos, and archives (already compressed) are not gzipped.

Note that, even without the gzip directive, Caddy will serve .gz (gzip) or .br (brotli) compressed files if they already exist on disk and the client supports that encoding.

Syntax

1
2
3
4
5
6
7
8
9
10
11
12
www.example.com {
gzip
...
}
web.example.com {
gzip {
ext extensions...
not paths
level compression_level
min_length min_bytes
}
}
  • extensions… is a space-separated list of file extensions to compress. Supports wildcard * to match all extensions.
  • paths is a space-separated list of paths in which not to compress.
  • compression_level is a number from 1 (best speed) to 9 (best compression). Default is 6.
  • min_bytes is the minimum number of bytes in a response needed before compression will happen. Default is no minimum length.

Example

1
2
3
4
5
6
7
8
9
10
web.example.com {
gzip {
ext *.mp4 *.mkv
not /images /fonts
level 4
min_length 4096 # 4KB
}
log /var/log/caddy.log
...
}

http.root

Caddy-Docs-root

root simply specifies the root of the site. This is very useful, in fact required, if the root (/) directory of the website is not the same as where Caddy is being executed from.

The default root is the current working directory. A relative root path is relative to the current working directory.

http.webdav

Caddy-Docs-Http.webdav

Syntax

1
2
3
4
5
6
7
8
9
10
11
12
www.example.com {
# use basicauth if you want add a password of webdav
# basicauth [url] usename passwd
webdav [url] {
scope path
modify [true|false]
allow path
allow_r regex
block path
block_r regex
}
}

All the options are optional.

  • url is the place where you can access the WebDAV interface. Defaults to /.
  • scope is an absolute or relative (to the current working directory of Caddy) path that indicates the scope of the WebDAV. Defaults to ..
  • modify indicates if the user has permission to edit/modify the files. Defaults to true.
  • allow and block are used to allow or deny access to specific files or directories using their relative path to the scope. You can use the magic word dotfiles to allow or deny the access to every file starting by a dot.
  • allow_r and block_r and variations of the previous options but you are able to use regular expressions with them.

Example

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
:~$ cat > /etc/caddy/Caddyfile << EOF
www.example.com {
root /var/www/
webdav /webdav {
scope /mnt/media
modify false
block *.sh
block _*
}
}
EOF
:~$ ls /mnt/media
a.mp4 b.mp4 c.mp4 a.sh _b.mp4
:~$ cadaver http://www.example.com/webdav
> ls
a.mp4 b.mp4 c.mp4

http.basicauth

Basic Authentication can be used to protect directories and files with a username and password.

Note that basic auth is not secure over plain HTTP. Use discretion when deciding what to protect with HTTP Basic Authentication.

When a user requests a resource that is protected, the browser will prompt the user for a username and password if they have not already supplied one.
If the proper credentials are present in the Authorization header, the server will grant access to the resource and set the {user} placeholder to the value of the username.
If the header is missing or the credentials are incorrect, the server will respond with HTTP 401 Unauthorized.

Syntax

1
2
3
4
5
6
7
8
9
www.example.com {
basicauth path usename password
}
media.example.com {
basicauth username password {
realm name
resources
}
}
  • path is the file or directory to protect
  • username is the username
  • password is the password
  • realm identifies the protection partition; it is optional and cannot be repeated. Realms are used to specify the space in which the protection applies. This can be convenient for user agents that are configured to remember authentication details (which is most browsers).
  • resources is a list of files/directories to protect, one per line.

Example

Add a Password for WebDAV with basicauth

1
2
3
4
5
6
7
8
www.example.com {
basicauth /webdav username password
basicauth /webdav username2 password2
webdav /webdav {
scope /var/www/media
modify false
}
}

http.redir

redir sends the client an HTTP redirect status code if the URL matches the specified pattern. It is also possible to make a redirect conditional.

Syntax

1
2
3
4
5
redir from to [code]

redir [code] {
from to [code]
}

Example

Redirect http to https

1
2
3
4
5
6
7
http://www.example.com {
redir https://{host}{uri}
}

https://www.example.com {
...
}

http.proxy

1
2
3
4
5
6
7
8
9
10
11
12
13
localhost:8080 {
root /var/www
tls off
}
example.com {
tls off
ext .html
gzip
log / stdout
proxy /api localhost:8080 {
without /api
}
}

request to http://example.com/api/abcd hits http://localhost:8080/abcd


Preference:

  1. Caddy-website
  2. Caddy-download
  3. Caddy-docs