
Twilio Functions で CORS を正しく設定する方法:筆者が体験した失敗例と成功例
はじめに
Twilio Functions は、API サーバーを素早く構築できる便利な仕組みですが、Web クライアントと連携する際には CORS(Cross-Origin Resource Sharing) への対応が欠かせません。たとえば、別オリジンのフロントエンドから fetch()
を使ってトークン発行エンドポイントなどを呼び出す場合、適切な CORS ヘッダーがレスポンスに含まれていないと、ブラウザによってリクエストがブロックされてしまいます。
本記事では、Twilio Functions における CORS 対応の具体的な実装方法を紹介するとともに、筆者が実際に遭遇した「うまくいかなかった例」と「うまくいった例」を比較しながら、CORS 対応のポイントを整理します。特に Next.js や v0 などフロントエンドの実装と組み合わせて使う方にとって参考になる内容を目指しています。
本記事で扱う内容
- CORS の基本的な仕組みと Twilio Functions で必要となる理由
- CORS 対応において陥りやすい実装ミスとその回避方法
Twilio.Response
を使った正しいヘッダー設定方法
対象読者
- Twilio Functions を使って Web クライアントと連携する API を構築したい方
- Twilio Conversations や Flex Plugin などの SDK から Function を呼び出そうとして CORS に悩まされたことのある方
参考
CORSとは何か、なぜ必要か
CORS(Cross-Origin Resource Sharing)は、異なるオリジン間での HTTP リクエストをブラウザが制限・制御するための仕組みです。ここでいう「オリジン」とは、スキーム(http/https)、ホスト名、ポート番号の組み合わせを指します。たとえば https://example.com
と https://api.example.com
は、同じドメインに見えても異なるオリジンと見なされます。
JavaScript を使ってフロントエンドから外部の API を呼び出す場合、もし CORS の仕組みがなければ、任意の Web サイトがユーザーの認証情報を持ったまま外部サーバーへリクエストを送り、意図しない情報の取得や操作が可能になってしまいます。CORS はこうしたクロスサイト攻撃を防ぐために設けられた、安全のためのルールです。
一方で、モダンな Web 開発においては、フロントエンドとバックエンドが異なるドメインやサーバーにホストされることが一般的です。たとえば、フロントエンドが http://localhost:3000
にあり、バックエンドの Twilio Function が https://xxxxx.twil.io
にあるような構成では、正当なアクセスであっても CORS による制限に引っかかってしまう場合があります。
そのため、バックエンド側で明示的に「どのオリジンからのアクセスを許可するか」をヘッダーとして返すことが必要になります。Twilio Functions を利用する場合も例外ではなく、CORS に対応しない限り、ブラウザ経由の API 通信がブロックされ、開発・運用に支障をきたすことになります。
CORS 対応 Function の実装例
このセクションでは、Twilio Functions における CORS 対応に関して、筆者が実際に検証した「うまくいかなかった例」と「うまくいった例」を紹介し、それぞれのポイントや落とし穴について解説します。
Twilio.Response
を使用しないケース
うまくいかなかった例 1:以下は、JWT トークンを返すための Function を定義したつもりの実装例です。
exports.handler = async function(context, event, callback) {
const token = createAccessTokenSomehow(); // 仮のトークン生成関数
const response = {
token: token,
};
callback(null, response); // ← Twilio.Response を使っていない
};
このコードでは、Twilio のレスポンスオブジェクト Twilio.Response
を使用せず、単にプレーンなオブジェクトを callback
で返しています。このような実装の場合、HTTP レスポンスヘッダーが自動的には付与されず、Access-Control-Allow-Origin
を含む CORS ヘッダーが設定されないため、ブラウザ側で以下のようなエラーが発生します。
Access to fetch at 'https://****.twil.io/token' from origin 'null' has been blocked by CORS policy:
No 'Access-Control-Allow-Origin' header is present on the requested resource.
このようなエラーは、ローカル HTML ファイルや異なるオリジンから fetch()
を用いて Function にアクセスした場合に一般的に発生します。
Content-Type
を指定していないケース
うまくいかなかった例 2:次に紹介するのは、Twilio.Response
を使用し、CORS に必要なヘッダーも設定しているにもかかわらず、依然としてエラーが発生した実装例です。
exports.handler = async function(context, event, callback) {
const response = new Twilio.Response();
response.appendHeader('Access-Control-Allow-Origin', '*');
response.appendHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
response.appendHeader('Access-Control-Allow-Headers', 'Content-Type');
const token = createAccessTokenSomehow(); // 仮のトークン生成関数
response.setBody({ token: token });
callback(null, response);
};
この実装では、CORS に関するヘッダーは適切に設定されていますが、Content-Type
ヘッダーが明示的に追加されていません。その結果、一部のブラウザやクライアント環境において、レスポンスが不正と見なされ、以下のようなエラーが出力されました。
Access to fetch at 'https://****.twil.io/token' from origin 'null' has been blocked by CORS policy:
No 'Access-Control-Allow-Origin' header is present on the requested resource.
このエラーは一見 Access-Control-Allow-Origin
に関するものに見えますが、実際には レスポンス全体の形式が正しく解釈されていないこと に起因しており、Content-Type
の明示がない場合、内部的にヘッダーが破棄される・無視されるケースがあることが原因です。
Content-Type
を含む正しいレスポンスの構築
うまくいった例:以下は、CORS 対応に成功した Function の実装例です。
exports.handler = async function(context, event, callback) {
const response = new Twilio.Response();
response.appendHeader('Content-Type', 'application/json');
response.appendHeader('Access-Control-Allow-Origin', '*');
response.appendHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
response.appendHeader('Access-Control-Allow-Headers', 'Content-Type');
const token = createAccessTokenSomehow(); // 仮のトークン生成関数
response.setBody({ token: token });
callback(null, response);
};
このコードでは、Twilio.Response
を用いてレスポンスを構築し、CORS に必要な各種ヘッダーに加えて、Content-Type: application/json
を明示的に追加しています。これにより、ブラウザはレスポンスを正しく解釈し、クロスオリジン通信も問題なく完了することが確認できました。
このように、CORS の対応では「何を返すか」に加えて、「どのように返すか」も重要なポイントとなります。特に Twilio Functions においては、標準のレスポンス形式でヘッダーが適切に処理されないケースがあるため、Twilio.Response
の利用と適切なヘッダー設定は必須と考えておくべきです。
まとめ
本記事では、Twilio Functions における CORS(Cross-Origin Resource Sharing)対応について、実際に遭遇した成功例・失敗例を交えながら解説しました。
特に重要なのは、Twilio Functions では CORS 対応を自動で行ってくれるわけではないという点です。Twilio.Response
オブジェクトを明示的に使用し、Access-Control-Allow-Origin
をはじめとする各種 CORS ヘッダーを手動で追加する必要があります。さらに、Content-Type
の指定漏れがレスポンス全体の無効化につながるなど、見落としやすい落とし穴も存在します。
Twilio.Response
を必ず使用するAccess-Control-Allow-Origin
を明示的に設定する(本番ではオリジンを限定)Access-Control-Allow-Methods
とAccess-Control-Allow-Headers
を必要に応じて追加するContent-Type
(例:application/json
)を明示的に指定する
CORS エラーは一見すると原因が分かりにくく、開発中のストレス要因になりがちです。しかし、構造的に正しく設計・対応すれば、安定した API 通信が可能になります。Twilio Functions をフロントエンドと安全に連携させるための第一歩として、本記事の内容が役立てば幸いです。