Skip to content

Instantly share code, notes, and snippets.

@AlexAtkinson
Last active June 7, 2023 01:26
Show Gist options
  • Save AlexAtkinson/7246dfafa76973c6cc60ac5fa5ce0858 to your computer and use it in GitHub Desktop.
Save AlexAtkinson/7246dfafa76973c6cc60ac5fa5ce0858 to your computer and use it in GitHub Desktop.
Some Fastly ProTips

Fastly ProTips

NOTE: This needs some organization work...

Certificate (TLS) Options

Pricing

The certificate pricing and options are not laid out clearly. As of July 30, 2020:

“In addition to further clarify your concerns with pricing with Fastly TLS the first five domains are free. This could be a mix of apex domains and wildcards that add up to five. With the shared certificates if you purchase one for the wildcard for $275, the apex is included as well in that pricing. If you purchase the shared certificate for $100 this would include one domain.” - Marita

“Yes Fastly TLS does cover wildcard domains. Fastly TLS offers the frist 5 domains for free so both the apex domain and wildcard (assuming those are the only two domains) would be free.” - Ben

WARNING: The above "Free" (5 or less) certs exist as SAN on a shared cert, so there will be random other entries from other customers on the cert as well. No, there is NO CONTROL over what types of domains may appear there. Some may be offensive. This is the business risk associated with these "free" options.

You can programatically retrieve the SANs available on a cert with the following command:

domain='nike.com'
printf "Q" | openssl s_client -connect $domain:443 -servername $domain 2>&1 | \
openssl x509 -in /dev/stdin -text -noout -certopt \
no_header,no_version,no_serial,no_signame,no_validity,no_subject,no_issuer,no_pubkey,no_sigdump,no_aux 2>&1 | \
grep -o -P "DNS:.*" | sed 's/, /\n/g' | tr -d "DNS:"

Beyond the "free" option, you can purchase a $600/mo enterprise cert[31], or upload your own cert. Note that 3rd party certs must be uploaded via their ssl upload tool

Shielding

  • Always configure a shield.
  • Select the shield location CLOSEST to your origin.
  • POP traffic to your SHIELD traverses a private, low-latency network.
  • Understand Request Collapsing & Hit-For-Pass
    • This is a key desirable as it prevents surge traffic, such as that incurred when running campaigns, from overloading your backend.
      • WARNING: Be cautious of disabling request collapsing by issuing a return(pass) from within vcl_recv.

Max Connection (To Origin)

You can limit the connections a single Fastly cache server will make to a specific origin server.

Image Optimization

Fastly's IO Service is SUPERIOR. It facilitates the dynamic use of IO against images by simply affixing the IO controls as query parameters on the resource URL. When compared to other CDNs such as Akamai, which require all "versions" of the asset to be created and uploaded piecemeal, it becomes a very attractive feature.

  • Upload the max resolution that your product requires, and leverage IO controls to downsize/scale the asset.
  • DO NOT upload pre-optimized images, but instead use high-quality assets, and rely on the 'quality' control to adjust as required.

Purging

NOTE: Purge all requests are logged in event logs. Key purge requests cannot be logged.

Anti-Patterns

  • Using PURGE ALL
  • Not batching requests when forced to use PURGE ALL against a service.
  • If a system would issue a purge-all request, then it MUST submit it to a queue which will collapse multiple requests for a given period.

ADVANCED: Powederhorn

The Fastly purge system is called Powderhorn, and leverages a bimodal multicast protocol propagate its messages across POPs.

Refs:

Event Logs

There's a good amount of activity that doesn't surface in Fastly service logs, including PURGE ALL events. Such events must be accessed via the web console, or via the Events API. If PURGE ALL Requests are required in the service logs, then they must retrieved and then forwarded into the service log collector.

There is no existing solution for this, but you'd want to use a Lambda for it.

Service Configuration

See here (removed) for the scripts. This repository includes such treasures as:

  • IP Whitelisting
  • Image Optimization
  • Two SEO Redirect Handling Options:
    • strings (Handles special character encodings such as %20 and %3F)
    • regex
  • APEX redirects
  • User Agent Parsing
  • API Purge Logging
  • Basic Authentication
  • Setting 'Access-Control-Allow-Origin: yes' for allowed hosts only
  • Sumologic Logging Configurations
  • Plus field extraction rules
  • Ready to go Main.vcl examples for:
    • Web Server
    • GraphQL
    • DAM
    • URL Facadeing

