簡介
Caddy 是一個年輕的 web server,以 Go 語言撰寫,特色:
- 支援自動從 Let’s Encrypt 取得與更新 TLS 1.3 加密憑證。
- 以 HTTPS 作為預設的通訊協定。
- 支援 HTTP/3。
- 支援 IPv6。
- 不依賴其它套件或 runtime,可獨立執行,因此很適合放在容器內跑。
- 以 production 為目標,可替代爺爺級的 Apache 或 Nginx。
- 提供組態設定 API。
- 支援 virtual host。
- 支援 reverse proxy。
- 支援 rewrite。
- 支援壓縮。
當然其它特色還很多,但其中比較值得一提的就是上面這幾點。
安裝
前面提到 Caddy 本身不用依賴其它套件,所以官方發布的就只是個單一的執行檔,撇開之後由我們自己產生的設定檔不算的話,這就是 Caddy 的全部了!
依照 Caddy 網站的路徑下載 caddy,這裡我們直接抓 Caddy 2 的版本(以下的範例也都是以 Caddy 2 為基準),抓下來的檔名因版次或平台不同會有差異,先改名成簡潔的 caddy
,再幫它加一下可執行的屬性:
> chmod a+x ./caddy
除了自行下載外,一些 Linux 也有納入 Caddy 包,以 Ubuntu 系為例:
> sudo apt install caddy
用套件包安裝還會自動配置好系統服務,但是以開發環境來說這反而有點礙手了,往往我會把服務關掉:
> sudo systemctl disable caddy.service
免得它跟我搶 port。
靜態網頁伺服器
在 Caddy 所在位置新增一個 index.html,內容如下:
<h1>Hello Caddy!</h1>
最單純的用法,這個簡易的指令體現了 Caddy 的簡潔:
> ./caddy file-server
2019/11/30 00:36:24.301 WARN admin admin endpoint disabled
2019/11/30 00:59:38.744 INFO tls cleaned up storage units
2019/11/30 08:36:24 [INFO][cache:0xc0000d0140 Started certificate maintenance routine
2019/11/30 08:36:24 Caddy 2 serving static files on :2015
從最後一行我們可以知道埠號是 2015,Caddy 會去找當前位置下的 index.html 來顯示,因此用瀏覽器開 127.0.0.1:2015 應該會看到如下圖畫面:
檔案伺服器
先把剛剛的 index.html 改名或刪除,再加上 --browse
來跑,--browse
表示如果找不到 index.html,就列出當前位置下的檔案:
> ./caddy file-server --browse
2019/11/30 00:36:24.301 WARN admin admin endpoint disabled
2019/11/30 08:36:24 [INFO][cache:0xc0000d0140 Started certificate maintenance routine
2019/11/30 08:36:24 Caddy 2 serving static files on :2015
從最後一行我們可以知道埠號是 2015,打開 127.0.0.1:2015 應該可以看到類似下面的畫面:
前面指令內的 --browse
表示開啟瀏覽目前 Caddy 所在位置的目錄檔案清單 ,也就是上面看到的抓圖內容。
Caddyfile
前面都是簡單的範例,如果要做比較正規的應用,則需要一些組態設定,在 Caddy 的世界,除了用 API 之外,還支援 Caddyfile 的設定檔,API 與 Caddyfile 不同之處在於,API 必須使用 JSON 格式傳遞溝通,而 Caddyfile 的語法更像是傳統的定檔的格式,對人類來說更容易讀寫。
這裡必須再提一次這篇文章是講 Caddy 2,因為 Caddy 1 與 Caddy 2 的 Caddyfile 是不同的,雖然不同,但只是設定檔的關鍵字與格式不同,整體的 Caddyfile 語法結構還是相同的,所以還是得參考 Caddy 1 的 The Caddyfile Syntax 來了解 Caddyfile 的結構。
沿用剛才的例子,做一個簡單的 index.html,以及一個 Caddyfile 內容如下:
localhost:8080 {
file_server
}
應該很容易理解,第一行表示監聽 localhost:8080,並且後方以大括號包住的區塊都是這組 localhost:8080 的設定。(在 Linux 針對埠號 1024 以下的常用埠號有權限限制,如果沒有權限的用戶要監聽埠號 80 的話會被系統阻擋,故要監聽 1024 以下的埠號須以 root 權限跑 Caddy。)
第二行表示啟動靜態檔案伺服,有 index.html 就會回應出去,並且未開啟目錄瀏覽。
跑起來看看:
> ./caddy run
caddy run
會自己去找同樣位置下的 Caddyfile,所以應該會看到同樣的 Hello Caddy! 網頁:
Virtual Host
如果想要設定兩組網址,以 www1.example.com 與 www2.example.com 為例,可能的 Caddyfile 會長這樣:
www1.example.com:80 {
root /www/www1.example.com
file_server
}
www2.example.com:80 {
root /www/www2.example.com
file_server
}
在上面的例子中,我們用 root
來指定該組網址對應到的真實路徑。
反向代理
Caddy 可以當坦承接流量,並把請求轉送到後端的服務,而且配置超簡單,非常適合跑當代百花齊放的框架、語言。
一般服務在正式環境都會配有所謂的 app server,它負責調配服務的資源,例如 Python 系的 Gunicorn、Ruby 的 Passenger、PHP-FPM 等等,不論是哪種,Caddy 都可以扮演他們的反向代理,典型的 Caddyfile 配置會像這樣:
roysucks.com {
encode zstd gzip
reverse_proxy /* 127.0.0.1:8000
}
沒了,真的只有這樣,並且該轉送的 HTTP 標頭一個也不少。
如果是 PHP,那 Caddy 更有專門為它服務的 php_fastcgi
可用,Caddyfile 大概會像這樣:
php_fastcgi /* 127.0.0.1:9000
是不是超級簡單,香~,當然這些都只是最簡單的用法,更進階的配置請去看 Caddy 文件囉!
實際範例
Caddyfile 可用的指令頗多,難以一一解釋,下面是本人比較常用的範例:
前後端都走反向代理
{
debug
auto_https disable_redirects
email [email protected]
}
http:// {
# Enable Zstandard and Gzip compression
encode zstd gzip
log {
# output file /var/log/access.log
}
handle_path /api/* {
reverse_proxy :8000
}
handle {
reverse_proxy :3000
}
}
這是一個典型前後端分離、開發環境的配置,分為兩個區塊,最上面那塊沒有標注位址的是 Caddy 的全域設定,意義如下:
debug
:在開發期間讓 log level 設為 debug,這要搭配下面的log
共同使用。auto_https disable_redirects
:關閉自動把用戶從 HTTP 導向 HTTPS 的機制,個人不太喜歡這種隱式的重導,一般都會關掉。email
:申裝 TLS 憑證用的信箱,我們只需要提供信箱,其他 Caddy 會辦到好。
第二個區塊就是具體的位址的設定,在這裡位址設定成 http://
,這表示不需要申裝 TLS,因為想要讓開發環境單純一點。
區塊內的部份:
encode zstd gzip
:讓網頁壓縮傳輸,在開發環境沒什麼意義,可開可不開。log
:開啟 log 輸出,輸出目的是 stdout,如果把註解拿掉就會寫入檔案,log level 則會是前面全域設的 debug。handle_path /api/*
:把所有送往 /api/* 的請求轉送到位於 8000 埠的後端服務。handle
:把所有請求(除了上面的 /api/*)轉送到 3000 埠的前端服務。
handle_path
和 handle
的區別為,handle_path
會去除掉 /api/
再轉送給後端,因為我的後端並不認得那 /api/ 路徑,而 handle
則會原樣轉送,該用哪個就取決於每個環境的狀況囉!
後端走反向代理 前端為靜態 SPA
全域設定、log 設定參照前例,在此省略。
http:// {
# Set this path to your site's directory.
root * /usr/share/caddy/
# Enable the static file server.
file_server
# Enable Zstandard and Gzip compression
encode zstd gzip
handle_path /api/* {
# Proxies requests to one or more backends
reverse_proxy unix//run/gunicorn.sock
}
handle {
try_files {path} / # For built static site
}
}
跟前例大同小異,只差幾個指令:
- 多設定了
root
指定靜態網頁的檔案路徑、file_server
啟動靜態資源伺服器。 - 最後的
try_files
把所有的網頁(除了那 /api/*)導向 SPA 的首頁,因為 SPA 通常都有自己的路由邏輯。
這裡的 SPA 是 Vue 的例子,把所有對 SPA 的請求都丟給 Vue Router 處理。
最後提醒一個重點中的重點:
檔案路徑要設對,不要像我設錯查半天。
結語
這篇文章介紹了 Caddy 2 的特性與 Caddyfile 的基本設定,Caddy 原生支援 HTTPS、HTTP/3、virtual host、反向代理,並且配置超級簡單,效能又好,實在沒有再回去用陳年的 Apache 的理由。
Caddy 2 的文件還沒有很完整,想要用的人還是必須多多參考 Caddy2 網站或 Caddy wiki 的文件。