はじめに
Cloudflare Workersは、エッジに分散配置された軽量なサーバーレス環境として、多くの開発者に利用されています。リクエスト処理をエッジ側で完結させることで、レイテンシ削減やスケーラビリティ向上を実現できる点は大きな魅力です。ところが、外部のAPIと連携しようとした際に、思わぬ落とし穴に遭遇する場合があります。
特にDeepL APIとの連携において「Error 525: SSL handshake failed」というエラーが発生する事例は、一定数の開発者を悩ませています。ローカル環境では正常に動作するにもかかわらず、Cloudflare Workersにデプロイした途端に失敗する現象は不可解に見えます。本稿では、このエラーの背景を丁寧に整理し、実運用における現実的な回避策を提示します。
Error 525とは何か
Error 525は、CloudflareにおけるSSLハンドシェイクの失敗を意味します。一般的には、Cloudflareがオリジンサーバーに接続しようとした際にTLS/SSLのネゴシエーションが成立しなかった場合に返されます。しかし、今回のケースでは「オリジン=DeepL API」という位置づけになるため、WorkersからDeepLへの直接的なTLS接続が失敗していることを意味します。
通常のWebブラウザやNode.js環境で同じコードを実行すると問題なくDeepLと通信できます。したがって、問題はCloudflare Workersの実行環境におけるTLS実装の仕様と、DeepL APIが期待する暗号スイートや証明書設定の間に不整合があると推測されます。
発生するコード例
以下のように、Cloudflare Workers内でDeepL APIにリクエストを送信すると525エラーが発生します。
export default {
async fetch(request, env) {
const response = await fetch('https://api-free.deepl.com/v2/translate', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Authorization': `DeepL-Auth-Key ${env.DEEPL_API_KEY}`,
},
body: new URLSearchParams({
text: 'Hello, world!',
target_lang: 'DE',
}),
});
if (!response.ok) {
return new Response(`DeepL API error: ${response.status} ${response.statusText}`);
}
return response;
}
}
このコードはNode.js環境では正常に翻訳結果を返します。しかし、Workers上で実行すると必ず525エラーとなり、レスポンスが返ってきません。
原因の考察
なぜこの問題が発生するのでしょうか。公開されている情報やGitHubのIssue報告をもとにすると、以下の要因が関与している可能性があります。
- TLSバージョンの不一致: Workers(workerd)の制約により、DeepL側が期待する暗号スイート/設定と不整合を起こす可能性。
- SNIの取り扱い差: エッジランタイム特有のSNI拡張挙動差により、証明書検証が失敗するケース。
- 証明書チェーン検証: ルート証明書ストア差異により、DeepLの証明書検証が失敗する可能性。
これらのいずれもCloudflare側で改善されるべき性質の問題であり、ユーザー側が直接制御することはできません。類似の報告が複数挙がっており、現時点では「既知の未解決問題」と見なされています。
解決策
現時点での実運用における解決策は、大きく分けて2つです。
1. 中継サーバーを利用する
Workersから直接DeepL APIに接続する代わりに、中継サーバーを用意して間接的にリクエストを転送する方法です。中継サーバーは一般的なNode.js環境や他のクラウド基盤で稼働させれば、DeepLとの通信は正常に行えます。
以下はExpressで実装した簡易的な例です。
import express from 'express';
import fetch from 'node-fetch';
const app = express();
app.use(express.json());
app.post('/translate', async (req, res) => {
const deeplRes = await fetch('https://api-free.deepl.com/v2/translate', {
method: 'POST',
headers: {
'Authorization': `DeepL-Auth-Key ${process.env.DEEPL_API_KEY}`,
'Content-Type': 'application/x-www-form-urlencoded',
},
body: new URLSearchParams(req.body),
});
const data = await deeplRes.json();
res.json(data);
});
app.listen(3000, () => console.log('Proxy server running on port 3000'));
Workersからはこの中継サーバーにリクエストを送ることで、間接的にDeepL APIを利用できます。セキュリティ上の観点から、認証やCORS制御を必ず導入すべきです。
2. 代替のサーバーレス環境を利用する
Workers特有の問題を避けるため、AWS LambdaやGoogle Cloud Functionsなど、別のサーバーレス基盤に処理を移すことも選択肢となります。これらの環境では一般的なTLSライブラリを利用しているため、DeepLとの接続に失敗することはほぼありません。
特に既存のバックエンド基盤をすでに運用している場合は、翻訳処理のみをそちらにオフロードする設計が合理的です。
運用上の留意点
- コストの増加: 中継に伴いレイテンシや運用コストが増加。利用頻度とコストのバランス検討が必要。
- セキュリティ対策: APIキーは中継側に置くため、認証・レート制限・監査ログ等のアクセス制御が必須。
- 将来的な改善期待: WorkersのTLS実装改善により直接DeepLと通信できる可能性。公式情報やIssue更新をウォッチ。
まとめ
Cloudflare WorkersとDeepL APIの組み合わせにおける「Error 525: SSL handshake failed」は、現在も未解決の技術的制約に由来する既知の問題です。根本的な解決はCloudflare側の改善を待つほかありませんが、中継サーバーを用いるか、代替のサーバーレス環境に処理を移すことで回避可能です。なるほど、というよりは「よくある落とし穴」と整理しておくのが適切でしょう。
エッジ環境の利用は強力ですが、暗号化通信の仕様差異が予期せぬ障害を生むこともあります。設計段階でこれらの制約を踏まえ、柔軟に回避策を組み込むことが健全な運用につながります。
参考文献
- Cloudflare Docs: Error 525(SSL handshake failed) https://developers.cloudflare.com/support/troubleshooting/http-status-codes/cloudflare-5xx-errors/error-525/
- DeepL API Documentation https://developers.deepl.com/docs
- cloudflare/workerd Issue #776 https://github.com/cloudflare/workerd/issues/776
- Vercel Edge Runtime – Issues https://github.com/vercel/edge-runtime/issues