See the Pro-Tips section for:

  • Cloning a Service
  • Adjusting Service Limits

Stripping Query Strings

Get the code here (removed), and see the Fiddle (removed) to run these query string manipulations. Uses:

  • Busting Cache Busting
  • Mitigating injection attacks
  • Reducing URL sprawl

Fastly Pro-Tips

  • Fastly will clone an entire service for you on demand. Include the service Name, ID, and Version to clone.
  • Fastly /CAN/ adjust service limits for you, but will assess risk to their operations first.
  • Domains per Service - Defaults to 20, but CAN be increased on request. Best reserved for non-prod services where evaluation counts will be lower.

Service Pre-Launch Validation

Warming Specific POPs

Prior to launching a site, it is preferable to pre-warm the Fastly cache layer, which means at minimum crawling the sitemap.xml. Some considerations here are whether or not you use a shield, and whether or not you're in the same region as the target market. If you're launching a global site, with equally dispersed audience, you will be using a shield, no doubt, and crawling the sitemap would be the best that you could do. If you're launching a site for a specific region, you can pre-warm specific POPs in the area, which will mitigate misses, and the potential of your origin falling over. This is the use case for this script[48]. To accommodate the script, you will have to get the hostnames for the POPs in the region. AFAIK, unless you're in the region and can discover it via the diagnostics method below, you'll have to open a ticket with Fastly.

Diagnostics

The Diagnostics Header

Curling Fastly services while setting the debug header enables the service to respond with additional debug info. See the doc for more details. https://docs.fastly.com/guides/debugging/checking-cache#using-a-fastly-debug-header-with-curl[49] Example:

curl -svo /dev/null -H "Fastly-Debug: true" www.fastly.com

Advanced

Cache Hosts

Cache hosts, as indicated by the 'X-Served-By' header (minus the trailing '-XXX') all have the full hostname of .hosts.fastly.net.

Finding the IP of a particular cache server.

[root@host ~]# curl -sSkIL 2>&1 | grep X-Served-By
X-Served-By: cache-mdw17353-MDW
[root@host ~]# nslookup cache-mdw17353.hosts.fastly.net[52]
Server: 10.98.4.75
Address: 10.98.4.75#53
Non-authoritative answer:
hosts.fastly.net[51] dname = hosts.secretcdn.net[53].
cache-mdw17353.hosts.fastly.net[52] canonical name = cache-mdw17353.hosts.secretcdn.net[54].
Name: _cache-mdw17353.hosts.secretcdn.net_
Address: 157.52.75.53# <<<<<< CACHE IP ADDRESS

Hitting Specific POPs

Point of Presence = POP = 'X-Served-By' header value. This is useful for working with HIT/MISS, TTL, or content validation.

curl -svo /dev/null -H "Fastly-Debug: true" www.fastly.com -x .hosts.fastly.net:80

Example:

user@host:../~$ curl -sSkIL -H "Host: <[55]your domain>" https://*cache-mdw17346*.hosts.fastly.net/path/to/resource.foo
HTTP/1.1 200 OK
Content-Type: application/javascript; charset=UTF-8
Server: nginx/1.12.1
X-Powered-By: Express
X-Frame-Options: DENY
X-Content-Type-Options: nosniff
Last-Modified: Fri, 13 Sep 2019 19:41:57 GMT
ETag: W/"22c687-16d2c257d76"
Cache-Control: max-age=31536000, public
Expires: Thu, 17 Sep 2020 15:33:01 GMT
Content-Length: 2279047
Accept-Ranges: bytes
Date: Wed, 18 Sep 2019 15:33:02 GMT
Via: 1.1 varnish
Age: 2
Connection: keep-alive
X-Served-By: *cache-mdw17346-MDW*
X-Cache: MISS
X-Cache-Hits: 0
X-Timer: S1568820781.845812,VS0,VE1984
Vary: Accept-Encoding
Strict-Transport-Security: max-age=604800;includeSubDomains

Disable Shielding

Useful for seeing exactly what your request/response looks like when a POP communicates directly with the origin. NOTE: This flag is disabled by the Main.vlc's special treatment of POP assignment.

curl -svo /dev/null-H"Fastly-Debug: true"-H"Fastly-No-Shield: true" www.fastly.com

