Shielding

Under normal conditions, visitor requests reach your Fastly service at points of presence (POPs) in various regions, which in turn independently forward those requests to your origin servers. When Shielding is enabled, visitor requests from across the global network funnel through a single, designated shield POP instead. This ensures that all user traffic converges at a singular entry point before reaching your origin, which can reduce upstream load.

Shielding illustration

Shielding has significant benefits:

  • Reduces origin load: reduces the volume of requests from Fastly to your origin servers
  • Improves cache hit ratio (CHR): increases the probability of end user requests resulting in a cache HIT (albeit potentially not from the first POP which handles the request)
  • Speeds up connections: reduces connection setup latency for MISS and PASS requests that must be served from origin (this feels counter-intuitive, but takes advantage of the fact that all Fastly POPs always have a pool of open connections to all other Fastly POPs, reducing the time required for costly multi-roundtrip handshakes).

Shielding is only applicable to requests that are forwarded to a backend. Shielding is not involved when the request is handled entirely at the edge, e.g., on a synthetic response or a static asset edge hit.

Enabling and disabling shielding

  1. CDN services
  2. Compute services

In CDN services, shielding may be enabled when adding or editing an origin server, and may be selected per-origin. If your origin servers are in different locations, it may make sense to choose different shields for each origin server. You can enable shielding via the web interface, or set the shield property of a Backend object when you create or modify it using the API or CLI. For example:

$ fastly backend create --name=app_server --address=192.168.123.123 --shield=amsterdam-nl --service-id=9yqrXWr5kfqroswtmxgQDz --version=latest
SUCCESS: Created backend app_server (service 9yqrXWr5kfqroswtmxgQDz version 1)

Each POP has a shield identifier. These are listed in the properties returned from the /pops API endpoint. For example, the POP in Amsterdam has the name AMS but a shield identifier of amsterdam-nl.

For more details, refer to choosing a shield location.

Effects of shielding

Enabling shielding on a Fastly service will create side effects that should be considered carefully.

Double execution

When shielding is enabled, your edge code typically executes twice: once at the edge POP where the visitor initially lands, and a second time at the designated shield POP. However, if the designated shield POP also happens to be the closest regional POP for that visitor, the request lands directly at the shield from the start. In this scenario, your edge code executes once, as the shield POP is the only step between the visitor and the origin servers.

When shielding is enabled, you must carefully choose where to modify your requests and responses. Because your code can run multiple times along a single request path, executing logic without consideration of the execution context can lead to redundant processing, duplicate headers, or issues like cache poisoning (refer to caching implications). To keep your application deterministic, it is best to separate origin-bound operations, which are best handled directly at the shield POP, from client-facing operations, which should execute only at the client-connected POP.

  1. CDN services
  2. Compute services

In CDN services, Fastly's infrastructure manages the routing behind the scenes, running your code twice implicitly.

Targeting the request to the origin

req.backend.is_origin and req.backend.is_shield tell you whether a backend request made from the current POP will go to your origin server or to a shield POP, which is usually important when manipulating a request. fastly.ff.visits_this_service tells you whether the current POP is acting as a shield POP or not, which is more often important when manipulating a response.

For example, use req.backend.is_origin to determine whether to modify request headers before forwarding a request to an origin:

sub vcl_miss { ... }
Fastly VCL
if (req.backend.is_origin) {
set req.http.host = "example.com";
}

Targeting the response to the client

But use fastly.ff.visits_this_service to determine whether to modify response headers before delivering a response:

sub vcl_deliver { ... }
Fastly VCL
if (fastly.ff.visits_this_service == 0) {
set resp.http.Cache-Control = "no-store, private";
}

Both of these conditionals will ensure that the associated logic only runs once. Here are some more examples of operations typically associated with one or the other phase:

