Guide · AWS CloudFront
MCP Server CloudFront Origin Shield — regional caching tier for MCP tool result delivery
CloudFront has over 600 edge locations globally; without Origin Shield, a cache miss at any one of them reaches your MCP server origin directly. During a traffic spike — after a viral demo, a new agent integration, or a repeated-tool-call from an agent loop — hundreds of simultaneous cache misses from different edges can overwhelm a small origin. Origin Shield adds a regional intermediate cache tier: all CloudFront edges in a region forward misses to the regional shield instead of the origin. Only the shield itself hits the origin on a miss, collapsing potentially hundreds of origin requests into one. For cacheable MCP tool responses (status checks, schema snapshots, historical data), this dramatically reduces origin traffic and cost while improving cache hit rates.
TL;DR
Enable Origin Shield on the S3 or ALB origin in your CloudFront distribution. Choose the shield region closest to your origin (not closest to your users — the edges handle user proximity). Cache hit rate improvement is most dramatic when your origin is single-region and your CloudFront distribution serves global traffic. For MCP tool results, pair Origin Shield with a custom cache policy that includes only the query parameters and headers that actually change the response — a narrow cache key maximizes collapsing. Origin Shield adds one network hop but reduces origin connections from O(edges) to O(1) per unique cache key during a traffic burst.
Enabling Origin Shield: region selection and configuration
Origin Shield is configured per-origin in the CloudFront distribution. It does not change the distribution's edge network or user-facing latency — it only affects the path from CloudFront edges to the origin. Choose the shield region that minimizes latency to your origin, not your users.
# CloudFormation snippet for enabling Origin Shield on an ALB origin
# (AWS::CloudFront::Distribution Origins entry)
Origins:
- Id: mcp-server-alb
DomainName: !GetAtt McpServerALB.DNSName
CustomOriginConfig:
HTTPSPort: 443
OriginProtocolPolicy: https-only
OriginSSLProtocols: [TLSv1.2]
OriginShield:
Enabled: true
# Choose the region closest to your ALB/origin — not to your users
# Available regions: us-east-1, us-east-2, us-west-2, ap-south-1,
# ap-northeast-1, ap-northeast-2, ap-southeast-1, ap-southeast-2,
# eu-central-1, eu-west-1, eu-west-2, sa-east-1
OriginShieldRegion: us-east-1 # if your ALB is in us-east-1
# CLI equivalent:
aws cloudfront update-distribution --id EDFDVBD6EXAMPLE \
--distribution-config file://dist-config.json
# In dist-config.json, add OriginShield: { Enabled: true, OriginShieldRegion: "us-east-1" }
# to the relevant Origin
Origin Shield is billed as an additional cache tier — you pay a small per-request fee for requests that hit the shield layer. The cost is negligible compared to the reduction in origin compute costs for cacheable tool responses. Origin Shield is not available in all regions — check the current list in the CloudFront documentation before configuring IaC.
Cache hit rate math: why shield collapsing works
Without Origin Shield, each of the ~600 CloudFront edge POPs maintains its own cache. A cache miss at edge A hits the origin, and a simultaneous miss at edge B for the same URL also hits the origin independently. During a traffic spike where 50 edges simultaneously lose a cached key (due to TTL expiry or a cold start), the origin receives 50 concurrent requests for the same object.
With Origin Shield, all edges in a region forward misses to the regional shield. The shield has one cache entry for each object. During the same 50-edge spike, only the shield's one request reaches the origin — the shield serves the other 49 from its regional cache (or coalesces them into one origin request if the shield is also cold).
// Cache key narrowing: what Origin Shield coalesces
// BAD: Wide cache key — includes all query params
// URL: /api/mcp/status?serverId=abc&requestId=xyz&nonce=123
// requestId and nonce are different on every request — no collapsing possible
// Origin Shield sees every request as unique → cache hit rate near 0%
// GOOD: Narrow cache key — only params that change the response
// Cache policy: forward only "serverId" to cache key; strip requestId and nonce
// URL normalized to: /api/mcp/status?serverId=abc
// All callers querying the same server get the same cached response
// CloudFront cache policy configuration (Terraform):
resource "aws_cloudfront_cache_policy" "mcp_status" {
name = "mcp-server-status-cache"
default_ttl = 60 // 60s — MCP server status updates every 60s in AliveMCP
max_ttl = 300
min_ttl = 0
parameters_in_cache_key_and_forwarded_to_origin {
cookies_config {
cookie_behavior = "none" // don't vary cache by cookie
}
headers_config {
header_behavior = "none" // don't vary by Accept-Language etc.
}
query_strings_config {
query_string_behavior = "whitelist"
query_strings {
items = ["serverId"] // ONLY this param affects the response
}
}
enable_accept_encoding_gzip = true
enable_accept_encoding_brotli = true
}
}
// Cache behavior attaching this policy to the /api/mcp/status/* path:
ordered_cache_behavior {
path_pattern = "/api/mcp/status/*"
cache_policy_id = aws_cloudfront_cache_policy.mcp_status.id
allowed_methods = ["GET", "HEAD"]
cached_methods = ["GET", "HEAD"]
target_origin_id = "mcp-server-alb"
viewer_protocol_policy = "https-only"
compress = true
}
TTL strategy for MCP tool results
Not all MCP tool responses are cacheable — tool calls that mutate state or return user-specific data must bypass the cache entirely. For cacheable responses, choose a TTL that balances freshness with origin load reduction.
// TTL recommendations by MCP response type:
// 1. Public MCP server status (AliveMCP's core use case)
// - Freshness requirement: 60s (ping interval)
// - TTL: 60s default, 300s max
// - Cache-Control from origin: "public, max-age=60, s-maxage=60"
// 2. MCP server schema snapshot
// - Freshness requirement: changes rarely; bust on publish
// - TTL: 3600s with cache invalidation on schema change
// - Cache-Control from origin: "public, max-age=3600, s-maxage=3600"
// - Invalidation: POST /2020-11-20/distributions/{id}/invalidations
// { Paths: { Quantity: 1, Items: ["/api/schema/SERVER_SLUG"] } }
// 3. Historical uptime data (30-day chart data)
// - Freshness requirement: acceptable to be 5 min stale
// - TTL: 300s
// - Cache-Control from origin: "public, max-age=300, s-maxage=300"
// 4. Real-time SSE stream (/sse/* endpoints)
// - NEVER cacheable — must bypass CloudFront entirely
// - Cache-Control from origin: "no-store"
// - Cache behavior: AllowedMethods = ["GET"], but set TTL min/max/default all to 0
// Origin-side Cache-Control header setting in Express:
app.get("/api/mcp/status/:slug", async (req, res) => {
const status = await getMcpServerStatus(req.params.slug);
res.set("Cache-Control", "public, max-age=60, s-maxage=60");
res.set("Surrogate-Key", `mcp-status:${req.params.slug}`); // for targeted invalidation
res.json(status);
});
Origin Shield and health check failover
Origin Shield does not change how CloudFront detects origin health failures. If the origin returns 5xx errors, CloudFront's normal error caching and failover behavior applies — Origin Shield is simply another hop in the path to the origin. What Origin Shield does change is the volume of health-check probes: with Origin Shield enabled, CloudFront's health checks reach the origin through the shield region, not directly from all edges, reducing the number of probe connections the origin must handle.
// When origin returns 5xx: error caching with Origin Shield
// CloudFront caches 5xx responses for ErrorCachingMinTTL (default 10s)
// During this window, the shield serves the cached error to all edges
// This means a short origin outage affects all global users until ErrorCachingMinTTL expires
// Recommended: lower ErrorCachingMinTTL for MCP status endpoints
// CloudFormation:
CustomErrorResponses:
- ErrorCode: 502
ErrorCachingMinTTL: 5 // cache origin errors for only 5s (default is 10s)
ResponseCode: 502
ResponsePagePath: /error/502.json
- ErrorCode: 503
ErrorCachingMinTTL: 5
ResponseCode: 503
ResponsePagePath: /error/503.json
// For multi-origin failover (Origin Group):
// CloudFront origin groups allow automatic failover to a secondary origin
// (e.g., secondary ALB or S3 fallback) if the primary returns 5xx
// Origin Shield can be enabled independently on each origin in the group
OriginGroups:
Quantity: 1
Items:
- Id: mcp-server-group
FailoverCriteria:
StatusCodes:
Quantity: 3
Items: [500, 502, 503]
Members:
Quantity: 2
Items:
- OriginId: mcp-server-primary-alb # Origin Shield: us-east-1
- OriginId: mcp-server-fallback-s3 # Origin Shield: us-east-1
Failure modes reference
| Failure | Symptom | Fix |
|---|---|---|
| Wrong shield region (far from origin) | Origin Shield adds latency instead of reducing it; cache hit rate unchanged | Choose shield region closest to origin, not users; run latency tests with/without shield enabled |
| Wide cache key defeats collapsing | Origin Shield doesn't reduce origin requests; each request has unique nonce/requestId in cache key | Create a narrow cache policy that includes only params that change the response; strip per-request IDs at the cache behavior level |
| SSE path included in Origin Shield cache behavior | CloudFront buffers streaming responses, breaking SSE; clients time out waiting for first event | Create a separate cache behavior for /sse/* with TTL=0 and no-caching; Origin Shield only helps with cacheable paths |
| Cache invalidation targets wrong distribution | Invalidation succeeds but stale content persists; shield's cache is not affected by edge-only invalidation | CloudFront invalidations propagate through Origin Shield automatically — use standard invalidation API |
| ErrorCachingMinTTL too high with shield | Short origin outage (10–15s) causes 5xx to be cached and served globally for 10s through shield | Set ErrorCachingMinTTL to 5s for MCP status paths; use custom error response pages for graceful degradation |