はじめに
本記事は any Advent Calendar #2 「マルチテナントSaaSにおけるエンジニアリング大全」 Day14 の記事です。 弊社anyのアドベントカレンダーをひとつ丸ごと占有して、ひとりアドベントカレンダーとして、筆者の「マルチテナントSaaSのエンジニアリング」への経験をすべてアウトプットしていくカレンダーです。
これまでデータストアの分離やID管理などについて解説してきましたが、マルチテナントSaaSを実装する上で避けて通れない重要なトピックが 「テナント解決」の技術 です💪
テナント解決とは
テナント解決は、HTTPリクエストやAPI呼び出しから「どのテナントに対する操作か」を特定するプロセスです。これはマルチテナントアーキテクチャにおける最初のゲートキーパーとして機能します。
例えば、ユーザーが https://example.com/dashboard にアクセスした場合、アプリケーションはこのリクエストがどのテナント(企業)のダッシュボードを表示すべきかを判断する必要があります。この判断を誤ると、別のテナントのデータが表示されてしまう深刻なセキュリティ問題につながります。
テナント解決の実装方式は大きく分けて以下のパターンが存在します。これらはそれぞれ異なるユースケースに適しており、「ブラウザとバックエンド通信」「バックエンド間通信」といった通信の種類にも左右されるでしょう。
主要なテナント解決戦略
1. ホスト戦略(Host Strategy / サブドメイン方式)
ホスト戦略は、サブドメインを利用してテナントを識別する方式です。例えば、a.example.comやb.example.comのように、テナントごとに専用のサブドメインを割り当てます。
この方式は、B2B SaaSにおいて最も広く採用されているパターンの一つです。ユーザーにとっても直感的で、「自社専用のURL」という印象を与えやすいです。実装面では、DNSレベルでのワイルドカード設定が必要になります。*.example.comのようなワイルドカードレコードを設定し、すべてのサブドメインを同一のアプリケーションサーバーに向けます。
セキュリティ面では、サブドメインレベルでの分離により、クッキーのスコープをテナントごとに制限できるメリット があります。また、将来的にテナントごとに異なるインフラリソースを割り当てる(サイロモデルへの移行)場合も、DNSレベルでルーティングを変更するだけで対応できる柔軟性があります。
弊社のプロダクトであるQastもWebフロントエンドとバックエンドの通信ではこの方式を採用しています。
余談にはなりますが、つい先日、Vercelもホストを分割する形でマルチテナント対応を発表し、フルマネージドで構築しやすい環境が少しずつ整ってきました。
2. ベースパス戦略(Base Path Strategy / パス先頭方式)
ベースパス戦略は、URLの最初のパスセグメントでテナントを識別する方式です。例えば、example.com/tenant-a/dashboardやexample.com/tenant-b/settingsのように、パスの先頭にテナント識別子を配置します。
この方式の最大の利点は、サブドメインやDNS設定が不要な点です。単一ドメインで運用できるため、SSL証明書の管理も簡素化され、初期の実装コストを抑えられます。特にスタートアップや初期フェーズのプロダクトにおいては、技術的な複雑性を避けつつマルチテナント機能を実現できる選択肢として有効になります。
一方で、アプリケーション内の全てのリンクやリダイレクトにテナント識別子を含める必要があるため、相対パスの扱いが複雑になります。
3. ルート戦略(Route Strategy / ルートパラメータ方式)
ルート戦略は、アプリケーションのルーティング設定にテナント識別子をパラメータとして組み込む方式です。例えば、/tenants/{tenant-a}/usersのようなルート定義を行い、リクエストパスから動的にテナントIDを抽出します。
この方式は、RESTful APIの設計において自然な形でテナント識別を実現できます。フレームワークのルーティング機能を活用することで、テナント識別のロジックをミドルウェアとして統一的に実装できる点も魅力です!
4. ヘッダー戦略(Header Strategy / カスタムヘッダー方式)
ヘッダー戦略は、HTTPリクエストのカスタムヘッダーにテナント識別子を含める方式です。例えば、X-Tenant-ID: tenant-aのようなヘッダーをリクエストに付与します。
この方式は、API中心のアーキテクチャやマイクロサービス環境において特に有効です。URLにテナント情報を含める必要がないため、エンドポイント設計がシンプルに保たれます。また、API Gatewayやリバースプロキシでヘッダーを付与・検証することで、アプリケーション層での実装を簡素化できます。
ただし、ブラウザベースのアプリケーションでは、JavaScriptからカスタムヘッダーを設定する必要があるため、実装が煩雑になる可能性があります。また、ヘッダーの検証が適切に行われないと、クライアント側で任意のテナントIDを指定できてしまうセキュリティリスクがあるため、認証・認可との連携が不可欠です。
5. クレーム戦略(Claim Strategy / 認証トークン方式)
クレーム戦略は、認証トークン(JWTなど)に含まれるクレーム(属性情報)からテナントを識別する方式です。ユーザーがログインした際に発行されるトークンに、所属するテナントの情報を埋め込んでおきます。
この方式の最大の強みは、認証とテナント解決が統合される点です。ユーザーの認証情報にテナント識別子が含まれているため、リクエストごとに別途テナント情報を付与する必要がありません。特にOIDC(OpenID Connect)やSAMLなどの標準的な認証プロトコルを使用している場合、IdP(Identity Provider)側でテナント情報をクレームとして提供することで、アプリケーション側の実装を簡素化できます。
セキュリティ面でも、トークンに署名が付与されているため、クライアント側でテナント情報を改ざんできないという利点があります。また、マルチテナント環境において、ユーザーが複数のテナントに所属するケースにも柔軟に対応できます。
6. セッション戦略(Session Strategy / サーバーサイドセッション方式)
セッション戦略は、サーバーサイドのセッションストアにテナント識別子を保存し、セッションIDから取得する方式です。ユーザーがログインした際にセッションにテナント情報を格納し、以降のリクエストではセッションから取得します。従来型のWebアプリケーションにおいては一般的でしたね。
ステートフルな実装になるため、スケーラビリティに課題があります。複数のアプリケーションサーバーを運用する場合、セッションストアを共有する仕組み(RedisやDynamoDBなど)が必要になります。
まとめ
テナント解決は、マルチテナントSaaSの基盤となる地味ながら重要な選択になります。それぞれの戦略には一長一短があり、また状況に応じては、これらを組み合わせることも必要になるため、ぜひアプリケーションの設計の参考にしてみてください💪

