Search code examples
cdncontent-delivery-network

Why isn't a CDN working as it's supposed to be?


I'm always confused by CDNs and whether they're doing their job effectively.

I know that in theory, their purpose is to shorten the latency between the server and the user.

However, I usually like to test things before I try. So I uploaded an image to imgur.com and then tested the speed of the image file on 2 sites:

First test: https://tools.keycdn.com/performance

Second test: https://tools.pingdom.com

I've selected New York as the testing location on the second website. So I hit test on the first one - it gave me about 200-300ms latency on New York. Then I hit test on the 2nd website and it also gave me a quite high latency like 300ms or so.

When I hit test the 2nd time it lowered down to 15-30ms of course because that's what a CDN should do.

The question is though that those 2 servers were in the same location, but it looks like the image didn't get cached at all. Why is this happening or what am I missing here? I thought if it's cached then it should already reduce the latency to ANY other requesting server/user in that area. Am I wrong?


Solution

  • A CDN besides trying to deliver your content fast (shorten the latency) can help you also to protect/secure your origin by not exposing it directly, check this post for an introduction about other benefits: what is a CDN?

    Regarding your test, there are many factors involved, for example, all new content (MISS) always will take more time to be served since it hasn't been cached, that content that as being already pre-fetched and cached (HIT).

    You could start by checking the headers, for example in a terminal run this:

    $ curl -I https://immortal.run/img/immortal.png
    

    You may see an output like this:

    HTTP/2 200
    date: Fri, 17 Aug 2018 07:51:20 GMT
    content-type: image/png
    content-length: 6757
    set-cookie: __cfduid=d0be8792ec1e81d223eaa9e05b780a8fa1534492280; expires=Sat, 17-Aug-19 07:51:20 GMT; path=/; domain=.immortal.run; HttpOnly
    last-modified: Thu, 07 Jun 2018 20:20:47 GMT
    access-control-allow-origin: *
    expires: Fri, 17 Aug 2018 11:51:20 GMT
    cache-control: public, max-age=14400
    x-github-request-id: 47B0:1A82:37CB24D:4D9A0A9:5B752825
    cf-cache-status: HIT
    accept-ranges: bytes
    strict-transport-security: max-age=15552000; includeSubDomains; preload
    x-content-type-options: nosniff
    expect-ct: max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct"
    server: cloudflare
    cf-ray: 44ba8e10cb6597aa-FRA
    

    Notice the header:

    cf-cache-status: HIT
    

    When the resource has been cached and served from the CDN it will be HIT if not it may be MISS, here you can see a list of more possible responses that apply to Cloudflare: https://support.cloudflare.com/hc/en-us/articles/200168266-What-do-the-various-Cloudflare-cache-responses-HIT-Expired-etc-mean-

    Now to test how fast a resource is loading you could use curl, Depending on your shell you may want to add the next function curl_time() into ~/.profile, ~/.zshrc or ~/.bashrc

    curl_time() {
        curl -o /dev/null -Ls -w " \
            time_namelookup:  %{time_namelookup}\n \
            time_connect:  %{time_connect}\n \
            time_appconnect:  %{time_appconnect}\n \
            time_pretransfer:  %{time_pretransfer}\n \
            time_redirect:  %{time_redirect}\n \
            time_starttransfer:  %{time_starttransfer}\n \
            ----------\n \
            time_total:  %{time_total}\n" "$1"
    }
    

    Then give a try with something like this:

    $ curl_time https://immortal.run/img/immortal.png
             time_namelookup:  0.133057
             time_connect:  0.144885
             time_appconnect:  0.200092
             time_pretransfer:  0.200299
             time_redirect:  0.000000
             time_starttransfer:  0.416685
             ----------
             time_total:  0.418580
    

    In my subsequent requests, I got it delivered faster time_total: 0.093495:

    $ curl_time https://immortal.run/img/immortal.png
             time_namelookup:  0.004583
             time_connect:  0.019833
             time_appconnect:  0.067715
             time_pretransfer:  0.067839
             time_redirect:  0.000000
             time_starttransfer:  0.091393
             ----------
             time_total:  0.093495
    

    If just want to get the total_time repeditely you could try this:

    $ for i in {1..3}; \
    curl -sL -w "%{time_total}\n" -o /dev/null https://immortal.run/img/immortal.png
    

    Analyzing the headers and the response times is a good start point for checking how the CDN is behaving.