Picking CloudFront or Global Accelerator at the Edge

February 21, 2029 · 14 min read

Advanced Networking · ANS-C01 · part of The Exam Room

The situation

The app is a mix of:

  • A web app served from an ALB in eu-west-1, behind which an ECS service renders pages. Static assets (JS, CSS, images) are served from the same ALB today.
  • A REST API also on the ALB, handling session-bound requests: authentication, order placement, user-specific data.
  • A realtime gaming component using WebSockets over TCP 443 to a separate NLB, also in eu-west-1. Low-latency-sensitive; typical session is 5-15 minutes of continuous connection.
  • A legacy TCP service on port 8443 used by a thick-client desktop app, again talking to an NLB in eu-west-1. Still supported for a handful of enterprise customers; they have firewalls that allowlist by IP, so the IP cannot change.

The users:

  • Europe, fine.
  • North America, noticeable latency, 100-150 ms round-trip.
  • India / SE Asia, painful, 200-300 ms, some connections timeout during TLS handshake.
  • Australia, worst, 300+ ms, and frequent retries.

Options on the menu:

  • CloudFront in front of the HTTP surfaces (web app, API, static assets).
  • Global Accelerator in front of the NLBs (gaming WebSocket, legacy TCP).
  • Multi-region deployment with Route 53 latency-based routing.
  • Do nothing, accept the latency as the cost of a single origin.

What actually matters

Before comparing services, it’s worth being concrete about what “edge acceleration” actually does.

There are three slices of latency in any cross-region request:

  1. User → AWS edge. Every AWS region has associated edge points of presence (PoPs). Users typically have a PoP within 20-50 ms; the internet path to that PoP is the first hop.
  2. AWS edge → origin region. This is where the two AWS edge services differ sharply. A caching CDN may serve a response straight from the nearest PoP (zero extra latency); on a cache miss it falls back to AWS’s private backbone to fetch from the origin. A pure network accelerator takes the user’s TCP connection at the nearest PoP and tunnels it over the backbone to the origin region. No caching; just network-path substitution.
  3. Origin processing + response back. The origin does its work; the response traverses back through the same path.

A caching edge shines when content is cacheable: the user → edge hop dominates, and the cache cuts out the edge → origin hop entirely for cached responses. A network accelerator shines when content is not cacheable: WebSockets, session-bound API calls, TCP services. It doesn’t cache; it improves the network path.

The second consideration is protocol coverage. Some edge services only handle HTTP and HTTPS; raw TCP, UDP, custom protocols on non-standard ports fall through. Others terminate any TCP or UDP port across the whole range.

The third is IP semantics. A CDN distribution resolves to a rotating set of edge IPs managed by AWS; the consumer CNAMEs to a distribution name. A network accelerator hands out a small set of anycast static IPs that remain stable for the life of the accelerator. When a customer firewall allowlists by IP, that stable set is the whole allowlist forever; a CDN’s rotating IPs cannot satisfy the same requirement.

The fourth is failover. Both shapes do failover, differently. A caching CDN does it at the HTTP layer via a primary/secondary origin list with status-code triggers. A network accelerator does it by health-checking regional endpoint groups and shifting traffic between them when one goes unhealthy.

The fifth is cost model. CDNs bill per-GB egress with regional pricing differences. Network accelerators bill a fixed per-hour charge plus a per-GB premium in exchange for the backbone path.

What we’ll filter on

  1. Caches responses, can edge serve from cache and skip the origin trip?
  2. Handles non-HTTP protocols. TCP, UDP, non-443 ports?
  3. Static IP for firewall allowlisting, fixed IPs the origin doesn’t have?
  4. Regional failover, automatic failover to another region on unhealthy origin?
  5. DDoS absorption at the edge, do we get edge-layer protection for free?

The edge-acceleration landscape

1. CloudFront. HTTP/HTTPS-only edge cache. 450+ PoPs. Origin can be S3, ALB, MediaStore, or any public HTTP server. Handles HTTP request routing, caching by cache-control headers, request and response transformations via CloudFront Functions and Lambda@Edge. Origin Shield adds a mid-tier cache for origin-offload. AWS Shield Standard included; WAF integrates at the distribution level. Ticks caching, HTTP surface; not TCP/UDP; no static IP.

