Cloudflare Workers の無料プランで動く URL 短縮サービスを Terraform で管理する

Cloudflare Workers の無料プランだけで動作する小さな URL 短縮サービスをデプロイする Terraform モジュール terraform-cloudflare-url-shortener を公開しました。 短縮リンクは Terraform の map として宣言的に定義され、Worker スクリプトに直接埋め込まれます。 KV namespace もデータベースも、有料機能も一切不要です。
モジュールは Terraform Registry に ngs/url-shortener/cloudflare として公開しています。
モチベーション
廃止された Google Short Links のような仕組み
—— 自分のドメイン上で、短く覚えやすいスラッグから長い URL へリダイレクトする
go/ リンク —— が欲しかったのがきっかけです。
この用途に対して、セルフホスト型の URL 短縮サービスの多くはオーバースペックです。 月に数回しか変更されない小さなキーバリューの表を管理するためだけに、 管理画面、データベース、認証機能が付いてきます。「サーバーレス」を謳うものでも、 たいてい Workers KV や D1 データベースを要求し、プロビジョニングすべきステートと バックアップ対象が増えてしまいます。
しかし、リンクがどのみち Terraform の設定に書かれるのであれば、データストアは不要になります。 リンクの一覧は Worker スクリプトの中に丸ごと収まってしまうので、次のような利点が生まれます。
- リンクがコードになる。 リンクの追加はプルリクエストです。他のコードと同じように
レビューされ、バージョン管理され、
git logから差し戻せます。 - ランタイム依存ゼロ。 KV の読み取りもデータベースへの問い合わせも発生しません。 すべてのリクエストはエッジ上のメモリにある定数オブジェクトから応答されます。
- 無料。 Workers スクリプト、ルート、プロキシされた DNS レコードはすべて Cloudflare の無料プランで利用できます。
仕組み
モジュールが管理するリソースはたった 3 つです。
- Workers スクリプト —— テンプレートからレンダリングされ、
linksmap が JSON として埋め込まれます。 - ホスト名に対するプロキシされたプレースホルダー DNS レコード(
AAAA 100::)—— これによりトラフィックが Cloudflare のエッジに到達します。100::は IPv6 の discard prefix で、すべてのリクエストを Worker が処理するため、実在する オリジンを指す必要がありません。 - Workers ルート(
<hostname>/*)—— ホスト名へのすべてのリクエストを Worker に送ります。
リクエストの処理は意図的にシンプルにしています。
| リクエスト | レスポンス |
|---|---|
/<既知のスラッグ> | 対応する URL へ redirect_status(デフォルト 301)でリダイレクト |
/ | root_url へ 302、root_url が null の場合は 404 |
| それ以外 | 404 |
細かいですが重要な点として、スラッグの検索には Object.hasOwn を使っているため、
constructor や __proto__ といったプロトタイプのプロパティ名がリンクとして
解決されることはありません。
リンクの更新は terraform apply を実行するだけです。Terraform がスクリプトの
テンプレートを再レンダリングして新しい Worker をアップロードし、数秒で世界中に
反映されます。
使い方
module "url_shortener" {
source = "ngs/url-shortener/cloudflare"
version = "~> 0.1"
account_id = var.cloudflare_account_id
zone_id = var.cloudflare_zone_id
hostname = "s.example.com"
root_url = "https://example.com/"
links = {
gh = "https://github.com/ngs"
resume = "https://example.com/resume.pdf"
"a.txt" = "https://example.com/files/a.txt"
}
}設定はこれだけです。これで https://s.example.com/gh が GitHub プロフィールへ
リダイレクトされ、ルートパスへのアクセスは root_url にフォールバックします。
プロバイダーが使用する Cloudflare API トークンには、対象ゾーンの Workers スクリプト、 Workers ルート、DNS レコードを管理する権限が必要です。モジュールは Terraform >= 1.0 と cloudflare プロバイダー ~> 5 を要求します。
いくつかのオプションも用意しています。
redirect_status—— デフォルトの301から302、307、308に変更できます (バリデーション付きなので、タイプミスは plan の時点で失敗します)。script_name—— Workers スクリプト名を上書きします。デフォルトはホスト名の ドットをダッシュに置き換えたものです。root_url——nullのままにすると、リダイレクトせずルートで404を返します。
認証情報なしでテストできる
モジュールには 2 つのテストスイートが付属しており、どちらも Cloudflare の アカウントを必要としません。
# Terraform ネイティブテスト(モックプロバイダー、plan のみ)
terraform init -backend=false
terraform test
# Worker のユニットテスト(依存なし、Node.js >= 18)
node --test tests/worker.test.mjsTerraform のテストは Terraform 1.7 で追加されたモックプロバイダーのサポートを使い、
plan の時点でリソースの構成を検証します。Worker のロジックは、テストフレームワークの
インストールが不要な素の node --test でカバーしています。
ソースコードは MIT ライセンスで GitHub に 公開しています。Issue やプルリクエストをお待ちしています。