Caddy WebServer入門
※ こちらで同じ記事を書いています
CaddyはGolangで書かれた軽量かつ高機能なWebサーバです
Let'sEncryptの証明書を自動生成したり、HTTP/3に逸早く対応出来たりと高機能なのが特徴です
また、設定ファイルも柔軟かつ簡単に記述出来ます
しかし設定ファイルに書き方が色々ありすぎて、結局どう書くのが正解なのかよく分からないという問題もあります
(2020年5月にバージョン2になりましたが、そのためネットで見つかる解決策がバージョン1のものが多かったりもします)
現在はWebServerというとNginxが優勢な状況ですが、情報が増えてくれば今後はCaddyの使用率も上がるかもしれません
1.Caddyの実験環境を作る
環境を汚さず手元で簡単に動かすためにDockerを使用します
1.1 ファイルの作成
実験が簡単に行えるように最小限の設定を記述したファイルを作ります
version: "3.7"
services:
caddy:
container_name: caddy
image: caddy:alpine
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile
- ./contents :/contents
ports:
- "80:80"
- "443:443/tcp"
- "443:443/udp"
:80 {
file_server {
root /contents
}
}
<html>
<head>
<title>Caddyテスト</title>
</head>
<body>Caddyテストコンテンツ</body>
</html>
1.2 実行
以下のコマンドでCaddyを実行します
最後に-dを付けていないのは、その場でエラーメッセージを確認するためです
docker-compose -f docker-compose.yml up
1.3 実行結果
http://localhost にアクセスして、以下の結果を確認します
Caddyテストコンテンツ
2.各種設定
2.1 VirtualHost
CaddyのVirtualHost機能を使ってみます
127.0.0.1:80 {
file_server
root * /contents/host1
}
localhost:80 {
file_server
root * /contents/host2
}
<html>
<head>
<title>Caddyテスト</title>
</head>
<body>
HOST1
</body>
</html>
<html>
<head>
<title>Caddyテスト</title>
</head>
<body>
HOST2
</body>
</html>
2.1.1 結果
以下のように表示されます
http://127.0.0.1 -> HOST1
http://localhost -> HOST2
2.2 location
nginxのlocation的なものを試してみます
ファイル配置は先ほどの続きになります
想定している結果は以下の内容です
http://localhost/a/ -> HOST1
http://localhost/b/ -> HOST2
2.2.1 駄目な例
:80 {
file_server
root /a/* /contents/host1
root /b/* /contents/host2
}
これは罠です
http://localhost/a にアクセスすると /contents/host1/a にファイルを探しに行きます
nginxと同じつもりで書くと、ファイルは見つかりません
2.2.2 正しい書き方
:80 {
file_server
handle_path /a/* {
root * /contents/host1
}
handle_path /b/* {
root * /contents/host2
}
}
handle_pathを使うと、ブロック内部へパスが結合されなくなります
かなり重要な項目なのですが、caddyのhandle_pathに関して日本語の情報は皆無でした
再利用可能ブロック
(SETTING1) {
root * /contents/host1
}
(SETTING2) {
root * /contents/host2
}
:80 {
file_server
handle_path /a/* {
import SETTING1
}
handle_path /b/* {
import SETTING2
}
}
()で囲んで名前を付けて、importで呼び出します
定義位置はブロックの中でも問題ありません
2.3 Methodによるアクセス分岐
:80 {
@PATH_A {
method GET POST
path *
}
@PATH_B {
method POST
path *
}
file_server
handle_path /a/* {
root @PATH_A /contents/host1
}
handle_path /b/* {
root @PATH_B /contents/host2
}
}
http://localhost/a/ はGETでもPOSTでもアクセスできます
http://localhost/a/ はPOSTのみコンテンツを返します
@の宣言はブロック内でのみ可能です
2.4 Headerの操作
:80 {
file_server
handle_path /a/* {
root * /contents/host1
header {
Content-Type text/plain
}
}
handle_path /b/* {
root * /contents/host2
}
}
http://localhost/a/ にアクセスするとHTMLがテキストとして表示されます
以下の書き方も可能です
:80 {
file_server
handle_path /a/* {
root * /contents/host1
}
handle_path /b/* {
root * /contents/host2
}
header /a/* {
Content-Type text/plain
}
}
2.5 Proxy
:80 {
file_server
handle_path /proxy/* {
reverse_proxy / unix//app/sock/app.sock {
header_up Location_path /proxy
}
}
}
reverse_proxyで他のプロセスなどからコンテンツをとってくることも出来ます
UNIXソケットを使う場合はunix/という書き方になります
また、http://アドレスでコンテンツを取得することも可能です
header_upはProxy先にヘッダを追加するときに使用します
header_downでレスポンスのヘッダを操作することも可能です
2.6 httpsとLet'sEncryptの証明書発行
www.example.com {
file_server {
root /contents
}
}
httpsを使いたいときはドメイン名を書くだけです
対象ドメインでアクセス可能な状態になっていれば、特に追加設定を書かずともLet'sEncryptの証明書は自動的に生成されます
(httpでのアクセスは自動的にhttpsにリダイレクトされます)
2.7 レスポンスの強制指定
:80 {
file_server
respond * 200 {
body "Hello World"
close
}
}
どこにアクセスしてもHello Worldを返します
2.8 HTTP3を使用する
{
experimental_http3
}
www.example.com:443 {
file_server {
root /contents
}
}
experimental_http3を追加するだけです
あとはサーバが443/UDPに対するアクセスが可能になっていれば、自動的にHTTP/3が使用されます
個別のブロックの中にexperimental_http3を記述すれば、特定のドメインのみHTTP/3を有効に出来ます
2.9 アクセス制限
このサンプルはWordpressによく見られる攻撃をループバックアドレスへリダイレクトする設定です
remote_ipを単独で使えば対象のアドレスを設定でき、notを使えば対象外を設定できます
www.example.com {
…
@disallowed {
path /xmlrpc.php
path *.sql
path /wp-content/uploads/*.php
path /wp-login.php
not {
remote_ip 許可したいIP
}
}
redir @disallowed http://127.0.0.1/
}
2.10 ログ設定とimportの引数
ログの出力設定でargsを使っています
特定のログ出力設定を複数書きたい場合などに便利です
argsはimportから引数を渡せます
(log) {
log {
format filter {
wrap json
fields {
common_log delete
}
}
output file /var/log/access-{args.0}.log {
roll_size 100mb
roll_keep 5
roll_keep_for 30d
}
}
}
www.example.com {
import log ファイル名
}
2.11 私の使っている共通設定
テキストファイルの圧縮や画像ファイルのキャッシュ設定が主です
(default) {
file_server
push
@encode {
path / *.html *.js *.css *.svg
}
encode @encode zstd gzip
@static {
file
path *.ico *.css *.js *.gif *.jpg *.jpeg *.png *.svg *.woff *.woff2 *.webp
}
header @static Cache-Control public, max-age=31536000, immutable
}
www.example.com {
import default
}
3.まとめ
Caddyの設定はブロックを省略して一行で全て完結するような書き方もあり、同じことをやろうと思っても、色々書き方が可能です。
柔軟と言えば柔軟なのですが、そのせいで分かりにくくなっている部分もあります。
設定を行うときは、とにかく色々試してみて、動くかどうか確認してみるというのが一番のようです。