エッジコンピューティング×フロントエンド:Cloudflare Workers/Vercel Edgeでサイトを驚異的に高速化
前端工程(更新: 2026年6月2日)
なぜフロントエンドにエッジコンピューティングが必要か?
従来のアーキテクチャでは、ユーザーリクエストはインターネットの半分を横断してオリジンサーバーに到達します:
ユーザー(東京)→ CDNノード(東京、静的キャッシュのみ)→ オリジン(米西海岸、動的計算)
遅延:~10ms ~150ms 太平洋横断 ~50ms 計算
合計:~210ms
エッジコンピューティングは計算ロジックをユーザーに最も近いノードにプッシュします:
ユーザー(東京)→ エッジノード(東京、直接計算)
遅延:~10ms ~5ms ローカル計算
合計:~15ms
14倍の遅延削減—これがエッジコンピューティングの力です。
三大エッジプラットフォームの比較
| 次元 | Cloudflare Workers | Vercel Edge Functions | Deno Deploy |
|---|---|---|---|
| ランタイム | V8 Isolate | V8 Isolate | Deno |
| コールドスタート | <5ms | <5ms | ~20ms |
| グローバルノード | 300+都市 | 30+リージョン | 35+リージョン |
| 無料枠 | 10万リクエスト/日 | プロジェクトごと課金 | 100万リクエスト/月 |
| 言語 | JS/TS/WASM | JS/TS | JS/TS/WASM |
| バンドルサイズ制限 | 10MB | 4MB | 無制限 |
| KVストレージ | Workers KV | Edge Config | Deno KV |
| フレームワーク統合 | フレームワーク非依存 | Next.js深層統合 | Fresh |
実践シナリオ1:Geoルーティング
ユーザーの地理的位置に基づいて異なるコンテンツにルーティング、ゼロ遅延:
// Cloudflare Workers
export default {
async fetch(request: Request, env: Env, ctx: ExecutionContext) {
const country = request.cf?.country as string;
const routes: Record<string, string> = {
CN: 'https://cn.example.com',
JP: 'https://jp.example.com',
US: 'https://us.example.com',
EU: 'https://eu.example.com',
};
const target = routes[country] || routes['US'];
return Response.redirect(target, 302);
},
};
Vercel Edge版
// middleware.ts (Next.js)
import { NextRequest, NextResponse } from 'next/server';
export function middleware(request: NextRequest) {
const country = request.geo?.country || 'US';
const localeMap: Record<string, string> = {
CN: '/zh-CN',
TW: '/zh-TW',
JP: '/ja',
};
const locale = localeMap[country] || '/en';
if (!request.nextUrl.pathname.startsWith(locale)) {
return NextResponse.redirect(new URL(locale, request.url));
}
return NextResponse.next();
}
export const config = {
matcher: ['/((?!api|_next/static).*)'],
};
実践シナリオ2:A/Bテスト
クライアントJS不要、エッジ層で直接トラフィックを振り分け:
export default {
async fetch(request: Request, env: Env) {
const url = new URL(request.url);
// 実験グループの読み取りまたは割り当て
let variant = getCookie(request, 'ab-variant');
if (!variant) {
variant = Math.random() < 0.5 ? 'control' : 'experiment';
}
// KVから対応バージョンのHTMLを読み取り
const html = await env.KV.get(`landing:${variant}`);
const response = new Response(html, {
headers: { 'Content-Type': 'text/html' },
});
// グループ一貫性を維持するCookieを設定
response.headers.append(
'Set-Cookie',
`ab-variant=${variant}; Path=/; Max-Age=86400`
);
return response;
},
};
function getCookie(request: Request, name: string): string | null {
const cookies = request.headers.get('Cookie') || '';
const match = cookies.match(new RegExp(`${name}=([^;]+)`));
return match ? match[1] : null;
}
利点比較
| 方式 | ファーストビューのちらつき | パフォーマンス影響 | SEO対応 | 実装複雑度 |
|---|---|---|---|---|
| クライアントJS振り分け | ❌ ちらつきあり | LCP低下 | ❌ | 低 |
| サーバーサイド振り分け | ✅ ちらつきなし | 遅延増加 | ✅ | 中 |
| エッジ振り分け | ✅ ちらつきなし | ほぼ影響なし | ✅ | 中 |
実践シナリオ3:パーソナライズドコンテンツ
export default {
async fetch(request: Request, env: Env) {
const country = request.cf?.country;
const timezone = request.cf?.timezone;
// タイムゾーンに基づいて現地時間を表示
const localTime = new Date().toLocaleTimeString('ja-JP', {
timeZone: timezone,
});
// 国に基づいて現地通貨を表示
const currencyMap: Record<string, { symbol: string; rate: number }> = {
CN: { symbol: '¥', rate: 7.2 },
JP: { symbol: '¥', rate: 150 },
US: { symbol: '$', rate: 1 },
};
const currency = currencyMap[country] || currencyMap['US'];
// ベースページを取得しパーソナライズデータを注入
const response = await fetch(request);
const html = await response.text();
const personalized = html
.replace('{{LOCAL_TIME}}', localTime)
.replace('{{CURRENCY_SYMBOL}}', currency.symbol);
return new Response(personalized, {
headers: response.headers,
});
},
};
実践シナリオ4:API集約とキャッシング
エッジ層で複数のAPIを集約し、クライアントリクエストを削減:
export default {
async fetch(request: Request, env: Env, ctx: ExecutionContext) {
const cache = caches.default;
const cacheKey = new Request(request.url);
// 1. エッジキャッシュをチェック
let response = await cache.match(cacheKey);
if (response) return response;
// 2. 複数APIに並列リクエスト
const [user, posts, notifications] = await Promise.all([
fetch('https://api.example.com/user', { headers: request.headers }),
fetch('https://api.example.com/posts?limit=10'),
fetch('https://api.example.com/notifications/unread'),
]);
// 3. レスポンスを集約
const data = {
user: await user.json(),
posts: await posts.json(),
notifications: await notifications.json(),
};
response = new Response(JSON.stringify(data), {
headers: {
'Content-Type': 'application/json',
'Cache-Control': 'public, max-age=60',
},
});
// 4. エッジキャッシュに書き込み
ctx.waitUntil(cache.put(cacheKey, response.clone()));
return response;
},
};
エッジKVストレージ:グローバル低遅延データベース
Cloudflare Workers KV
// 書き込み
await env.KV.put('user:123:preferences', JSON.stringify({
theme: 'dark',
language: 'ja',
}));
// 読み取り(結果整合性、~60sグローバル同期)
const prefs = await env.KV.get('user:123:preferences', 'json');
// リスト
const list = await env.KV.list({ prefix: 'user:123:' });
Vercel Edge Config
import { get } from '@vercel/edge-config';
// 設定を読み取り(グローバル同期、~1s遅延)
const flags = await get('feature-flags');
if (flags?.newHomepage) {
return newHomepage();
}
return oldHomepage();
従来アーキテクチャからの移行
移行パス
フェーズ1:静的リソースをCDNに(1-2日)
→ HTML/JS/CSS/画像 → CDNエッジキャッシュ
→ 動的リクエストはまだオリジンへ
フェーズ2:エッジミドルウェア(1-2週間)
→ リダイレクト、A/Bテスト、Geoルーティング → エッジ
→ ビジネスロジックはまだオリジン
フェーズ3:軽量APIのエッジ化(2-4週間)
→ 読み取り中心のAPI → エッジ + KV
→ 書き込み操作はまだオリジンへ
フェーズ4:重量ロジックのエッジ化(必要に応じて)
→ SSR → エッジSSR
→ ストリーミングレスポンス → エッジストリーミング
注意事項
| 制限 | 説明 | 対策 |
|---|---|---|
| Node.js APIなし | fs、path等なし |
Web APIで代替 |
| 実行時間制限 | Workers: 30ms(無料)/ 30s(有料) | 長いタスクを分割 |
| バンドルサイズ制限 | Workers: 10MB | Tree-shaking、WASM |
| グローバル状態なし | リクエストごとに独立Isolate | KV/D1で永続化 |
| コールドスタートは小さいがゼロではない | V8 Isolate ~5ms | 主要ルートをウォームアップ |
パフォーマンス実測
ToolsKuサイトはCloudflare CDN + 静的エクスポートアーキテクチャを使用:
指標 従来のSSR エッジ静的
TTFB 200-500ms 10-30ms
LCP 1.5-3s 0.3-0.8s
グローバルP50遅延 150ms 15ms
グローバルP99遅延 800ms 50ms
まとめ
エッジコンピューティングはフロントエンドパフォーマンス最適化の究極の武器です—サーバーで50ms節約するのではなく、150msのネットワーク遅延を5msに削減することです。2026年、もし動的ページがまだエッジで実行されていないなら、ユーザーに100ms以上の待ち時間を無駄に浪費させています。Geoルーティング、A/BテストからAPI集約まで、エッジコンピューティングはフロントエンド開発者に「世界300以上の都市にコードをデプロイする」超能力を与えます。
ブラウザローカルツールを無料で試す →
#边缘计算#CDN#Edge Functions#性能优化#Cloudflare Workers