Targeting the response to the clientTargeting the request to the origin
VCL: fastly.ff.visits_this_service == 0req.backend.is_origin == true
Compute: presence of custom tracking header (such as X-From-Edge-POP)shield.runningOn() (or your SDK's equivalent)
Manipulating the request URL
Normalizing the request
Authentication
Security filtering (e.g., WAF or bot detection)
Redirects
Geolocation
A/B testing
ESI
Compressing responses
Setting backend-specific headers

HINT: As well as using the above conditional expressions, you can also write your code in a way that is idempotent, that is, it only has effect once, and if you run it again, nothing happens.

For example, static bucket storage origins like AWS S3 or GCS may require a path prefix to be added to the URL. Doing this unconditionally may result in the prefix being added twice, e.g. /bucket-name/bucket-name/path/to/file.

  1. CDN services
  2. Compute services

While you could use a variable such as fastly.ff.visits_this_service to avoid this, a better solution is to detect the presence of the prefix:

if (req.url.path !~ "^/bucket-name/") {
set req.url = "/bucket-name/" + req.url;
}

Not available with code-defined backends (Applies to VCL only)

In CDN services, it is possible to define backends using VCL code as well as via the web interface, API or CLI. However, you cannot apply shielding to backends that you define in VCL.

Host header hidden problems

Fastly's platform uses an incoming request's HTTP Host header to identify the service that handles the request. This applies to public traffic as well as internal requests that travel from edge POPs to shield POPs.

If you mutate the Host header on the forwarded request at the outer edge (e.g., changing it from example.com to my-domain.com), the request will land at the shield POP with the changed hostname. Because the Host header used to identify the service has been altered, the shield POP won't recognize the request and will immediately drop it with an HTTP 500 Internal Server Error.

  1. CDN services
  2. Compute services

There are two primary ways to avoid this pitfall in CDN services.

Approach 1: Conditionally mutate the header only on the shield POP

If you must mutate the Host header manually in your code, only do so when the request is known to be going to your own origin (see req.backend.is_origin)

Approach 2: Use override_host on the Backend definition (Recommended)

Use the override_host property when creating the backend, and don't modify req.http.host in VCL. This approach is often the most conceptually straightforward and least prone to error.

Client IP inaccuracy

  1. CDN services
  2. Compute services

In CDN services, client.ip reflects the IP address of the immediate downstream connection. In a shielding configuration, this means the shield POP will see the edge POP's IP instead of the user's.

To obtain the true client IP address across both edge and shield execution contexts, read the Fastly-Client-IP header.

HINT: The client.identity variable is also influenced by the apparent client IP, so if making use of client directors, client.identity should be reset to Fastly-Client-IP or to an identifier specific to your service.

set client.identity = req.http.Fastly-Client-IP;

Cache hit ratio inaccuracy

If a request results in a MISS at an edge POP and is forwarded to a shield POP where it finds a HIT, the user is ultimately served from cache, but we will record both the miss and the hit for the purpose of calculating your cache hit ratio. While 'shield hits' will involve more latency for end users than 'edge hits', the hit will still mean there is no need for an origin request. Equally, a request that does reach your origin server will be counted as two misses, one at the edge, and one at the shield.

This will result in a cache hit ratio (CHR) that may be lower than you expect. Since there are multiple ways of calculating CHR on shielded configurations, you may like to use our historical stats API to get raw numbers and perform your own calculations.

Backend assignment in code (Applies to VCL only)

In CDN services, if you wish to write custom VCL logic for assigning a backend to a request (i.e. set req.backend = backend_name;), and that backend is shielded, see multiple backends, to avoid overriding (or being overridden by) the generated shield routing logic.

Caching implications

When shielding is active, you must take care when modifying responses. A mistake here can accidentally poison your edge caches.

  • Modifying at the edge POP: Changes made right before serving the response to the visitor are never stored in that POP's cache. Your code executes these modifications dynamically on every single requesteven when a request is served as a local cache hit without fetching anything from the shield or origin.
  • Modifying at the shield POP (not recommended): Any changes made here are viewed by downstream edge POPs as part of the official payload from your origin servers. Consequently, these modified responses will be stored in the edge POP's cache.

IMPORTANT: If you want to alter a response just before it hits the browser (such as stripping debugging headers or injecting security headers), always ensure that code executes exclusively at the "client-connected" POP. For details, refer to Targeting the response to the client under Double Execution.

Billing implications

Traffic from one Fastly POP to another will count towards your request count and billable bandwidth. In the most extreme case, if your service is configured to PASS every request, then your request count and delivery bandwidth will almost double, since most requests will be presented to two Fastly POPs, but in more realistic scenarios, shielding will often reduce costs overall. See our guide in documentation for more information.

Advanced shielding scenarios

Shielding can be used in many different configurations and variations. Some of the most common include:

Multiple backends

If you have multiple backends (for example because you are performing load balancing, have origin servers serving different regions, or a microservices architecture), then each backend must have shielding configured independently.

Multiple backends

  1. CDN services
  2. Compute services

Configure each backend with the shield location that is most appropriate for its origin server, by setting the shield property to your chosen shield identifier. Backends may share the same shield POP or may shield in different locations, unless they have automatic load balancing enabled, in which case all backends must shield in the same location.

With multiple backends your service also requires some configuration to tell Fastly which backend to use. If you do not have custom VCL and use conditions or automatic load balancing to select backends, this happens automatically. However, if you want to use custom VCL to select an origin that is shielded, we recommend combining custom VCL with conditions:

  1. Declare a custom local boolean variable per backend at the start of vcl_recv
  2. Add a condition to each backend, which selects that backend if the matching variable is true, e.g., var.backend_a == true
  3. Add custom backend selection logic before the #FASTLY placeholder in your VCL code (or using a VCL snippet)

For example:

sub vcl_recv { ... }
Fastly VCL
1
2
3
4
5
6
7
8
9
10
declare local var.backend_a BOOL;
declare local var.backend_b BOOL;
if (req.url.path ~ "^/account(?:/.*)?\z") {
set var.backend_a = true;
} else {
set var.backend_b = true;
}
#FASTLY RECV

This way, Fastly is able to perform the backend selection (the code to do this will be generated and will replace the #FASTLY RECV placeholder), and will assign the shield POP or the actual origin server as appropriate, while still ultimately selecting the correct backend for the request based on your own logic.

Why is this? Learn more...

Service pinning

If your service is pinned, the end user's domain may not be explicitly linked to it. This is because your service responds to any request resolving to your Fastly-assigned dedicated IP space, as long as a TLS configuration exists. When a request moves from an edge POP to a shield POP, the shield may not know which service to invoke.

  1. CDN services
  2. Compute services

To resolve this in CDN services, use a custom header (such as req.http.x-orig-host) to carry the true client domain across the internal Fastly network. When executing at the edge POP, save the client's original Host header to the custom header and swap the backend request's Host header to your explicitly associated anchor domain before routing it to the shield. When the request lands at the shield POP, your VCL code reads that custom header and restores it.

sub vcl_vcl { ... }
Fastly VCL
1
2
3
4
5
6
7
8
#capture the incoming hostname from the client
if (fastly.ff.visits_this_service == 0 && req.restarts == 0) {
set req.http.x-orig-host = req.http.host;
}
#overwrite the anchor hostname at the shield with the original client requested hostname
if (fastly.ff.visits_this_service > 0 && req.http.x-orig-host) {
set req.http.host = req.http.x-orig-host;
}
sub vcl_miss { ... } / sub vcl_pass { ... }
Fastly VCL
set bereq.http.host = if (
req.backend.is_shield,
"{some-name}.freetls.fastly.net", # A domain explicitly associated with your service, to allow shielding to work
req.http.x-orig-host # The hostname to forward to your backend
);

Enable or disable shielding for a single request

  1. CDN services
  2. Compute services

In CDN services, shielding behavior is part of the backend configuration. If a shielded backend is selected and the current POP is not the designated shield POP, shielding will happen by default if req.restarts == 0 (i.e., the request has not been restarted). You can change this using a "recv" VCL snippet):

set var.fastly_req_do_shield = true;

You may want to do this to achieve use cases such as:

  • Enable shielding even after a restart
  • Disable shielding for certain URL paths which cannot be cached

var.fastly_req_do_shield is a custom VCL variable defined by Fastly's generated VCL. It's defined at the beginning of the #FASTLY RECV macro and affects the shielding decisions made at the end of #FASTLY RECV, so the only way to use it effectively is in a VCL snippet, because snippets are rendered within the #FASTLY RECV macro.

Debugging

Shielding increases the number of potential outcomes for a request presented to a Fastly edge. It's possible that the request will be answered directly from the edge POP. If the edge POP doesn't have the object, the request might still result in a cache HIT, but from the shield POP. Observing these effects and understanding how they affect your metrics can be a necessary step in debugging services with shielding enabled.

The X-Served-By, X-Cache-Hits and X-Cache response headers, which normally show only one entry without shielding enabled, will include an entry for each Fastly POP that has processed the request, but bear in mind that if a request is a HIT at the edge, the entry representing the shield POP will be from when the cached object was originally cached. First, start by understanding the possible values of X-Cache:

XCacheMeaningCHR implications
MISS, MISSThe object was not in cache at either the edge or the shield. The requested object was fetched from the backend. This will count as two misses as part of the calculation of your headline CHR.2 misses
HIT, MISSThe object was not in cache at the edge, so was forwarded to the shield, where it was found in cache. This outcome will contribute one 'miss' to your headline CHR although ultimately the request is satisfied from within the Fastly network.1 hit, 1 miss
MISS, HITThe object was found in cache at the edge. When the object was (previously) fetched from the shield, it was a MISS at the shield. The 'MISS' here is a record of a prior event, not something that happened in this request.1 hit
HIT, HITThe object was found in cache at the edge. When the object was (previously) fetched from the shield, it was a HIT at the shield. The first 'HIT' here is a record of a prior event, not something that happened in this request.1 hit
HITThe object was found in cache, and the POP that received the request in this case happens to be the designated shield, so the object was originally loaded directly from the backend.1 hit
MISSThe object was not in cache, and the POP that received the request in this case happens to be the designated shield, so the object was fetched directly from the backend and served to the end user.1 miss

So, where the X-Cache header contains two entries and the second one is 'HIT', the first entry in each of the three debugging headers relates to when the object was originally fetched from the shield, not the current status of the object at the shield.

Additionally, the X-Cache-Hits header records the value of the obj.hits VCL variable, which is local to the individual cache node. To optimise and balance load, Fastly may cache objects on multiple machines in a POP, and particularly hot objects may end up cached on every node in the POP (see clustering to learn more). As a result, where the second token of X-Cache is 'HIT', the first token of X-Cache-Hits will refer to the number of hits recorded on the individual cache server at the shield POP at the time that the object was served from the shield to the edge. This can often be confusing.

Example response data

Imagine a request for an object that is not cached by Fastly, on a service with shielding enabled. The response would contain headers that look like this:

X-Cache: MISS, MISS
X-Served-By: cache-iad2120-IAD, cache-sjc3120-SJC
X-Cache-Hits: 0, 0

In this instance, the X-Cache: MISS, MISS shows that the request has transited two Fastly POPs and was not in the cache in either of them. X-Served-By lists the servers acting as the delivery node in each POP, in the order in which they processed the response. In this case, cache-iad2120-IAD (Dulles, Virginia) was the shield POP (closest to the backend), and therefore saw the response first, and cache-sjc3120-SJC (San Jose, California) was the edge POP (closest to the end user).

If the same request is made, moments later, by the same user on a still-open connection, it would be expected to be handled by exactly the same edge server:

X-Cache: MISS, HIT
X-Served-By: cache-iad2120-IAD, cache-sjc3120-SJC
X-Cache-Hits: 0, 1

This time, the request was a hit at the edge cache-node (cache-sjc3120-SJC). Because it is a hit at the edge it would not be forwarded to a shield. The MISS listed for cache-iad2120-IAD reflects the state of that node from the first request, and not its current state. The object is now cached in both POPs. Making a third request on the same connection would result in the same response except that X-Cache-Hits would now be 0, 2.

Requesting the object again on a fresh connection will likely result in the request being handled by a different edge cache server:

X-Cache: MISS, HIT
X-Served-By: cache-iad2120-IAD, cache-sjc3122-SJC
X-Cache-Hits: 0, 1

This third request is very similar to the second, but in being handled by a different cache node (cache-sjc3122-SJC) at the edge POP, X-Cache-Hits reflects the hit count at the individual server level so still shows only 1 hit on this machine and 0 at the shield POP.

Choosing a shield location

You should choose a shield POP that is physically close to your origin servers. There are a couple of other parameters to consider:

  • Some Fastly POPs have interconnection points with cloud provider networks. An interconnection point is a location where two networks exchange data or traffic. When a POP uses a private network interconnect (PNI), the POP and cloud provider use the interconnection point instead of the public internet to manage requests. If your origin is hosted with one of these providers, choose a shield location where we have an interconnect for optimal performance and potential cost savings.

  • Fastly POPs vary dramatically in size and current spare capacity. Choose a POP that offers the largest cache storage for a better cache hit ratio at the shield, and therefore reduced origin traffic.

Understanding PNI cost savings

Depending on your origin's provider, choosing a shield location with an interconnect could provide significant cost savings.

WARNING: Fastly makes no warranty on third-party software. We assume no responsibility for errors or omissions in the third-party software or documentation available. Using such software is done entirely at your own discretion and risk.

ProviderCost savingsAvailability
Amazon Web ServicesNoneNot applicable
Backblaze B2 Cloud StorageFree egressAny shield location
Google Cloud PlatformDiscounted egressAll GCP PNIs
Microsoft AzureFree egressAMS, BFI, CHI, DFW, IAD, and PAO only

Shield locations

The following POPs are suitable for shielding Fastly services:

LocationPOPCapacityShield codePNIsRecommended for
AmsterdamAMS●●○amsterdam-nl west-europe
europe-west4
AshburnIAD●●●iad-va-us east-us
us-east-1
us-east1
us-east4
us-east5
AtlantaPDK●●●atlanta-ga-us
AucklandAKL●○○auckland-akl
BogotaBOG●●○bog-bogota-co
BostonBOS●○○bos-ma-us
BrisbaneBNE●○○brisbane-au
BrusselsBRU●○○bru-brussels-be
Cape TownCPT●○○cpt-capetown-zaaf-south-1
ChennaiMAA●○○maa-chennai-in
ChicagoCHI●●●chi-il-us ca-central-1
us-east-2
us-central1
ChristchurchCHC●○○chc-christchurch-nz
CopenhagenCPH●○○cph-copenhagen-dk
DallasDFW●●●dallas-tx-us us-south1
DelhiDEL●○○del-delhi-in asia-south2
DenverDEN●●○den-co-usus-west3
DublinDUB●●○dub-dublin-ieeu-west-1
FrankfurtFRA●●●frankfurt-de eu-central-1
eu-central-2
europe-central2
europe-west3
europe-west6
europe-west8
me-west1
Fujairah Al MahtaFJR●○○fjr-fujairah-uaeme-central-1
me-south-1
HelsinkiHEL●○○hel-helsinki-fieurope-north1
Hong KongHKG●○○hongkong-hk ap-east-1
asia-east2
HoustonIAH●●●iah-tx-us
HyderabadHYD●○○hyd-hyderabad-in
JohannesburgJNB●○○jnb-johannesburg-za
KolkataCCU●○○ccu-kolkata-in
LisbonLIS●○○lis-lisbon-pt
LondonLCY●●●london_city-uk eu-west-2
europe-west2
LondonLHR●●○london-uk
LondonLON●●○lon-london-uk
Los AngelesBUR●●○bur-ca-us west-us
us-west2
us-west4
MadridMAD●●○mad-madrid-es europe-southwest1
ManchesterMAN●●○man-manchester-uk
MarseilleMRS●●○mrs-marseille-fr
MelbourneMEL●●○melbourne-au australia-southeast2
MiamiMIA●●○miami-fl-us
MilanMXP●●○mxp-milan-it eu-south-1
MinneapolisMSP●●○msp-mn-us
MontrealYUL●●○yul-montreal-ca
MumbaiBOM●●○bom-mumbai-inap-south-1
asia-south1
New York CityLGA●●○lga-ny-us northamerica-northeast1
NewarkEWR●●○ewr-nj-us
OsakaITM●●○osaka-jp ap-northeast-3
asia-northeast2
OsloOSL●○○osl-oslo-no
ParisPAR●●●paris-fr eu-west-3
europe-west1
europe-west9
PerthPER●○○perth-au
PortlandPDX●○○pdx-or-us
Rio de JaneiroGIG●●○gig-riodejaneiro-br southamerica-west1
San JoseSJC●●○sjc-ca-us us-west-1
Sao PauloGRU●●○gru-saopaulo-br sa-east-1
southamerica-east1
SeattleBFI●●●bfi-wa-us us-west-2
us-west1
SeoulICN●●○icn-seoul-krap-northeast-2
asia-northeast3
SingaporeSIN●●●sin-singapore-sg ap-southeast-1
ap-southeast-3
asia-southeast1
asia-southeast2
SofiaSOF●○○sof-sofia-bg
StockholmBMA●●○stockholm-bma eu-north-1
europe-north1
SydneySYD●●○sydney-au australia-east
ap-southeast-2
australia-southeast1
SydneyWSI●●○wsi-australia-au
TokyoNRT●●●nrt-tokyo-jp ap-northeast-1
asia-east1
asia-northeast1
TorontoYYZ●●○yyz-on-ca northamerica-northeast2
ViennaVIE●●○vie-vienna-at

Cloud provider recommendations are powered by jsDelivr's Globalping platform. jsDelivr is a member of our Fast Forward logo community program, which nurtures and supports projects that share our vision of an open internet that is fast and secure for all.


This is not a complete list of Fastly POPs. For a complete list, see the POPs API, run fastly pops on your terminal, or see our network map.