2. Global Accelerator. Anycast IP service. Two static IPv4 addresses (and optionally IPv6 /56 prefix) that BGP-advertise from every AWS edge. A user’s TCP SYN hits the nearest edge; the accelerator forwards the connection over AWS’s private backbone to an endpoint in a registered endpoint group (one per region). Endpoints are ALBs, NLBs, EC2 instances, or Elastic IPs. Traffic dials are per-endpoint-group to shift percentages of traffic between regions. Health checks drive automatic failover between endpoint groups. AWS Shield Standard + Advanced-eligible. Ticks TCP/UDP, static IP, regional failover; does not cache.

3. Multi-region with Route 53 latency routing. Deploy the app in two or three regions; use Route 53 latency-based routing so users resolve to the nearest regional deployment. Lowest latency on the path itself; highest operational cost (the app has to be genuinely multi-region, with replicated data). Failover via Route 53 health checks. No edge caching, no anycast.

4. Do nothing. The null option. For non-latency-sensitive apps with tolerant users, legitimate. For the Sydney users of a realtime gaming component, not.

Side by side

Option Caches responses TCP/UDP beyond HTTP Static IP Regional failover DDoS absorption
CloudFront ✗ (HTTP/S only) Origin failover Shield Standard included
Global Accelerator ✓ (2 x IPv4) Shield Standard included
Multi-region + R53 Per-region ✓ (via R53 HC) Per-region defences
Do nothing n/a Shield Standard at origin

Two edges, two paths, one origin

Users Sydney user realtime WebSocket legacy TCP 8443 Mumbai user web + API realtime New York user web + API cacheable images London user all traffic everything fast Thick-client apps TCP 8443 allowlist by IP CloudFront edge PoPs HTTP/HTTPS caching, 450+ locations PoP Sydney cache hit: local miss: backbone to eu-west-1 PoP Mumbai same shape origin shield: cdg PoP NY cached images, JS, CSS SigV4 / OAI to ALB PoP London cache closest to origin shield @ cdg Global Accelerator edge PoPs anycast 75.2.60.5 and 99.83.89.12 PoP Sydney TCP ingress backbone to origin PoP Mumbai TCP ingress backbone to origin PoP NY no caching just a better path PoP London nearly direct to origin region Origin: eu-west-1 ALB (HTTP/HTTPS) web app + REST API target: ECS service NLB (TCP) WebSocket :443 legacy :8443 endpoint group: eu-west-1 standby: us-east-1 (traffic dial 0%) auto-failover on health check HTTP/S TCP any port
CloudFront handles the cacheable HTTP surface; Global Accelerator handles the WebSocket and legacy TCP. Both use AWS's backbone from the user-nearest PoP to the origin region.

The pick(s) in depth

CloudFront in front of the HTTP surfaces; Global Accelerator in front of the NLB for the WebSocket and legacy TCP. They are complementary, not competitive, and the question is rarely “which one”, it’s “both, for the traffic each fits.”