Surrogate-Key Example

Disabling Shielding is useful when diagnosing Surrogate-Key issues.

[user@host ~]# curl -IH "Fastly-Debug: true" -H "Fastly-No-Shield: true" "<[56]URL>"
HTTP/1.1 200 OK
...
Surrogate-Key: inventory cart <userIdHash>
...
Via: 1.1 varnish
...
Fastly-Debug-Path: (D cache-chi21136-CHI 1568051322) (F cache-chi21136-CHI 1568051322) (D cache-lhr6334-LHR 1568051321) (F cache-lhr6334-LHR 1568051321)
Fastly-Debug-TTL: (M cache-chi21136-CHI - - 0) (M cache-lhr6334-LHR - - 0)
Fastly-Debug-Digest: 6bea4765138367ba1e560ae04625c6c563c0dbae8a8f6630ce4feb4fece1b1b1
X-Served-By: cache-lhr6334-LHR, cache-chi21136-CHI
X-Cache: Miss from cloudfront, MISS, MISS
X-Cache-Hits: 0, 0
X-Timer: S1568051321.314909,VS0,VE372

Here we can see that the Surrogate-Key header is being set.

Compression

Useful for testing GZIP.

[root@host ~]# curl -SkIL -H "Fastly-Debug: true" https://www.domain.com/ 2>&1 | grep Length
Content-Length: 1204847
[root@host ~]# curl -SkIL --compressed -H "Fastly-Debug: true" https://www.domain.com 2>&1 | grep Length
Content-Length: 99739

Forced Name Resolution

The --resolve flag forces CURL to use a custom IP for a given hostname and port. Useful for validating a service setup in Fastly without pointing DNS at it, or adjusting your /etc/hosts file. Note: Depends on the service being setup, AND the domain having been added to a shared or hosted cert in your Fastly account.

curl -SkIL --resolve 'www.domain.com:443:157.52.75.53' https://www.domain.com

Image Optimization

You can verify IO functionality by running a curl with an IO triggering query string, as shown below. Do NOT include a username/password if you're hitting a PROD endpoint. See here for more on the Fastly-IO-Info header.

$ curl -svo /dev/null -H "Fastly-Debug: true" https://www.domain.com/uri/to/image.jpg?width=200
...< Fastly-Io-Info: ifsz=46994 idim=1600x1200 ifmt=png ofsz=1924 odim=200x150 ofmt=png
< Fastly-Stats: io=1
...
< Fastly-Debug-Path: (D cache-chi21131-CHI 1562183657) (F cache-chi21121-CHI 1562183657) (D cache-lhr6324-LHR 1562183657) (F cache-lhr6341-LHR 1562181547)< Fastly-Debug-TTL: (M cache-chi21131-CHI - - 2110) (H cache-lhr6324-LHR 84290.041 43200.000 2110)
< Fastly-Debug-Digest: 6207eadf45e601b507cda9778a57fe35df9db34084f419ec850c1bb2cc31bd54
< X-Served-By: cache-lhr6324-LHR, cache-chi21131-CHI
< X-Cache: HIT, MISS
< X-Cache-Hits: 2, 0
< X-Timer: S1562183657.983333,VS0,VE388
...

Benchmarking IO

Save the following as fastly-io-bench.sh.

NOTE: The 'result' simply checks that there was no IO warning. This needs a bit more polish. cms.domain.com[58] is hardcoded. This was just a quick tool in a moment...

