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

FailureSymptomFix
Wrong shield region (far from origin)Origin Shield adds latency instead of reducing it; cache hit rate unchangedChoose shield region closest to origin, not users; run latency tests with/without shield enabled
Wide cache key defeats collapsingOrigin Shield doesn't reduce origin requests; each request has unique nonce/requestId in cache keyCreate 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 behaviorCloudFront buffers streaming responses, breaking SSE; clients time out waiting for first eventCreate a separate cache behavior for /sse/* with TTL=0 and no-caching; Origin Shield only helps with cacheable paths
Cache invalidation targets wrong distributionInvalidation succeeds but stale content persists; shield's cache is not affected by edge-only invalidationCloudFront invalidations propagate through Origin Shield automatically — use standard invalidation API
ErrorCachingMinTTL too high with shieldShort origin outage (10–15s) causes 5xx to be cached and served globally for 10s through shieldSet ErrorCachingMinTTL to 5s for MCP status paths; use custom error response pages for graceful degradation