CloudFront, for web + REST. Set the web app’s origin as the ALB. Configure cache behaviours:

  • Static assets (/assets/*, *.css, *.js, *.png, *.jpg): cacheable by default, long TTL (e.g., Cache-Control: public, max-age=31536000, immutable). Use versioned file names (fingerprint in the path) so cache-busting is “deploy a new filename.”
  • HTML routes (/, /page/*): short TTL (e.g., 60 s) or private, depending on personalisation. If pages have per-user content, Cache-Control: private and let the browser cache, but CloudFront doesn’t.
  • REST API paths (/api/*): Cache-Control: no-store typically, or use CloudFront with a very short TTL for idempotent GETs that tolerate staleness.
  • Origin Shield in an appropriate mid-tier region (e.g., CDG if origin is in DUB) to consolidate origin-facing traffic and reduce origin load.

Auth between CloudFront and the ALB: use signed headers (custom-header secret verified by a WAF rule at the ALB) so only CloudFront can reach the origin directly. That closes the “users can bypass CloudFront by hitting the ALB directly” hole.

Global Accelerator, for WebSocket + legacy TCP. Create one accelerator. Two listeners: :443 (TCP) for the WebSocket, :8443 (TCP) for the legacy TCP service. One endpoint group per listener, each pointing to the appropriate NLB in eu-west-1.

Client affinity: set ClientAffinity=SOURCE_IP on the WebSocket listener so a given user’s repeated connections land on the same endpoint (useful for sticky-session requirements). Legacy TCP, same.

Health checks: Global Accelerator does its own health checks against the endpoint (not the user’s traffic path). An unhealthy endpoint is removed from rotation within seconds.

The static IPs. Global Accelerator hands you two anycast IPs – 75.2.60.5 and 99.83.89.12 are placeholder examples. Publish them in the thick-client firmware or in the customer docs. Those two IPs are the entire allowlist the enterprise firewall needs. Forever.

Regional failover. Add us-east-1 as a second endpoint group on the accelerator, with traffic dial 0%. Deploy a warm-standby NLB in us-east-1 (using the same infrastructure templates). When eu-west-1 is healthy, 100% of traffic lands in eu-west-1. When the eu-west-1 endpoint group is unhealthy, Global Accelerator shifts traffic to us-east-1 endpoints, automatically, within health-check timing. This pattern earns the “regional failover” attribute.

For the web app, CloudFront’s origin failover configuration does the equivalent at the HTTP layer: a primary and a secondary origin group with status-code-based failover criteria.

A worked path

A Sydney gamer opens the app:

  1. Static assets. Browser resolves app.example.com to CloudFront. Nearest PoP is Sydney; cached assets serve from Sydney with sub-20 ms latency.
  2. REST API for session auth. Browser POSTs to /api/auth. CloudFront does not cache this path (short TTL or no-store). It opens a TCP connection from the Sydney PoP to the origin ALB over AWS’s backbone. Backbone latency Sydney-to-Dublin is ~260 ms vs public-internet ~300+ ms. Saves 30-60 ms, mostly because of the better path and TCP acceleration.
  3. WebSocket for gameplay. Browser opens wss://realtime.example.com:443. DNS resolves to the Global Accelerator IPs. TCP SYN hits the nearest edge (Sydney). Accelerator forwards to eu-west-1 NLB over the backbone. Subsequent frames run over this long-lived TCP connection. Latency from Sydney to origin via accelerator is within a few ms of what a direct backbone path would be.
  4. Legacy thick client. Connects to 75.2.60.5:8443. Same path as the WebSocket: nearest edge, backbone to eu-west-1, NLB handles.

Each traffic class takes the right path. The gamer’s experience: static assets at ~20 ms (cached), API calls at ~260 ms (backbone), WebSocket frames at ~260 ms one-way (unavoidable physics). Before the combined setup, the same user was seeing ~350 ms everywhere and intermittent TCP establishment failures from India.

What’s worth remembering

  1. CloudFront is HTTP/HTTPS caching; Global Accelerator is anycast TCP/UDP routing. Different layers, different problems.
  2. CloudFront wins for cacheable content. Cache hits skip the origin entirely. Origin Shield adds a mid-tier cache for origin offload.
  3. Global Accelerator wins for non-HTTP or non-cacheable. WebSockets, raw TCP, UDP, session-bound APIs. The win is the backbone path, not caching.
  4. Two static IPv4 addresses (and /56 IPv6) are Global Accelerator’s killer feature for enterprise. Stable for the accelerator’s lifetime; the allowlist firewall users love it.
  5. Regional failover differs. CloudFront origin failover is HTTP-status-code-driven primary/secondary. Global Accelerator uses health checks and traffic dials between endpoint groups; automatic and fast.
  6. Both support AWS Shield Standard. Edge-layer DDoS absorption without additional config; Shield Advanced adds more for both.
  7. They layer. A single domain can be a CloudFront distribution whose origin is a Global Accelerator endpoint, occasionally useful when you want HTTP caching plus backbone reach to a fleet of origins with static IP affinity. Most deployments don’t need the composite.
  8. Cost differs significantly. CloudFront per-GB egress is tiered and often cheaper than origin egress; Global Accelerator has a fixed $/hour plus data charges. Model both for your traffic mix.

CloudFront and Global Accelerator both live at “the edge,” both improve global latency, and both use AWS’s backbone to get traffic home. “Which one” is almost always a category error; what to ask is which traffic goes through which service. HTTP that caches → CloudFront. Not-HTTP or doesn’t-cache → Global Accelerator. The work isn’t picking a favourite, it’s mapping each protocol in the app to the service whose shape fits it.

These posts are LLM-aided. Backbone, original writing, and structure by Craig. Research and editing by Craig + LLM. Proof-reading by Craig.