#!/usr/bin/env bash
env="$1"
endpoint="$2"
asset='/path/to/some/image.jpg'
title="IO Benchmark For: ${endpoint}"
printf"\n${title}"
printf'\n%*s'"$((${COLUMNS}-$((${COLUMNS}-$(wc -c<<<$title)+1))))"| tr' '-
origSize=$(curl -sI https://_cms.domain.com_${asset} 2>&1 | grep -i 'Content-Length' | awk '{print $2}' | tr -d '\r\n')
foriin50 100 200 400 600 800 1000 1200 1400 1600 1800 2000;do
result=pass
ts=$(date +%s%N)
curl -svo /dev/null-H"Fastly-Debug: true"${endpoint}${asset}?width=${i} 2>&1 | grep -q'Fastly-Io-Warning'&& result=fail
ioSize=$(curl -sI ${endpoint}${asset}?width=${i} 2>&1 | grep -i'Content-Length'| awk'{print $2}'| tr -d'\r\n')
tt=$((($(date +%s%N) - $ts)/1000000))
printf"\nIO (${i}px Wide) Total Time: $tt ms - $result - Original: ${origSize} B - Optimized: ${ioSize} B"
done
printf"\n"

Example Output:

user@host:../~$ ./fastlyIOBench.sh prod https://domain.com
IO Benchmark For: https://domain.com_
------------------------------------
IO (50px Wide) Total Time: 579 ms - pass - Original: 824072 B - Optimized: 1406 B
IO (100px Wide) Total Time: 588 ms - pass - Original: 824072 B - Optimized: 2001 B
IO (200px Wide) Total Time: 593 ms - pass - Original: 824072 B - Optimized: 3536 B
IO (400px Wide) Total Time: 583 ms - pass - Original: 824072 B - Optimized: 8558 B
IO (600px Wide) Total Time: 603 ms - pass - Original: 824072 B - Optimized: 15959 B
IO (800px Wide) Total Time: 621 ms - pass - Original: 824072 B - Optimized: 25169 B
IO (1000px Wide) Total Time: 679 ms - pass - Original: 824072 B - Optimized: 36694 B
IO (1200px Wide) Total Time: 1021 ms - pass - Original: 824072 B - Optimized: 51697 B
IO (1400px Wide) Total Time: 736 ms - pass - Original: 824072 B - Optimized: 67783 B
IO (1600px Wide) Total Time: 665 ms - pass - Original: 824072 B - Optimized: 85003 B
IO (1800px Wide) Total Time: 668 ms - pass - Original: 824072 B - Optimized: 103754 B
IO (2000px Wide) Total Time: 679 ms - pass - Original: 824072 B - Optimized: 123239 B

SSLv3 Alert Handshake Failure

Cause & Resolution The Certificate hostname and SNI hostname are not aligned. Ensure they're aligned.

Install Ruby with rbenv

Centos 7

Install Pre-requisites

sudo yum -y install git-core zlib zlib-devel gcc-c++ patch readline readline-devel libyaml-devel libffi-devel openssl-devel make bzip2 autoconf automake libtool bison curl sqlite-devel

Install rbenv NOTE: This will issue an error due to 'rbenv' not existing within your PATH. Continue on.

curl -sL https://github.com/rbenv/rbenv-installer/raw/master/bin/rbenv-installer | bash -

Configure your ~/.bashrc file

echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bashrc
echo 'eval "$(rbenv init -)"' >> ~/.bashrc
source ~/.bashrc

Verify rbenv installation. The following should report that it is a function.

type rbenv

Setup Ruby using rbenv

rbenv install -l         # list all avialable versions
rbenv install 2.7.0      # install version 2.7.0 (subsitute latest version)
rbenv global 2.7.0       # Use Ruby 2.7.0 globally (substitute latest version)
rbenv versions           # see installed versions

rbenv cheatsheet

Install FastlyCTL

gem install fastlyctl

Some useful commands

Login

fastlyctl login

Watch stats on a service

fastlyctl watch --s <serviceId>

Download service configuration NOTE: This is required to work with a service

fastlyctl download --s <serviceId>

Create 5 blank services

for i in {1..5}; do fastlyctl create_service "Market_Blank-$i" ; done

Clone the config from a template service to the five blanks created above

svc=1; for i in <serviceId1> <serviceId2> <serviceId3> <serviceId4> <serviceId5>; do fastlyctl clone --v 6 <templateServiceId> $i; fastlyctl domain --s $i create blank${svc}.foo.com; (( svc++ )) ; fastlyctl activate --s $i ; done

VCL Examples

seo_redirect_GENERIC-1-of-3.vcl.template

# Name        : seo_redirect-GENERIC-1-of-3_recv.vcl
# Subroutine  : init
# Priority    : 100
#
# NOTE: Name 1-of-3 to qualify that suite of redirects if desired.
#       For example you may have different snippets for different properties/cammppaigns/etc.
#       2-of-3, and 3-of-3 will catch all redirects from all snippets generated from the 1-of-3 template.
#
# REQUIRES:
#   - All THREE redirect snippets
#     - 1-of-3 CUSTOMIZED snippet with your redirects
#     - 2-of-3 GENERIC
#     - 3-of-3 GENERIC

table seo_redirects {
  "/source1": "/dest1",
  "/source2": "/dest2"
}

seo_redirect_GENERIC-2-of-3.vcl

# Name        : seo_redirect_<CC>-1-of-2.vcl
# Subroutine  : vcl_recv
# Priorty     : 0
#
# REQUIRES:
#   - All THREE redirect snippets

if (table.lookup(seo_redirects, req.url.path)) {
  error 799 "redirect";
}

seo_redirect_GENERIC-3-of-3.vcl

# Name        : seo_redirect_<CC>-2-of-2.vcl
# Subroutine  : vcl_error
# Priority    : 100
#
# REQUIRES:
#   - All THREE redirect snippets

if (obj.status == 799 && obj.response == "redirect") {
  set obj.status = 302;
  set obj.response = "Moved Temporarily";
  set obj.http.Location = "https://" + req.http.host + table.lookup(seo_redirects, req.url.path) + if(req.url.qs != "", "?" + req.url.qs, "");
  return (deliver);
}

seo_redirect_REGEX-1-of-2.vcl

# Name        : seo_redirect_REGEX_1-of-2.vcl
# Subroutine  : vcl_recv
# Priorty     : 0
#
# REQUIRES:
#   - BOTH redirect snippets
#
# FASTLY REGEX REFERENCE:
#   https://docs.fastly.com/en/guides/vcl-regular-expression-cheat-sheet

if (req.url ~ "^/to/be/redirected/") {
  error 799 "redirect example 1";
} else if (req.url ~ "foo_(\d+)\.(\d+)") {
  error 799 "redirect example 2";
}

seo_redirect_REGEX-2-of-2.vcl

# Name        : seo_redirect_REGEX-2-of-2.vcl
# Subroutine  : vcl_error
# Priority    : 100
#
# REQUIRES:
#   - Both REGEX redirect snippets

if (obj.status == 799 && obj.response == "redirect example 1") {
  set req.url = "/redirected-1/" +  if(req.url.qs != "", "?" + req.url.qs, "");
} else if (obj.status == 799 && obj.response == "redirect example 2") {
  set req.url = "/redirected-2" +  if(req.url.qs != "", "?" + req.url.qs, "");
}

if (obj.status == 799) {
  set obj.status = 302;
  set obj.response = "Moved Temporarily";
  set obj.http.Location = "https://" + req.http.host + req.url;
  return (deliver);
}

Serve backend resource with a different path

# SEO & other uses:
#   - include 3rd party resources under own domain
#   - mask ads/trackers from blockers (source based)
#   - facilitate multiple url based origins

# Name         : url_facades.vcl
# Description  : Serve a resource from a different url
# Subrouteine  : vcl_recv
# Priority     : 100

if (req.url == "/foo") {
    set req.url = "/content/dam/foo.jpg";
} else if (req.url == "/bar") {
    set req.url = "/content/dam/bar.jpg";
}

if (req.url == "/googlebasdf.html") {
    set req.url = "/content/dam/sitename/gsc/googlebasdf.html";
}

BASH Scripts

PURGE

#!/usr/bin/env bash
# the_purge.sh
# ------------
# NOTE: Sleeps for 15 seconds to mitigate origin overload
#
service_urls=(
  https://serviceurl.com
)

echo -e "\nNOTE: This script purges alphabetically."
for i in ${service_urls[@]}; do
  echo -e "\nPurging: $i..."
  curl -sS -X PURGE -H Fastly-Key:${api_key} $i
  echo -e "Sleeping for 15s..."
  sleep 15
done

Image Optimization Benchmark

#!/usr/bin/env bash
# fastly_io_bench.sh
# ./fastly_io_bench.sh prod www.prod.foo.com
# Update CMS Backend below to enable the script to get the original file
env="$1"
endpoint="$2"
asset='/content/dam/some/image/file.jpg'
title="IO Benchmark For: ${endpoint}"
printf "\n${title}"
printf '\n%*s' "$((${COLUMNS}-$((${COLUMNS}-$(wc -c<<<$title)+1))))" | tr ' ' -
origSize=$(curl -sI https://cms.${env}.backend.com${asset} 2>&1 | grep -i 'Content-Length' | awk '{print $2}' | tr -d '\r\n')
for i in 50 100 200 400 600 800 1000 1200 1400 1600 1800 2000; do
  result=pass
  ts=$(date +%s%N)
  curl -svo /dev/null -H "Fastly-Debug: true" ${endpoint}${asset}?width=${i} 2>&1 | grep -q 'Fastly-Io-Warning' && result=fail
  ioSize=$(curl -sI ${endpoint}${asset}?width=${i} 2>&1 | grep -i 'Content-Length' | awk '{print $2}' | tr -d '\r\n')
  tt=$((($(date +%s%N) - $ts)/1000000))
  printf "\nIO (${i}px Wide) Total Time: $tt ms - $result - Original: ${origSize} B - Optimized: ${ioSize} B"
done
printf "\n"

Regional Warming

#!/usr/bin/env bash
# warm-fastly-cache.sh
# --------------------------------------------------------------------------------------------------
# HELP
# --------------------------------------------------------------------------------------------------
help="\

NAME
        warm-fastly-cache.sh

SYNOPSIS
        ${0##*/} [-h] [-d \e[04mdomain\e[0m] [-c \e[04mcache\e[0m>

DESCRIPTION
        Crawls the sitemap.xml of the specified domain against the specified cache.

        -h    Print this menu.

        -d \e[04mdomain\e[0m
              Specify the domain to crawl. The script will automatically parse the sitemap.xml
              located at the root of the domain. (<loc></loc>)

        -c \e[04mcache\e[0m
              Specify the fastly cache to warm.
                  Example: cache12345.hosts.fastly.net
              See here for more on discovering cache hosts:
                  https://confluence.criticalmass.com/display/GT/Fastly#Fastly-CacheHosts

EXAMPLES
        ./warm-fastly-cache.sh -d www.domain.com -c cache-mdw17353.hosts.fastly.net

"
printHelp() {
  echo -e "$help" >&2
}
if [[ "$1" == '-h' ]]; then
  printHelp
  exit 1
fi

# --------------------------------------------------------------------------------------------------
# ARGUMENTS
# --------------------------------------------------------------------------------------------------
OPTIND=1
while getopts "hd:c:" opt; do
  case $opt in
    h)
      printHelp
      exit 0
      ;;
    d)
      arg_d='set'
      arg_d_val="$OPTARG"
      ;;
    c)
      arg_c='set'
      arg_c_val="$OPTARG"
      ;;
    *)
      echo -e "\e[01;31mERROR\e[00m: Invalid argument!"
      printHelp
      exit 1
      ;;
  esac
done
shift $((OPTIND-1))

# --------------------------------------------------------------------------------------------------
# SANITY
# --------------------------------------------------------------------------------------------------
if ! dig +noall +answer $arg_d_val | grep -v -e '^$' >/dev/null 2>&1; then
  echo -e "\e[01;31mERROR\e[00m: Specified domain does not exist!\n"
  exit 1
fi

if ! grep -q '<loc>.*</loc>' sitemap.xml; then
  echo -e "\e[01;31mERROR\e[00m: sitemap.xml does NOT contain <loc></loc> child entries to parse!\n"
  exit 1
fi

# --------------------------------------------------------------------------------------------------
# VARS
# --------------------------------------------------------------------------------------------------
host="$arg_d_val"
cache="$arg_c_val"
urls=$(curl -sS https://${host}/sitemap.xml | grep -o -P '(?<=<loc>).*?(?=</loc>)' | sed "s/https:\/\/${host}//g")
urlCount=$(wc -w <<< $urls)
warmCount=0

# --------------------------------------------------------------------------------------------------
# MAIN OPERATIONS
# --------------------------------------------------------------------------------------------------
echo -e "\n\nWARMING: ${host} on Fastly POP ${cache}..."
# WARM CACHE -- SILENT
for url in $urls; do
  curl -sSk -o /dev/null --max-time 0.25 -H "Host: ${host}" https://${cache}${url} > /dev/null 2>&1
done

# VERIFY CACHE WARMTH
for url in $urls; do
  responseCode=$(curl -sSkIf -o /dev/null -w "%{http_code}" -H "Host: ${host}" https://${cache}${url})
  if [[ "$responseCode" =~ 200|301 ]]; then
    ((warmCount++))
  else
    echo "ERROR: $responseCode - $url"
  fi
done

echo -e "\nURLS: ${urlCount}"
echo -e "WARM: $warmCount"
echo -e "\n"

DEBUGGING

Debug Header

curl -svo /dev/null -H "Fastly-Debug: true" https://www.fastly.com

Cache Servers

[root@host ~]# curl -SkIL https://www.fastly.com  2>&1 | grep X-Served-By
X-Served-By: cache-sjc3147-SJC, cache-mdw17370-MDW
[root@host ~]# nslookup cache-mdw17364.hosts.fastly.net
Server:   10.98.4.75
Address:  10.98.4.75#53

Non-authoritative answer:
hosts.fastly.net  dname = hosts.secretcdn.net.
cache-mdw17364.hosts.fastly.net canonical name = cache-mdw17364.hosts.secretcdn.net.
Name: cache-mdw17364.hosts.secretcdn.net
Address: 157.52.75.64 # <<<<<<<<<<<<<<< The Cache Server IP

CACHE SERVER NAMES == "x-served-by: cache-mdw17346-MDW" MINUS the trailing -XXX ie: cache-mdw17346 FQDN: cache-mdw17346.hosts.fastly.net

Hit specific cache servers

curl -svo /dev/null -H "Fastly-Debug: true" www.fastly.com -x <cache-node>.hosts.fastly.net:80

Send request to specific cache server

user@host:../~$ curl -sSkIL -H "Host: www.fastly.com" https://cache-mdw17346.hosts.fastly.net/js/main.bundle.adf89c82d7525cd029b8.js

Disabling Shielding

curl -svo /dev/null -H "Fastly-Debug: true" -H "Fastly-No-Shield: true" www.fastly.com

Compression Checking

[root@host ~]# curl -SkIL --compressed https://www.fastly.com 2>&1 | grep Length
Content-Length: 11527
[root@host ~]# curl -SkIL https://www.fastly.com 2>&1 | grep Length
Content-Length: 50187

Validating IO

curl -SkIL -H "Fastly-Debug: true" https://www.fastly.com/assets/io/1-95dedb9c9cbad11892a73a34c42611d85270c0499d5abdb191e393b84ed19f38.jpg?width=400

VCL: Logging Purges

if (req.method == "FASTLYPURGE") {
   log {"syslog "} req.service_id {" Sumologic :: "} "[" strftime({"%d/%b/%Y:%H:%M:%S %z"}, time.start) "] " req.request " - URL: " json.escape(req.url) ", Client IP: " req.http.Fastly-Client-IP;
}

VCL : Logging Format

%h %l %u %t %A %{Fastly-Orig-Host}i %{Host}i "%r" %{regsub(fastly_info.state, "^(HIT-(SYNTH)|(HITPASS|HIT|MISS|PASS|ERROR|PIPE)).*", "\\2\\3") }V %>s %b

Superlog

set req.http.X-Shield = "0"; if (req.http.fastly-ff) { set req.http.X-Shield = "1"; }
log {"syslog  xxxxx logging-endpoint :: "}
{" client_ip="}     req.http.Fastly-Client-IP
{" timestamp="}     now.sec
{" reqmethod="}     req.request
{" host="}          req.http.host
{" url="}           {"""}req.url{"""}
{" contenttype="}   resp.http.content-type
{" status="}        resp.status
{" response="}      {"""}resp.response{"""}
{" infostate="}     fastly_info.state
{" age="}           resp.http.age
{" setcookie="}     {"""}resp.http.Set-Cookie{"""}
{" size="}          resp.bytes_written
{" pop="}           server.datacenter
{" shield="}        req.http.x-shield
{" fastlyregion="}  server.region
{" clientcontinent="}     geoip.continent_code
{" clientcountry="}       geoip.country_code
{" clientregion="}        geoip.region
{" referrer="}      {"""}req.http.referer{"""}
{" useragent="}     req.http.Orig-User-Agent
{" hits="}          obj.hits
{" restarts="}      req.restarts
{" elapsed="}       time.elapsed.msec
{" ttl="}           obj.ttl
{" grace="}         obj.grace;
{" serving stale content="} fastly_info.state;

server.identity == cachenode and POP

See here for more: https://docs.fastly.com/guides/performance-tuning/shielding

Use req.http.fastly-ff to test if the node is in a shield POP. If it's set the request has passed through another POP already.

Example:

  # Rewrite req.url when hitting a specific backend.
  sub vcl_miss { if (req.backend == F_special_origin) { set bereq.url = regsub(bereq.url, "^/p/special/", "/"); } }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment