---
title: Shielding
summary: null
url: https://www.fastly.com/documentation/guides/concepts/shielding
---

Under normal conditions, visitor requests reach your Fastly service at [points of presence (POPs)](https://www.fastly.com/documentation/guides/getting-started/concepts/using-fastlys-global-pop-network) 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](https://www.fastly.com/documentation/reference/glossary#term-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](/img/shielding-precomposed.svg)

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](https://www.fastly.com/documentation/guides/getting-started/concepts/using-fastlys-global-pop-network) 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](https://www.fastly.com/documentation/guides/getting-started/concepts/using-fastlys-global-pop-network) always have a pool of open connections to all other Fastly [POPs](https://www.fastly.com/documentation/guides/getting-started/concepts/using-fastlys-global-pop-network), reducing the time required for costly [multi-roundtrip handshakes](https://hpbn.co/transport-layer-security-tls/#tls-handshake)).

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

### Cdn Services

In VCL 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](https://www.fastly.com/documentation/guides/getting-started/hosts/shielding), or set the [`shield`](https://www.fastly.com/documentation/reference/api/services/backend/#field_shield) property of a `Backend` object when you create or modify it using the API or CLI. For example:

```term
$ 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](https://www.fastly.com/documentation/guides/getting-started/concepts/using-fastlys-global-pop-network) has a shield identifier. These are listed in the properties returned from the [`/pops`](https://www.fastly.com/documentation/reference/api/utils/pops/#list-pops) API endpoint. For example, the [POP](https://www.fastly.com/documentation/guides/getting-started/concepts/using-fastlys-global-pop-network) in Amsterdam has the name `AMS` but a shield identifier of `amsterdam-nl`.

For more details, refer to [choosing a shield location](https://www.fastly.com/documentation/guides/concepts/shielding#choosing-a-shield-location).

### Compute Services

In Compute services, shielding is implemented programmatically using your language SDK's Shielding API. You choose a [Fastly POP](https://www.fastly.com/documentation/guides/getting-started/concepts/using-fastlys-global-pop-network) to be the designated shield POP for each origin your service shields—a choice you make based on geographical proximity to your origin servers and other network routing factors. Your application code then uses the API to learn whether it is currently running on that specific POP; if it isn’t, the API provides a secure tunnel to it. This tunnel behaves like a standard [Backend](https://www.fastly.com/documentation/guides/integrations/non-fastly-services/developer-guide-backends/), allowing the Compute service to seamlessly issue HTTP requests to the shield and manipulate or cache the resulting responses.

Each [POP](https://www.fastly.com/documentation/guides/getting-started/concepts/using-fastlys-global-pop-network) has a shield identifier. These are listed in the properties returned from the [`/pops`](https://www.fastly.com/documentation/reference/api/utils/pops/#list-pops) API endpoint. For example, the [POP](https://www.fastly.com/documentation/guides/getting-started/concepts/using-fastlys-global-pop-network) in Amsterdam has the name `AMS` but a shield identifier of `amsterdam-nl`.

For more details, refer to [choosing a shield location](https://www.fastly.com/documentation/guides/concepts/shielding#choosing-a-shield-location).

When building a resilient architecture, your service should always **fail open** rather than crashing, to avoid dropping visitor traffic with an unexpected HTTP `500` error. If a shield POP is unavailable due to an error in the shield configuration, a lookup error, or an unlikely temporary lapse in availability, your edge code should gracefully fall back to sending requests directly to your origin servers.

> **NOTE:** Shielding APIs are not supported in Fastly Fiddle.

### Rust

In a Compute service written in Rust, use the [`fastly::shielding` module](https://docs.rs/fastly/latest/fastly/shielding/index.html).

```rust
// section visible
use fastly::shielding::Shield;
// section-end visible
use fastly::{Error, Request, Response};

#[fastly::main]
fn main(req: Request) -> Result {
// section visible
    // Obtain a reference to the shield POP
    // (will return None if this fails)
    let shield = Shield::new("amsterdam-nl")
        .inspect_err(|e| eprintln!("Unable to obtain reference to shield: {e:?}"))
        .ok();

    // Filter out the shield if we are already running on it,
    // then extract the secure encrypted tunnel backend
    // (will return None if this fails)
    let tunnel = shield
        .filter(|s| !s.running_on())
        .and_then(|s| s.encrypted_backend()
            .inspect_err(|e| eprintln!("Unable to obtain encrypted backend for shield: {e:?}"))
            .ok());

    if let Some(tunnel) = tunnel {
        // If we have a tunnel to the shield POP, forward the request to the shield.
        // We will want to make considerations about caching here.
        Ok(req.send(tunnel)?)
    } else {
        // If we get here, we are either running on the shield POP, or shielding
        // initialization failed for any reason.
        // Forward the request directly to your origin.
        Ok(req.send("origin_backend")?)
    }
// section-end visible
}
```

### Javascript

In a Compute service written in JavaScript, use the [`fastly:shielding` package](https://js-compute-reference-docs.edgecompute.app/docs/shielding/Shield/).

```js
// section visible

// section-end visible

async function handleRequest(event) {
// section visible
  const req = event.request;

  // Obtain a reference to the shield POP
  // (will return null if this fails)
  let shield = null;
  try {
    shield = new Shield('amsterdam-nl');
  } catch (e) {
    // Shield configuration error, shield remains null
    console.warn('Unable to obtain reference to shield', e);
  }

  // Filter out the shield if we are already running on it,
  // then extract the secure encrypted tunnel backend
  // (will return null if this fails)
  let tunnel = null;
  try {
    if (shield && !shield.runningOn()) {
      tunnel = shield.encryptedBackend();
    }
  } catch (e) {
    // Encryption or platform error, tunnel remains null
    console.warn('Unable to obtain encrypted backend for shield', e);
  }

  let response;
  if (tunnel) {
    // If we have a tunnel to the shield POP, forward the request to the shield.
    // We will want to make considerations about caching here.
    response = await fetch(req, { backend: tunnel });
  } else {
    // If we get here, we are either running on the shield POP, or shielding
    // initialization failed for any reason.
    // Forward the request directly to your origin.
    response = await fetch(req, { backend: 'origin_backend' });
  }

  return response;
// section-end visible
}

addEventListener("fetch", (event) => event.respondWith(handleRequest(event)));
```

### Go

In a Compute service written in Go, use the [`shielding` package](https://pkg.go.dev/github.com/fastly/compute-sdk-go/shielding).

```go
package main

// section visible
import (
// section-end visible
	"context"
	"io"

	"github.com/fastly/compute-sdk-go/fsthttp"
// section visible
	"github.com/fastly/compute-sdk-go/shielding"
)
// section-end visible

func main() {
	fsthttp.ServeFunc(func(ctx context.Context, w fsthttp.ResponseWriter, r *fsthttp.Request) {
// section visible
		// Obtain a reference to the shield POP
		var shield *shielding.Shield
		if s, err := shielding.ShieldFromName("amsterdam-nl"); err != nil {
			// Shield configuration error, shield remains nil
			fmt.Fprintln(os.Stderr, "Unable to obtain reference to shield:", err)
		} else {
			shield = s
		}

		// Filter out the shield if we are already running on it,
		// then extract the secure encrypted tunnel backend
		var tunnel string
		if shield != nil && !shield.IsRunningOn() {
			if backendName, err := shield.Backend(nil); err != nil {
				// Encryption or platform error, tunnel remains empty
				fmt.Fprintln(os.Stderr, "Unable to obtain encrypted backend for shield:", err)
			} else {
				tunnel = backendName
			}
		}

		var resp *fsthttp.Response
		var err error

		if tunnel != "" {
			// If we have a tunnel to the shield POP, forward the request to the shield.
			// We will want to make considerations about caching here.
			resp, err = r.Send(ctx, tunnel)
		} else {
			// If we get here, we are either running on the shield POP, or shielding
			// initialization failed for any reason.
			// Forward the request directly to your origin.
			resp, err = r.Send(ctx, "origin_backend")
		}

// section-end visible
		if err != nil {
			fsthttp.Error(w, err.Error(), fsthttp.StatusBadGateway)
			return
		}

		w.Header().Reset(resp.Header)
		w.WriteHeader(resp.StatusCode)
		io.Copy(w, resp.Body)
	})
}
```

### Cpp

Shielding is not available in this version of the Fastly Compute C++ SDK.

## 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](https://www.fastly.com/documentation/guides/concepts/shielding#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.

### Cdn Services

In VCL 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:

```vcl context="sub vcl_miss { ... }"
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:

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

### Compute Services

In Compute services, your code explicitly triggers double execution by sending a request to the shield POP's encrypted tunnel backend as appropriate.

**Targeting the request to the origin**

For best results, ensure that any request manipulation intended for your origin servers only happens at the step the request is transitioning from the shield POP to your origin servers.

Calling `shield.runningOn()` (or your SDK's equivalent) determines the current execution context:

- When it returns `false`: The code is executing at an edge POP. At this stage, your backend request will target the shield POP.

- When it returns `true`: The code is executing at the shield POP (either forwarded via a shielding tunnel from an edge POP or because the visitor hit the shield POP directly). At this stage, your backend request will target your origin servers.

  > **For VCL developers:** Modifying the request here is equivalent to checking `req.backend.is_origin` in VCL to prepare requests right before they leave the Fastly network for your origin servers.

### Rust

```rust
use fastly::shielding::Shield;
use fastly::{Error, Request, Response};

#[fastly::main]
fn main(mut req: Request) -> Result {
    let shield = Shield::new("amsterdam-nl").unwrap();

// section visible
    let response = if shield.running_on() {
        // We are on the shield POP (forwarded via shileding from an
        // edge POP tunnel, or because the visitor hit the shield POP directly)

        // Ok to manipulate request meant for origin
        req.set_header("Host", "example.com");
        // Send to origin server
        req.send("origin_backend")?
    } else {
        // We are on an edge POP
        // Send request to the shield POP's tunnel backend
        req.send(shield.encrypted_backend().unwrap())?
    };
// section-end visible

    Ok(response)
}
```

### Javascript

```js

async function handleRequest(event) {
  const req = event.request;
  const shield = new Shield('amsterdam-nl');

// section visible
  let response;

  if (shield.runningOn()) {
    // We are on the shield POP (forwarded via shileding from an
    // edge POP tunnel, or because the visitor hit the shield POP directly)

    // Ok to manipulate request meant for origin
    req.headers.set("Host", "example.com");
    // Send to origin server
    response = await fetch(req, { backend: 'origin_backend' });
  } else {
    // We are on an edge POP
    // Send request to the shield POP's tunnel backend
    response = await fetch(req, { backend: shield.encryptedBackend() });
  }
// section-end visible

  return response;
}
```

### Go

```go
package main

import (
	"context"
	"io"
	"github.com/fastly/compute-sdk-go/fsthttp"
	"github.com/fastly/compute-sdk-go/shielding"
)

func main() {
	fsthttp.ServeFunc(func(ctx context.Context, w fsthttp.ResponseWriter, r *fsthttp.Request) {
		shield, err := shielding.ShieldFromName("amsterdam-nl")
		if err != nil {
			fsthttp.Error(w, err.Error(), fsthttp.StatusInternalServerError)
			return
		}

// section visible
		var resp *fsthttp.Response

		if shield.IsRunningOn() {
			// We are on the shield POP (forwarded via shielding from an
			// edge POP tunnel, or because the visitor hit the shield POP directly)

			// Ok to manipulate request meant for origin
			r.Header.Set("Host", "example.com")
			// Send to origin server
			resp, err = r.Send(ctx, "origin_backend")
		} else {
			// We are on an edge POP
			shieldBackendName, err := shield.Backend(nil)
// section-end visible
			if err != nil {
				fsthttp.Error(w, err.Error(), fsthttp.StatusInternalServerError)
				return
			}
// section visible
			// Send request to the shield POP's tunnel backend
			resp, err = r.Send(ctx, shieldBackendName)
		}

// section-end visible
		if err != nil {
			fsthttp.Error(w, err.Error(), fsthttp.StatusBadGateway)
			return
		}

		w.Header().Reset(resp.Header)
		w.WriteHeader(resp.StatusCode)
		io.Copy(w, resp.Body)
	})
}
```

### Cpp

Shielding is not available in this version of the Fastly Compute C++ SDK.

**Targeting the response to the client**

For best results, regardless of whether a single- or double-execution occurs, any modifications to the response that are intended for the client should be performed at the edge POP directly connected from the visitor (which can be the shield POP itself if the visitor's request hit the shield POP directly).

You can detect this "client-connected" context by passing a custom tracking header (such as `X-From-Edge-POP`) containing a shared secret during the internal edge-to-shield hop:

- When `shield.runningOn()` is `false` (running on a POP other than the shield POP), insert the tracking header `X-From-Edge-POP: SECRET_VALUE` right before forwarding the request to the shield POP tunnel.

- **During execution on any POP:** If the `X-From-Edge-POP` header value on the incoming request is missing or does not match `SECRET_VALUE`, you are guaranteed to be at the POP communicating directly with the client.

> **IMPORTANT:** Use a private, cryptographically secure value for `SECRET_VALUE`. All instances of your service across the network (shield POP or otherwise) share access to this value (which ideally loaded via a Fastly Secret Store). This makes it impossible for external clients to guess or spoof the token.

> **For VCL developers:** Intercepting the response here has the same effect as using `fastly.ff.visits_this_service == 0` in VCL to apply client-facing logic (like **Cache-Control** headers). Note that the internal `Fastly-FF` header is not available in Compute services.

### Rust

```rust
use fastly::secret_store::SecretStore;
use fastly::shielding::Shield;
use fastly::{Error, Request, Response};

#[fastly::main]
fn main(mut req: Request) -> Result {
    let shield = Shield::new("amsterdam-nl").unwrap();

// section visible
    // Load the shared token securely from the Fastly Secret Store
    let secret_store = SecretStore::open("shield-configuration")?;
    let secret_value = secret_store
        .get("routing-token")
        .map(|key| key.plaintext())
        .ok_or_else(|| {
            Error::msg("Missing [shield-configuration.routing-token] configuration in Secret Store")
        })?;

    // DETECT: We are "client-connected" if the `X-From-Edge-POP` header value on the
    // incoming request does not match the secret exactly
    let is_client_connected = req
        .get_header("X-From-Edge-POP")
        .and_then(|v| v.to_str().ok())
        .map(|v| v != secret_value)
        .unwrap_or(true);

    let mut response;

    if shield.running_on() {
        // Clean up internal tracking header before hitting origin servers
        req.remove_header("X-From-Edge-POP");
        response = req.send("origin_backend")?;
    } else {
        // Mark the request right before sending it through the tunnel
        req.set_header("X-From-Edge-POP", secret_value.as_ref());
        response = req.send(shield.encrypted_backend().unwrap())?;
    }

    // MODIFY RESPONSE: Safely apply client-only logic when "client-connected"
    if is_client_connected {
        response.set_header("Cache-Control", "no-store, private");
    }

    Ok(response)
// section-end visible
}
```

### Javascript

```js

async function handleRequest(event) {
  const req = event.request;
  const shield = new Shield('amsterdam-nl');

// section visible
  // Load the shared token securely from the Fastly Secret Store
  const secretStore = new SecretStore('shield-configuration');
  const secretKey = await secretStore.get('routing-token');
  const secretValue = secretKey?.plaintext();
  if (!secretValue) {
    throw new Error("Missing [shield-configuration.routing-token] configuration in Secret Store");
  }

  // DETECT: We are "client-connected" if the `X-From-Edge-POP` header value on the
  // incoming request does not match the secret exactly
  const isClientConnected = req.headers.get("X-From-Edge-POP") !== secretValue;

  let response;

  if (shield.runningOn()) {
    // Clean up internal tracking header before hitting origin servers
    req.headers.delete("X-From-Edge-POP");
    response = await fetch(req, { backend: 'origin_backend' });
  } else {
    // Mark the request right before sending it through the tunnel
    if (secretValue) {
      req.headers.set("X-From-Edge-POP", secretValue);
    }
    response = await fetch(req, { backend: shield.encryptedBackend() });
  }

  // MODIFY RESPONSE: Safely apply client-only logic when "client-connected"
  if (isClientConnected) {
    response.headers.set("Cache-Control", "no-store, private");
  }

  return response;
// section-end visible
}
```

### Go

```go
package main

import (
	"context"
	"io"

	"github.com/fastly/compute-sdk-go/fsthttp"
	"github.com/fastly/compute-sdk-go/secretstore"
	"github.com/fastly/compute-sdk-go/shielding"
)

func main() {
	fsthttp.ServeFunc(func(ctx context.Context, w fsthttp.ResponseWriter, r *fsthttp.Request) {
		shield, err := shielding.ShieldFromName("amsterdam-nl")
		if err != nil {
			fsthttp.Error(w, err.Error(), fsthttp.StatusInternalServerError)
			return
		}

// section visible
		// Load the shared token securely from the Fastly Secret Store
		secretStore, err := secretstore.Open("shield-configuration")
// section-end visible
		if err != nil {
			fsthttp.Error(w, "Missing [shield-configuration.routing-token] configuration in Secret Store", fsthttp.StatusInternalServerError)
			return
		}
// section visible
		secretKey, err := secretStore.Get("routing-token")
// section-end visible
		if err != nil || secretKey == nil {
			fsthttp.Error(w, "Missing [shield-configuration.routing-token] configuration in Secret Store", fsthttp.StatusInternalServerError)
			return
		}
// section visible
		secretValue, err := secretKey.Plaintext()
		if err != nil || secretValue == "" {
			fsthttp.Error(w, "Missing [shield-configuration.routing-token] configuration in Secret Store", fsthttp.StatusInternalServerError)
			return
		}

		// DETECT: We are "client-connected" if the `X-From-Edge-POP` header value on the
		// incoming request does not match the secret exactly
		isClientConnected := r.Header.Get("X-From-Edge-POP") != secretValue

		var resp *fsthttp.Response

		if shield.IsRunningOn() {
			// Clean up internal tracking header before hitting origin servers
			r.Header.Del("X-From-Edge-POP")
			resp, err = r.Send(ctx, "origin_backend")
// section-end visible
			if err != nil {
				fsthttp.Error(w, err.Error(), fsthttp.StatusBadGateway)
				return
			}
// section visible
		} else {
			// Mark the request right before sending it through the tunnel
			r.Header.Set("X-From-Edge-POP", secretValue)
			shieldBackendName, err := shield.Backend(nil)
// section-end visible
			if err != nil {
				fsthttp.Error(w, err.Error(), fsthttp.StatusInternalServerError)
				return
			}
// section visible
			resp, err = r.Send(ctx, shieldBackendName)
// section-end visible
			if err != nil {
				fsthttp.Error(w, err.Error(), fsthttp.StatusBadGateway)
				return
			}
// section visible
		}

		// MODIFY RESPONSE: Safely apply client-only logic when "client-connected"
		if isClientConnected {
			resp.Header.Set("Cache-Control", "no-store, private")
		}

// section-end visible
		w.Header().Reset(resp.Header)
		w.WriteHeader(resp.StatusCode)
		io.Copy(w, resp.Body)
	})
}
```

### Cpp

Shielding is not available in this version of the Fastly Compute C++ SDK.

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 client                                                                                                                                                         | Targeting the request to the origin                         |
| -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------- |
| **VCL:** `fastly.ff.visits_this_service == 0`                                                                                                                                                | `req.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<br />Normalizing the request<br />Authentication<br />Security filtering (e.g., WAF or bot detection)<br />Redirects<br />Geolocation<br />A/B testing<br />ESI | Compressing responses<br />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](https://aws.amazon.com/s3/) or [GCS](https://cloud.google.com/storage) may [require a path prefix](https://www.fastly.com/documentation/guides/integrations/non-fastly-services/developer-guide-backends/#modifying-the-request-path) 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`.
>
>
> ### Cdn 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:
>
> ```vcl
> if (req.url.path !~ "^/bucket-name/") {
>   set req.url = "/bucket-name/" + req.url;
> }
> ```
>
>
> ### Compute Services
>
>
>
> While you could use the `Fastly-FF` check described above to avoid this, a better solution is to detect the presence of the prefix:
>
>
> ### Rust
>
>
>
> ```rust compile_fail
> // Clone the request's URL to mutate it safely
> let mut url = req.get_url().clone();
> let path = url.path();
>
> // IDEMPOTENT CHECK: Only add the prefix if it isn't already there
> if !path.starts_with("/bucket-name/") {
>     let new_path = format!("/bucket-name{}", path);
>     url.set_path(&new_path);
>     req.set_url(url);
> }
> ```
>
>
> ### Javascript
>
>
>
> ```js
> // Parse the incoming request URL
> const url = new URL(req.url);
>
> // IDEMPOTENT CHECK: Only add the prefix if it isn't already there
> if (!url.pathname.startsWith("/bucket-name/")) {
>   url.pathname = `/bucket-name${url.pathname}`;
> }
> ```
>
>
> ### Go
>
>
>
> ```go
> // IDEMPOTENT CHECK: Only add the prefix if it isn't already there
> if !strings.HasPrefix(r.URL.Path, "/bucket-name/") {
>     r.URL.Path = "/bucket-name" + r.URL.Path
> }
> ```
>
>
> ### Cpp
>
>
>
> Shielding is not available in this version of the Fastly Compute C++ SDK.
>
> 
>
> 

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

In VCL services, it is possible to [define backends](https://www.fastly.com/documentation/reference/vcl/declarations/backend/) 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.

### Cdn Services

There are two primary ways to avoid this pitfall in VCL 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`](https://www.fastly.com/documentation/reference/api/services/backend/#field_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.

### Compute Services

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

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

If you must mutate the `Host` header manually in your code, wrap it in a `shield.runningOn()` check. This guarantees that the original, recognizable `Host` header is preserved while the request transits the Fastly network, and is only swapped right before it exits the shield POP to your own origin.

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

The recommended method is to use the [`override_host`](https://www.fastly.com/documentation/reference/api/services/backend/#field_override_host) property when creating the backend, and leave the request's `Host` header untouched in your code. With this method, Fastly's infrastructure swaps it out automatically on the very last hop.

### Client IP inaccuracy

### Cdn Services

In VCL 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](https://www.fastly.com/documentation/guides/concepts/load-balancing/#custom-directors), `client.identity` should be reset to `Fastly-Client-IP` or to an identifier specific to your service.
>
> ```vcl
> set client.identity = req.http.Fastly-Client-IP;
> ```

### Compute Services

In Compute services, your SDK's native client IP property (e.g., `event.client.address`) 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.

### 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](https://www.fastly.com/documentation/reference/api/metrics-stats/historical-stats/) to get raw numbers and perform your own calculations.

### Backend assignment in code (Applies to VCL only)

In VCL 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](https://www.fastly.com/documentation/guides/concepts/shielding#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 request—even 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](https://www.fastly.com/documentation/guides/concepts/shielding#double-execution).

### Billing implications

Traffic from one Fastly [POP](https://www.fastly.com/documentation/guides/getting-started/concepts/using-fastlys-global-pop-network) 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](https://www.fastly.com/documentation/guides/getting-started/concepts/using-fastlys-global-pop-network), but in more realistic scenarios, shielding will often reduce costs overall. [See our guide in documentation for more information](https://www.fastly.com/documentation/guides/getting-started/hosts/shielding/).

## 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](https://www.fastly.com/documentation/guides/concepts/load-balancing), have origin servers serving different regions, or a microservices architecture), then each backend must have shielding configured independently.

![Multiple backends](/img/multi-origin-precomposed.svg)

### Cdn Services

Configure each backend with the shield location that is most appropriate for its origin server, by setting the [`shield`](https://www.fastly.com/documentation/reference/api/services/backend/#field_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](https://www.fastly.com/documentation/guides/concepts/load-balancing#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](https://www.fastly.com/documentation/guides/full-site-delivery/conditions/using-conditions) 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](https://www.fastly.com/documentation/guides/full-site-delivery/fastly-vcl/about-fastly-vcl/#vcl-snippets))

For example:

```vcl context="sub vcl_recv { ... }"
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.

> **NOTE (Why is this?):** The `vcl_recv` subroutine is where backend selection happens. Some of this code is generated and managed by Fastly within the `#FASTLY RECV` placeholder that is required to be in [your VCL file](https://www.fastly.com/documentation/guides/full-site-delivery/fastly-vcl/about-fastly-vcl/#custom-vcl). The order of execution is:
>
> 1. Custom VCL prior to `#FASTLY RECV`
> 2. VCL snippets
> 3. Generated backend assignments
> 4. Generated shield routing logic
> 5. Custom VCL after `#FASTLY RECV`
>
> Fastly generates backend assignments based on your defined backends and any conditions that are applied to them, and then if the backend that got assigned is a shielded backend, will reassign the request to the shield POP instead of the actual backend (unless the current POP _is_ the shield POP). There is no opportunity to write custom VCL between steps 3 and 4, but if you add a condition on all backends which uses variables defined earlier in custom VCL or VCL snippets, you can set the values of those variables and they will be used by the generated backend assignments.
>
> If you assign directly to `req.backend` in your own code _after_ `#FASTLY RECV`, your assignment will override the shield routing and you'll effectively disable shielding. If you assign to `req.backend` _before_ `#FASTLY RECV` (or in a VCL snippet), your assignment will be overridden by the generated backend assignments.

### Compute Services

Designate a shield POP location independently for each backend. For best results, choose the shield POP based on geographical proximity to that specific backend's physical or cloud infrastructure.

When routing requests across multiple shielded backends, your application code dynamically maps each request to its corresponding shield execution path. Once your application selects which backend to target, reference or instantiate the designated shield POP for that specific backend. Call the `runningOn()` logic to check your execution state: if it returns `true`, forward the request directly to the target origin backend; if it returns `false`, route the request through that specific shield POP's encrypted tunnel backend.

### Rust

The following is an example of multi-backend shielded routing, using a scenario where `/account` routes to `backend_a` (shielded in Amsterdam) and all other traffic routes to `backend_b` (shielded in Ashburn).

```rust
use fastly::shielding::Shield;
use fastly::{Error, Request, Response};

#[fastly::main]
fn main(req: Request) -> Result {
// section visible
    let path = req.get_url().path();

    // 1. Map the request to a target backend and its corresponding shield POP
    let (target_backend, shield_location) = if path.starts_with("/account") {
        ("backend_a", "amsterdam-nl")
    } else {
        ("backend_b", "iad-va-us")
    };

    let shield = Shield::new(shield_location).unwrap();

    // 2. Fork the routing logic based on the selected shield's execution state
    if shield.running_on() {
        // Already on the designated shield POP; forward directly to the origin backend
        Ok(req.send(target_backend)?)
    } else {
        // Executing at an edge POP; route through this specific shield's tunnel
        Ok(req.send(shield.encrypted_backend().unwrap())?)
    }
// section-end visible
}
```

### Javascript

The following is an example of multi-backend shielded routing, using a scenario where `/account` routes to `backend_a` (shielded in Amsterdam) and all other traffic routes to `backend_b` (shielded in Ashburn).

```js

async function handleRequest(event) {
  const req = event.request;
  const url = new URL(req.url);

// section visible
  let targetBackend;
  let shieldLocation;

  // 1. Map the request to a target backend and its corresponding shield POP
  if (url.pathname.startsWith("/account")) {
    targetBackend = "backend_a";
    shieldLocation = "amsterdam-nl";
  } else {
    targetBackend = "backend_b";
    shieldLocation = "iad-va-us";
  }

  const shield = new Shield(shieldLocation);

  // 2. Fork the routing logic based on the selected shield's execution state
  if (shield.runningOn()) {
    // Already on the designated shield POP; forward directly to the origin backend
    return await fetch(req, { backend: targetBackend });
  } else {
    // Executing at an edge POP; route through this specific shield's tunnel
    return await fetch(req, { backend: shield.encryptedBackend() });
  }
// section-end visible
}
```

### Go

The following is an example of multi-backend shielded routing, using a scenario where `/account` routes to `backend_a` (shielded in Amsterdam) and all other traffic routes to `backend_b` (shielded in Ashburn).

```go
package main

import (
	"context"
	"io"
	"strings"

	"github.com/fastly/compute-sdk-go/fsthttp"
	"github.com/fastly/compute-sdk-go/shielding"
)

func main() {
	fsthttp.ServeFunc(func(ctx context.Context, w fsthttp.ResponseWriter, r *fsthttp.Request) {
// section visible
		var targetBackend string
		var shieldLocation string

		// 1. Map the request to a target backend and its corresponding shield POP
		if strings.HasPrefix(r.URL.Path, "/account") {
			targetBackend = "backend_a"
			shieldLocation = "amsterdam-nl"
		} else {
			targetBackend = "backend_b"
			shieldLocation = "iad-va-us"
		}

		shield, err := shielding.ShieldFromName(shieldLocation)
// section-end visible
		if err != nil {
			fsthttp.Error(w, err.Error(), fsthttp.StatusInternalServerError)
			return
		}

// section visible
		// 2. Fork the routing logic based on the selected shield's execution state
		if shield.IsRunningOn() {
			// Already on the designated shield POP; forward directly to the origin backend
			resp, err := r.Send(ctx, targetBackend)
// section-end visible
			if err != nil {
				fsthttp.Error(w, err.Error(), fsthttp.StatusBadGateway)
				return
			}
			w.Header().Reset(resp.Header)
			w.WriteHeader(resp.StatusCode)
			io.Copy(w, resp.Body)
// section visible
			return
		}

		// Executing at an edge POP; route through this specific shield's tunnel
		shieldBackendName, err := shield.Backend(nil)
// section-end visible
		if err != nil {
			fsthttp.Error(w, err.Error(), fsthttp.StatusInternalServerError)
			return
		}

// section visible
		resp, err := r.Send(ctx, shieldBackendName)
// section-end visible
		if err != nil {
			fsthttp.Error(w, err.Error(), fsthttp.StatusBadGateway)
			return
		}

		w.Header().Reset(resp.Header)
		w.WriteHeader(resp.StatusCode)
		io.Copy(w, resp.Body)
	})
}
```

### Cpp

Shielding is not available in this version of the Fastly Compute C++ SDK.

### Service pinning

If your service is [pinned](https://www.fastly.com/documentation/reference/glossary#term-service-pinning), the end user's [domain](https://www.fastly.com/documentation/reference/glossary#term-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](https://www.fastly.com/documentation/guides/concepts/routing-traffic-to-fastly/#dedicated-ips), 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.

### Cdn Services

To resolve this in VCL 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.

```vcl context="sub vcl_vcl { ... }"
#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;
}
```

```vcl context="sub vcl_miss { ... } / sub vcl_pass { ... }"
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
);
```

### Compute Services

To resolve this in Compute services, use a custom header (such as `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 `X-Orig-Host` and swap the active `Host` header to your explicitly associated anchor domain before passing the request to the shield tunnel. When that internally forwarded request executes at the shield POP, your code reads that custom header and restores it.

### Rust

```rust
use fastly::shielding::Shield;
use fastly::{Error, Request, Response};

#[fastly::main]
fn main(mut req: Request) -> Result {
    let shield = Shield::new("amsterdam-nl").unwrap();

// section visible
    if shield.running_on() {
        // Shield POP Execution
        // Overwrite the anchor hostname at the shield with the original client requested hostname
        if let Some(orig_host) = req.get_header("X-Orig-Host").cloned() {
            req.set_header("Host", orig_host);
        }
        Ok(req.send("origin_backend")?)
    } else {
        // Edge POP Execution
        // Capture the incoming hostname from the client
        if let Some(orig_host) = req.get_header("Host").cloned() {
            req.set_header("X-Orig-Host", orig_host);
        }

        // Set an anchor domain explicitly associated with your service,
        // allowing shielding to work under a pinned IP setup.
        req.set_header("Host", "example-service.freetls.fastly.net");

        Ok(req.send(shield.encrypted_backend().unwrap())?)
    }
// section-end visible
}
```

### Javascript

```js

async function handleRequest(event) {
  const req = event.request;
  const shield = new Shield('amsterdam-nl');

// section visible
  if (shield.runningOn()) {
    // Shield POP Execution
    // Overwrite the anchor hostname at the shield with the original client requested hostname
    const origHost = req.headers.get("X-Orig-Host");
    if (origHost) {
      req.headers.set("Host", origHost);
    }
    return await fetch(req, { backend: 'origin_backend' });
  } else {
    // Edge POP Execution
    // Capture the incoming hostname from the client
    const origHost = req.headers.get("Host");
    req.headers.set("X-Orig-Host", origHost);

    // Set an anchor domain explicitly associated with your service,
    // allowing shielding to work under a pinned IP setup.
    req.headers.set("Host", "example-service.freetls.fastly.net");

    return await fetch(req, { backend: shield.encryptedBackend() });
  }
// section-end visible
}
```

### Go

```go
package main

import (
	"context"
	"io"
	"github.com/fastly/compute-sdk-go/fsthttp"
	"github.com/fastly/compute-sdk-go/shielding"
)

func main() {
	fsthttp.ServeFunc(func(ctx context.Context, w fsthttp.ResponseWriter, r *fsthttp.Request) {
		shield, err := shielding.ShieldFromName("amsterdam-nl")
		if err != nil {
			fsthttp.Error(w, err.Error(), fsthttp.StatusInternalServerError)
			return
		}

// section visible
		if shield.IsRunningOn() {
			// Shield POP Execution
			// Overwrite the anchor hostname at the shield with the original client requested hostname
			origHost := r.Header.Get("X-Orig-Host")
			if origHost != "" {
				r.Header.Set("Host", origHost)
			}

			resp, err := r.Send(ctx, "origin_backend")
// section-end visible
			if err != nil {
				fsthttp.Error(w, err.Error(), fsthttp.StatusBadGateway)
				return
			}
			w.Header().Reset(resp.Header)
			w.WriteHeader(resp.StatusCode)
			io.Copy(w, resp.Body)
// section visible
			return
		}

		// Edge POP Execution
		// Capture the incoming hostname from the client
		origHost := r.Header.Get("Host")
		r.Header.Set("X-Orig-Host", origHost)

		// Set an anchor domain explicitly associated with your service,
		// allowing shielding to work under a pinned IP setup.
		r.Header.Set("Host", "example-service.freetls.fastly.net")

		shieldBackendName, err := shield.Backend(nil)
// section-end visible
		if err != nil {
			fsthttp.Error(w, err.Error(), fsthttp.StatusInternalServerError)
			return
		}

// section visible
		resp, err := r.Send(ctx, shieldBackendName)
// section-end visible
		if err != nil {
			fsthttp.Error(w, err.Error(), fsthttp.StatusBadGateway)
			return
		}
		w.Header().Reset(resp.Header)
		w.WriteHeader(resp.StatusCode)
		io.Copy(w, resp.Body)
	})
}
```

### Cpp

Shielding is not available in this version of the Fastly Compute C++ SDK.

### Enable or disable shielding for a single request

### Cdn Services

In VCL 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 `restart`ed). You can change this using a "recv" [VCL snippet](https://www.fastly.com/documentation/guides/full-site-delivery/fastly-vcl/about-fastly-vcl/#vcl-snippets)):

```vcl
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](https://www.fastly.com/documentation/reference/vcl/variables/#user-defined-variables) 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](https://www.fastly.com/documentation/guides/full-site-delivery/fastly-vcl/about-fastly-vcl/#vcl-snippets), because snippets are rendered within the `#FASTLY RECV` macro.

### Compute Services

In Compute services, shielding behavior is dictated entirely by your application code. If you need to enable or disable shielding for specific requests, such as for certain URL paths, simply evaluate those conditions in code and perform the fetch via the shield's encrypted tunnel or route directly to your own backend.

## 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](https://www.fastly.com/documentation/guides/getting-started/concepts/using-fastlys-global-pop-network). If the edge [POP](https://www.fastly.com/documentation/guides/getting-started/concepts/using-fastlys-global-pop-network) doesn't have the object, the request might still result in a cache HIT, but from the _shield_ [POP](https://www.fastly.com/documentation/guides/getting-started/concepts/using-fastlys-global-pop-network). 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](https://www.fastly.com/documentation/guides/getting-started/concepts/using-fastlys-global-pop-network) 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](https://www.fastly.com/documentation/guides/getting-started/concepts/using-fastlys-global-pop-network) will be from when the cached object was originally cached. First, start by understanding the possible values of `X-Cache`:

| X‑Cache                     | Meaning                                                                                                                                                                                                                                                                                                       | CHR implications |
| --------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------- |
| **<code>MISS, MISS</code>** | The 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, MISS`**             | The 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, HIT`**             | The 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, HIT`**              | The 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            |
| **`HIT`**                   | The object was found in cache, and the [POP](https://www.fastly.com/documentation/guides/getting-started/concepts/using-fastlys-global-pop-network) 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            |
| **`MISS`**                  | The object was not in cache, and the [POP](https://www.fastly.com/documentation/guides/getting-started/concepts/using-fastlys-global-pop-network) 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](https://www.fastly.com/documentation/guides/getting-started/concepts/using-fastlys-global-pop-network), and particularly hot objects may end up cached on every node in the [POP](https://www.fastly.com/documentation/guides/getting-started/concepts/using-fastlys-global-pop-network) (see [clustering](https://www.fastly.com/documentation/guides/full-site-delivery/fastly-vcl/clustering-in-vcl) 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](https://www.fastly.com/documentation/guides/getting-started/concepts/using-fastlys-global-pop-network) 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:

```http
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](https://www.fastly.com/documentation/guides/getting-started/concepts/using-fastlys-global-pop-network) and was not in the cache in either of them. `X-Served-By` lists the servers acting as the [delivery node](https://www.fastly.com/documentation/guides/full-site-delivery/fastly-vcl/clustering-in-vcl) in each [POP](https://www.fastly.com/documentation/guides/getting-started/concepts/using-fastlys-global-pop-network), in the order in which they processed the _response_. In this case, `cache-iad2120-IAD` (Dulles, Virginia) was the shield [POP](https://www.fastly.com/documentation/guides/getting-started/concepts/using-fastlys-global-pop-network) (closest to the backend), and therefore saw the response first, and `cache-sjc3120-SJC` (San Jose, California) was the edge [POP](https://www.fastly.com/documentation/guides/getting-started/concepts/using-fastlys-global-pop-network) (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:

```http
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:

```http
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](https://www.fastly.com/documentation/guides/getting-started/concepts/using-fastlys-global-pop-network), `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 few other parameters that can be taken into account too:

- Some Fastly POPs have interconnection points with cloud provider networks. Where a POP has a private network interconnect (PNI), requests from that POP to any host that is within that provider's network will typically flow over the interconnect and not via the public internet. 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.

| Provider                   | Cost savings                                                                                                                    | Availability                          |
| -------------------------- | ------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------- |
| Amazon Web Services        | None                                                                                                                            | Not applicable                        |
| Backblaze B2 Cloud Storage | [Free egress](https://www.fastly.com/documentation/guides/integrations/non-fastly-services/data-transfer-with-backblaze-b2)     | Any shield location                   |
| Google Cloud Platform      | [Discounted egress](https://www.fastly.com/documentation/guides/integrations/non-fastly-services/discounted-egress-from-google) | All GCP PNIs                          |
| Microsoft Azure            | [Free egress](https://www.fastly.com/documentation/guides/integrations/non-fastly-services/outbound-data-transfer-from-azure)   | AMS, BFI, CHI, DFW, IAD, and PAO only |

### Shield locations

The following POPs are suitable for shielding Fastly services:

## Shield POPs

| Location | POP | Capacity | Shield code | PNIs | Recommended for |
|----------|-----|----------|-------------|------|------------------|
| Amsterdam | AMS | Medium | amsterdam-nl | Amazon Web Services, Microsoft Azure, Google Cloud Platform | Microsoft Azure (west-europe), Google Cloud Platform (europe-west4) |
| Ashburn | IAD | Large | iad-va-us | Amazon Web Services, Microsoft Azure, Google Cloud Platform | Microsoft Azure (east-us), Amazon Web Services (us-east-1), Google Cloud Platform (us-east1), Google Cloud Platform (us-east4), Google Cloud Platform (us-east5) |
| Atlanta | PDK | Large | atlanta-ga-us | Amazon Web Services, Google Cloud Platform |  |
| Auckland | AKL | Small | auckland-akl | Microsoft Azure |  |
| Bogota | BOG | Medium | bog-bogota-co |  |  |
| Boston | BOS | Small | bos-ma-us |  |  |
| Brisbane | BNE | Small | brisbane-au |  |  |
| Brussels | BRU | Small | bru-brussels-be | Amazon Web Services, Google Cloud Platform |  |
| Cape Town | CPT | Small | cpt-capetown-za |  | Amazon Web Services (af-south-1) |
| Chennai | MAA | Small | maa-chennai-in |  |  |
| Chicago | CHI | Large | chi-il-us | Amazon Web Services, Microsoft Azure, Google Cloud Platform | Amazon Web Services (ca-central-1), Amazon Web Services (us-east-2), Google Cloud Platform (us-central1) |
| Christchurch | CHC | Small | chc-christchurch-nz |  |  |
| Copenhagen | CPH | Small | cph-copenhagen-dk | Amazon Web Services |  |
| Dallas | DFW | Large | dallas-tx-us | Amazon Web Services, Microsoft Azure, Google Cloud Platform | Google Cloud Platform (us-south1) |
| Delhi | DEL | Small | del-delhi-in | Amazon Web Services, Google Cloud Platform | Google Cloud Platform (asia-south2) |
| Denver | DEN | Medium | den-co-us | Google Cloud Platform | Google Cloud Platform (us-west3) |
| Dublin | DUB | Medium | dub-dublin-ie | Amazon Web Services | Amazon Web Services (eu-west-1) |
| Frankfurt | FRA | Large | frankfurt-de | Amazon Web Services, Microsoft Azure, Google Cloud Platform | Amazon Web Services (eu-central-1), Amazon Web Services (eu-central-2), Google Cloud Platform (europe-central2), Google Cloud Platform (europe-west3), Google Cloud Platform (europe-west6), Google Cloud Platform (europe-west8), Google Cloud Platform (me-west1) |
| Fujairah Al Mahta | FJR | Small | fjr-fujairah-uae | Amazon Web Services | Amazon Web Services (me-central-1), Amazon Web Services (me-south-1) |
| Helsinki | HEL | Small | hel-helsinki-fi | Google Cloud Platform | Google Cloud Platform (europe-north1) |
| Hong Kong | HKG | Small | hongkong-hk | Amazon Web Services, Microsoft Azure, Google Cloud Platform | Amazon Web Services (ap-east-1), Google Cloud Platform (asia-east2) |
| Houston | IAH | Large | iah-tx-us | Amazon Web Services |  |
| Hyderabad | HYD | Small | hyd-hyderabad-in | Amazon Web Services, Microsoft Azure |  |
| Johannesburg | JNB | Small | jnb-johannesburg-za |  |  |
| Kolkata | CCU | Small | ccu-kolkata-in | Amazon Web Services |  |
| Lisbon | LIS | Small | lis-lisbon-pt | Amazon Web Services |  |
| London | LCY | Large | london_city-uk | Amazon Web Services, Microsoft Azure, Google Cloud Platform | Amazon Web Services (eu-west-2), Google Cloud Platform (europe-west2) |
| London | LHR | Medium | london-uk | Amazon Web Services, Microsoft Azure, Google Cloud Platform |  |
| London | LON | Medium | lon-london-uk | Amazon Web Services |  |
| Los Angeles | BUR | Medium | bur-ca-us | Microsoft Azure, Google Cloud Platform | Microsoft Azure (west-us), Google Cloud Platform (us-west2), Google Cloud Platform (us-west4) |
| Madrid | MAD | Medium | mad-madrid-es | Amazon Web Services, Google Cloud Platform | Google Cloud Platform (europe-southwest1) |
| Manchester | MAN | Medium | man-manchester-uk | Amazon Web Services |  |
| Marseille | MRS | Medium | mrs-marseille-fr | Amazon Web Services, Google Cloud Platform |  |
| Melbourne | MEL | Medium | melbourne-au | Amazon Web Services, Microsoft Azure | Google Cloud Platform (australia-southeast2) |
| Miami | MIA | Medium | miami-fl-us | Google Cloud Platform |  |
| Milan | MXP | Medium | mxp-milan-it | Amazon Web Services, Google Cloud Platform | Amazon Web Services (eu-south-1) |
| Minneapolis | MSP | Medium | msp-mn-us |  |  |
| Montreal | YUL | Medium | yul-montreal-ca | Microsoft Azure |  |
| Mumbai | BOM | Medium | bom-mumbai-in | Amazon Web Services | Amazon Web Services (ap-south-1), Google Cloud Platform (asia-south1) |
| New York City | LGA | Medium | lga-ny-us | Amazon Web Services, Google Cloud Platform | Google Cloud Platform (northamerica-northeast1) |
| Newark | EWR | Medium | ewr-nj-us | Amazon Web Services, Microsoft Azure |  |
| Osaka | ITM | Medium | osaka-jp | Amazon Web Services, Microsoft Azure, Google Cloud Platform | Amazon Web Services (ap-northeast-3), Google Cloud Platform (asia-northeast2) |
| Oslo | OSL | Small | osl-oslo-no | Amazon Web Services |  |
| Paris | PAR | Large | paris-fr | Amazon Web Services, Google Cloud Platform | Amazon Web Services (eu-west-3), Google Cloud Platform (europe-west1), Google Cloud Platform (europe-west9) |
| Perth | PER | Small | perth-au |  |  |
| Portland | PDX | Small | pdx-or-us |  |  |
| Rio de Janeiro | GIG | Medium | gig-riodejaneiro-br | Microsoft Azure, Google Cloud Platform | Google Cloud Platform (southamerica-west1) |
| San Jose | SJC | Medium | sjc-ca-us | Amazon Web Services, Microsoft Azure, Google Cloud Platform | Amazon Web Services (us-west-1) |
| Sao Paulo | GRU | Medium | gru-saopaulo-br | Amazon Web Services, Microsoft Azure, Google Cloud Platform | Amazon Web Services (sa-east-1), Google Cloud Platform (southamerica-east1) |
| Seattle | BFI | Large | bfi-wa-us | Amazon Web Services, Microsoft Azure, Google Cloud Platform | Amazon Web Services (us-west-2), Google Cloud Platform (us-west1) |
| Seoul | ICN | Medium | icn-seoul-kr |  | Amazon Web Services (ap-northeast-2), Google Cloud Platform (asia-northeast3) |
| Singapore | SIN | Large | sin-singapore-sg | Amazon Web Services, Microsoft Azure, Google Cloud Platform | Amazon Web Services (ap-southeast-1), Amazon Web Services (ap-southeast-3), Google Cloud Platform (asia-southeast1), Google Cloud Platform (asia-southeast2) |
| Sofia | SOF | Small | sof-sofia-bg | Google Cloud Platform |  |
| Stockholm | BMA | Medium | stockholm-bma | Amazon Web Services, Google Cloud Platform | Amazon Web Services (eu-north-1), Google Cloud Platform (europe-north1) |
| Sydney | SYD | Medium | sydney-au | Amazon Web Services, Microsoft Azure, Google Cloud Platform | Microsoft Azure (australia-east), Amazon Web Services (ap-southeast-2), Google Cloud Platform (australia-southeast1) |
| Sydney | WSI | Medium | wsi-australia-au | Amazon Web Services, Microsoft Azure, Google Cloud Platform |  |
| Tokyo | NRT | Large | nrt-tokyo-jp | Amazon Web Services, Microsoft Azure, Google Cloud Platform | Amazon Web Services (ap-northeast-1), Google Cloud Platform (asia-east1), Google Cloud Platform (asia-northeast1) |
| Toronto | YYZ | Medium | yyz-on-ca | Microsoft Azure, Google Cloud Platform | Google Cloud Platform (northamerica-northeast2) |
| Vienna | VIE | Medium | vie-vienna-at | Amazon Web Services |  |

This is not a complete list of Fastly POPs. For a complete list, see the [POPs API](https://www.fastly.com/documentation/reference/api/utils/pops/), run <kbd>fastly pops</kbd> on your terminal, or see our [network map](https://www.fastly.com/network).
