en-usCDN Planet BlogThe CDN Planet blog has articles related to Content Delivery Networks. News, tips, how-to and market developments.2024-02-12T01:00:00+01:00https://www.cdnplanet.com/Aaron PetersHow to Improve TTFB on Cloudflare with Speculation Rules2023-10-25T02:00:00+02:00https://www.cdnplanet.com/blog/how-improve-ttfb-on-cloudflare-with-speculation-rules/Learn about the Speculation Rules API and how CDN Planet used this new browser technology to reduce Time to First Byte on its Cloudflare powered website.
<!--
TODO:
- Post on Twitter > add to front matter: tweetId: 'xxx' > build > publish
- Changelog
- Quora, reddit, HN ... get the word out
-->
<div class="toc-container">
<div>
<p>
If your website is on Cloudflare and the HTML is not served from edge cache,
your Time to First Byte (TTFB) is probably high for some/many users.
High TTFB is bad for user experience and SEO (Core Web Vitals).
</p>
<p>
There are many ways to solve high TTFB problems on Cloudflare and Speculation Rules is a new option to explore.
<strong>Speculation Rules</strong> is a browser API to prefetch/prerender the page that is likely visited next, and it's coming full to Chrome/Edge browsers soon.
Speculation Rules can help speed up any website on any CDN, not just Cloudflare.
</p>
<p>
This article is a <strong>case study</strong> of how Speculation Rules improved the TTFB of the CDN Planet website by hundreds of milliseconds for ~20% of pageviews for non-landing pages 🥳
</p>
<p>
Read on to learn <strong>how to improve TTFB on Cloudflare with Speculation Rules</strong>.
</p>
<!-- <p>
<i>Coming soon: a blog post about tracking effectiveness of Speculation Rules in Google Analytics.</i>
</p> -->
</div>
<div class="toc">
<strong>Table of contents</strong>
<ul>
<li><a href="#workers-sites">High TTFB on Cloudflare Workers Sites</a></li>
<li><a href="#intro">Introduction to Speculation Rules</a></li>
<li><a href="#our-implementation">How We Use Speculation Rules</a></li>
<li><a href="#impact">Impact of Speculation Rules on TTFB</a></li>
<li><a href="#cloudflare-prefetch-urls">Cloudflare's Prefetch URLs vs Speculation Rules</a></li>
<li><a href="#learnings">Learnings & Tips</a></li>
</ul>
</div>
</div>
<!--
the li must have position: absolute;
<img class="author-profile-image br-100 mr2" src="https://media.licdn.com/dms/image/D4E03AQF6WzkwxU7zvA/profile-displayphoto-shrink_100_100/0/1674830596103?e=1700697600&v=beta&t=YR8Dqfwz8nxStI7dQJBxAf0gXgH5EE8NiNUdPuuUxRM" alt="Aaron Peters" width="52" height="52" style="
border-radius: 50%;
position: relative;
top: -0.25em;
z-index: 100;
border: 2px solid var(--primary-700);
filter: drop-shadow(0px 1px 6px #00363c);
">
the next li has margin-left: 80px; and author name as contents
-->
<h2 id="workers-sites">High TTFB on Cloudflare Workers Sites</h2>
<p>
The CDN Planet website has many pages (thousands) and a truly global audience.
We use Eleventy to generate our static site and push all site resources to Cloudflare using <a href="https://developers.cloudflare.com/workers/configuration/sites/" class="external-link">Workers Sites</a>, the predecessor of Cloudflare Pages.
</p>
<p>
All site resources are stored in Workers KV, Cloudflare's distributed key-value store.
The Cloudflare Docs site describes <a href="https://developers.cloudflare.com/kv/learning/how-kv-works/" class="external-link">how KV works</a>:
</p>
<blockquote>
KV is a global, low-latency, key-value data store. It stores data in a small number of centralized data centers, then caches that data in Cloudflare's data centers after access.<br>
...<br>
While reads are periodically revalidated in the background, requests which are not in cache and need to hit the centralized back end can experience high latencies.
</blockquote>
<p>
And high latencies we have.
Our HTML may be stored in edge cache for up to an hour, but due to the long tail nature of our content, users across the globe and <a href="https://www.cdnplanet.com/cdns/cloudflare/#network">Cloudflare's many POP locations</a>, the cache hit ratio for HTML is only ~35% 😞
</p>
<p>
We measure how long it takes to read the HTML from KV or edge cache from within our Worker:
in the past 3 weeks, the daily <em>average</em> 'edge read time' varied between 108 ms and 288 ms and that time goes straight into TTFB and Largest Contentful Paint.
That average is based on edge cache reads and reads from KV and assuming edge cache reads are very fast, the average 'KV read time' is surely higher than those 108 and 288 ms.
</p>
<p>
Cloudflare Pages uses KV as storage/origin too, so this case study is relevant to websites running on Pages too.
</p>
<h3>Our TTFB on Desktop and Mobile</h3>
<p>
The Chrome UX Report (known as <a href="https://developer.chrome.com/docs/crux/" class="external-link">CrUX</a>) is a public dataset of real user experience data on millions of websites.
Data is collected by the Chrome browser on mobile and desktop and is constantly updated.
The most talked about data in CrUX are the Core Web Vitals, but there is more, including TTFB.
</p>
<!-- <p>
Google classifies a TTFB lower than 0.8 seconds as Good and a TTFB higher than 1.8 seconds as Poor.
</p> -->
<p>
You can access the CrUX data in many ways.
We often use <a href="https://treo.sh/sitespeed/www.cdnplanet.com" class="external-link">Treo</a> because of the beautiful visualizations.
</p>
<p>
In September 2023, our TTFB on desktop was 0.4 seconds at the 75<sup>th</sup> percentile and ~11% of pageviews had a TTFB that Google considers Moderate or Poor.
On mobile (second chart below), the TTFB picture looks much worse: TTFB was Moderate or Poor for more than 21% of pageviews.
TTFB on mobile is 1.3s at the 90<sup>th</sup> percentile, meaning 10% of pageviews had a TTFB higher than 1.3 seconds. Ouch.
</p>
<p>
<img loading="lazy" src="https://www.cdnplanet.com/static/img/crux-ttfb-buckets-cdnplanet-origin-desktop-history-2023.png" width="371" height="355" alt="CrUX TTFB distribution over time for desktop on www.cdnplanet.com" title="CrUX TTFB distribution over time for desktop on www.cdnplanet.com">
<img loading="lazy" src="https://www.cdnplanet.com/static/img/crux-ttfb-buckets-cdnplanet-origin-mobile-history-2023.png" width="366" height="357" alt="CrUX TTFB distribution over time for mobile on www.cdnplanet.com" title="CrUX TTFB distribution over time for mobile on www.cdnplanet.com">
</p>
<p>
The CDN Planet website has many users on mobile (43%) and no less than 33% of mobile users are on a slow 3G connection.
Improving TTFB is especially important for those 3G users, so what can we do?
</p>
<p>
<img src="https://www.cdnplanet.com/static/img/crux-device-distribution-cdnplanet-origin-mobile.png" width="355" height="265" alt="CrUX device distribution over time on www.cdnplanet.com" title="CrUX device distribution over time on www.cdnplanet.com">
<img loading="lazy" src="https://www.cdnplanet.com/static/img/crux-connection-distribution-cdnplanet-origin-mobile.png" width="355" height="265" alt="CrUX connection distribution over time for mobile on www.cdnplanet.com" title="CrUX connection distribution over time for mobile on www.cdnplanet.com">
</p>
<h3>Five Ways to Improve TTFB on Cloudflare Workers Sites</h3>
<p>
Some brainstorming led to five ideas for solving our high TTFB on Cloudflare problem:
</p>
<table class="td-border-top valign-text-top">
<thead>
<tr>
<th>Solution</th>
<th>Thoughts</th>
</tr>
</thead>
<tbody>
<tr>
<td>Increase edge cache-control</td>
<td>Easy to implement but unlikely the speed gains are significant</td>
</tr>
<tr>
<td>Put HTML of popular pages in edge cache</td>
<td>Small code change in our Worker and probably somewhat effective</td>
</tr>
<!-- <tr>
<td>Add HTML of popular pages to the Worker</td>
<td>Our Worker is less than 50 KB so we could add our Top 15 pages' HTML, but not sure how to import static files into the Worker JS file at build time</td>
</tr> -->
<tr>
<td>Switch to Cloudflare Pages</td>
<td>Pages uses KV for storage too, so no speed gains expected</td>
</tr>
<tr>
<td>Use Speculation Rules to prefetch next page</td>
<td>Speculation Rules seems easy to implement, configurable and can do not only prefetch but also full page prerendering</td>
</tr>
<tr>
<td>Move from KV to multi-region origin</td>
<td>Stop using KV and instead store our static site in multiple datacenters across the globe, using something like Fly.io. Probably very effective, but a big project</td>
</tr>
</tbody>
</table>
<p>
We decided on using <strong>Speculation Rules to improve TTFB</strong> for our website on Cloudflare,
mainly because it allows for starting with prefetch and later upgrading to full page rendering for instant loading of the next page.
</p>
<h2 id="intro">Introduction to Speculation Rules</h2>
<p>
The best place to learn all about the what, why and how of the Speculation Rules API is the Chrome for Developers website where
Barry Pollard, a Web Performance Developer Advocate for Google, published two great articles: <a href="https://developer.chrome.com/blog/prerender-pages/" class="external-link">Prerender pages in Chrome for instant page navigations</a> and <a href="https://developer.chrome.com/blog/debugging-speculation-rules/" class="external-link">Debugging speculation rules</a>.<br>
I'll provide a brief overview here.
</p>
<p>
The Speculation Rules API provides a new way for website builders to reduce user-visible latency (= improve TTFB and LCP).
The API allows a web page to declare which URLs the user may navigate to next, so the browser can combine this information with its own heuristics to decide whether to speculatively prefetch or prerender pages. The rules are <em>hints</em> and the browser may decide not to act upon them.
</p>
<p>
The rules are expressed as a JSON object included within a script tag. In the future, the browser may load external rule sets that are served with the <code>Speculation-Rules</code> header. The script tag can statically exist in the page or be dynamically injected by JavaScript.
</p>
<p>
The prefetched/prerendered page is held in memory and so will be available quicker to the browser once needed.
Browsers cache prefetched pages for a short time before discarding them (max 5 minutes in Chrome 118).
If the user navigates to the URL while the prefetch/prerender is still in-flight, the browser will use the in-flight fetch/render and continue from there.
</p>
<p>
Speculation Rules replaces what <code><link rel="prerender"...></code> and <code><link rel="prefetch"...></code> used to do for preloading navigations.
For now, the API works for same-site speculative loading only.
</p>
<!-- <blockquote>
The proposed <code><script type="speculationrules"></code> JSON format allows more detailed configuration, and can be integrated well with user agent heuristics. It also avoids the interoperability and compatibility minefield of these existing <code><link></code> behaviors
</blockquote> -->
<h3>List Rules</h3>
<p>
With <strong>list rules</strong>, the speculation rules JSON contains a list of resources to be fetched, plus options like an explicit Referrer-Policy.
If a resource in the list is not a full, absolute URL, it will be parsed relative to the document base URL.<br>
List rules are supported in Chrome/Edge.
<!-- A rule may include a list of requirements, which are assertions in the rule about the capabilities of the browser while executing them.
Currently the only requirement that is specified is denoted by <code>anonymous-client-ip-when-cross-origin</code>.
If present, it means the rule matches only if the browser can prevent the client IP address from being visible to the origin server if a cross-origin prefetch request is issued. -->
</p>
<p>
Example of speculation rules using list rules:
</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>script</span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>speculationrules<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token script"><span class="token language-javascript"><br> <span class="token punctuation">{</span><br> <span class="token string">"prerender"</span><span class="token operator">:</span> <span class="token punctuation">[</span><br> <span class="token punctuation">{</span><br> <span class="token string">"source"</span><span class="token operator">:</span> <span class="token string">"list"</span><span class="token punctuation">,</span><br> <span class="token string">"urls"</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token string">"next.html"</span><span class="token punctuation">,</span> <span class="token string">"next2.html"</span><span class="token punctuation">]</span><br> <span class="token punctuation">}</span><br> <span class="token punctuation">]</span><br> <span class="token punctuation">}</span><br></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>script</span><span class="token punctuation">></span></span></code></pre>
<h3>Document Rules</h3>
<p>
Instead of List rules, you may also use <strong>document rules</strong>.
These allow the browser to find URLs for speculation from link elements in the page.
Document rules allow developers to specify pattern-based allow/deny lists, which is more convenient in scenarios where a page contains a large number of links or dynamic content.
Document rules will land in Chrome/Edge mid January 2024 at the earliest, but you can use document rules already on your website by participating in the Origin Trial (more on this below).
</p>
<h3>Eagerness</h3>
<p>
You may provide hints about how eagerly the browser should prefetch/prerender links in order to balance the performance advantage against resource overhead.
The value of <code>eagerness</code> may be <code>"conservative"</code>, <code>"moderate"</code> or .
If not specified, list rules default to <code>"eager"</code> and document rules default to <code>"conservative"</code>.
In Chrome, setting eagerness to <code>moderate</code> means the navigations are prefetched/prerendered when the link is hovered (desktop) or clicked (mobile: touchstart).
</p>
<h3>Content Security Policy</h3>
<p>
To allow inline speculation rules in script tags,
use either the <code>'inline-speculation-rules'</code> or <code>'unsafe-inline'</code> keyword in the <code>script-src</code> or <code>script-src-elem</code> CSP directives.
</p>
<p>
The <code>default-src</code> directive can be used to restrict which URLs can be prefetched or prerendered.
</p>
<p>
Read more about all the (techy) details like <code>anonymous-client-ip-when-cross-origin</code>, window name targeting hints, explicit referrer policy and the <code>No-Vary-Search</code> hint in the <a href="https://github.com/WICG/nav-speculation/blob/main/triggers.md" class="external-link">Speculation Rules explainer</a> on GitHub, or read the <a href="https://developer.mozilla.org/en-US/docs/Web/API/Speculation_Rules_API" class="external-link">Speculation Rules API reference on MDN</a>
</p>
<h2 id="our-implementation">How We Use Speculation Rules</h2>
<p>
We started using Speculation Rules early October and wanted to use document rules, so we had to participate in the Chrome Origin Trial.
</p>
<ul>
<li>Read the <a href="https://github.com/WICG/nav-speculation/blob/main/triggers.md" class="external-link">explainer</a> and <a href="https://wicg.github.io/nav-speculation/speculation-rules.html" class="external-link">spec</a></li>
<li>Register for the <a href="https://developer.chrome.com/origintrials/#/view_trial/705939241590325249" class="external-link">Chrome Origin Trial</a></li>
<li>Create the speculation rules JSON (trial and error, URL patterns != regex)</li>
<li>Add Origin Trial token and speculation rules JSON to HTML</li>
</ul>
<p>
Origin Trial token in head of HTML:
</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>meta</span> <span class="token attr-name">http-equiv</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>origin-trial<span class="token punctuation">"</span></span> <span class="token attr-name">content</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>AlGsXbB8btGaaxi/5Py8egKSo1M+A1sTXuTpw2gqIpU9Tm+LCwQzSKJ7PVR5VwuioAz3rD7zwbhg+fG659+UOgYAAABpeyJvcmlnaW4iOiJodHRwczovL3d3dy5jZG5wbGFuZXQuY29tOjQ0MyIsImZlYXR1cmUiOiJTcGVjdWxhdGlvblJ1bGVzUHJlZmV0Y2hGdXR1cmUiLCJleHBpcnkiOjE3MDk2ODMxOTl9<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></code></pre>
<p>
Script with speculation rules JSON in footer:
</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>script</span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>speculationrules<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token script"><span class="token language-javascript"><br> <span class="token punctuation">{</span><br> <span class="token string">"prefetch"</span><span class="token operator">:</span> <span class="token punctuation">[</span><br> <span class="token punctuation">{</span><br> <span class="token string">"source"</span><span class="token operator">:</span> <span class="token string">"document"</span><span class="token punctuation">,</span><br> <span class="token string">"where"</span><span class="token operator">:</span> <span class="token punctuation">{</span><br> <span class="token string">"and"</span><span class="token operator">:</span> <span class="token punctuation">[</span><br> <span class="token punctuation">{</span><span class="token string">"href_matches"</span><span class="token operator">:</span> <span class="token string">"https:\\/\\/www.cdnplanet.com/*"</span><span class="token punctuation">}</span><span class="token punctuation">,</span><br> <span class="token punctuation">{</span><span class="token string">"not"</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token string">"href_matches"</span><span class="token operator">:</span> <span class="token string">"#*"</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span><br> <span class="token punctuation">]</span><br> <span class="token punctuation">}</span><span class="token punctuation">,</span><br> <span class="token string">"eagerness"</span><span class="token operator">:</span> <span class="token string">"moderate"</span><br> <span class="token punctuation">}</span><br> <span class="token punctuation">]</span><br> <span class="token punctuation">}</span><br></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>script</span><span class="token punctuation">></span></span></code></pre>
<p>
We're using document rules and declaring to the browser that it may prefetch any link that starts with <code>https://www.cdnplanet.com/</code> <em>and</em> is not an in-page link.
The prefetching should happen with moderate eagerness (hover/touch).
</p>
<p>
It was weird to notice Chrome would prefetch the same page when hovering over an in-page link.
This is now fixed in Chrome 118, so the <code>{"not": { "href_matches": "#*" } }</code> may be removed from the speculation rules.
Knowing there is no harm in leaving it in, that is what we're doing, so Chrome users that have not upgraded to 118 yet are still helped by the prefetching.
</p>
<h3>Validate Speculation Rules Behaviour</h3>
<p>
We did not have an Origin Trial token for our dev and staging environments (a mistake) and so could only validate it works in production.
Validation of a correct implementation is easy in Chrome Dev Tools:
</p>
<p>
Open Dev Tools in Chrome and select the Application tab. On the left hand side you'll see a section Preloading.
The Speculation rules view should show the number of preloads associated with the rule set that is active on the page.
This is confirmation your Speculation Rules are valid and picked up by the browser.
</p>
<p>
Click on Preloads in the pane on the left to view a list of all URLs the browser may prefetch and each URL's status.
Now hover over a link on the page that should trigger a prefetch and sort the Status column (click the column heading twice).
At top of the table you'll see the status of the hovered link's URL has changed to Ready.
</p>
<p>
<img loading="lazy" class="bordered" src="https://www.cdnplanet.com/static/img/speculation-rules-preloads-chrome-devtools-screenshot.png" width="740" height="660" alt="Speculation Rules in Chrome Dev Tools: Preloads">
</p>
<p>
Next, hover over another link to trigger the prefetch and then click that link to navigate to the next page.
In Dev Tools, click This Page to view the preloading status.
</p>
<p>
<img loading="lazy" class="bordered" src="https://www.cdnplanet.com/static/img/speculation-rules-thispage-chrome-devtools-screenshot.png" width="600" height="500" alt="Speculation Rules in Chrome Dev Tools: This Page">
</p>
<h2 id="impact">Impact of Speculation Rules on TTFB</h2>
<p>
The goal is to speed up user experiences by reducing Time to First Byte with prefetching.
This translates to two success metrics:
</p>
<table class="td-border-top valign-text-top">
<thead>
<tr>
<th>Metrics</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td>Prefetch success ratio</td>
<td>What percentage of eligible pageviews had a prefetched HTML?</td>
</tr>
<tr>
<td>TTFB Improvement</td>
<td>How much faster did prefetched pages load as a result of lowering TTFB to zero?</td>
</tr></tbody>
</table>
<p>
Landing pages and back-forward navigations are logically out of scope.
Furthermore, our measurement code ignores page reloads and navigations where the HTML comes from browser cache.
</p>
<h3>Prefetch success ratio</h3>
<p>
The percentage of eligible pageviews where the HTML came from the browser's in-memory prefetch cache versus over network was
~15% on mobile and ~22% on desktop 🥳
</p>
<h3>TTFB Improvement</h3>
<p>
How much time waiting for the page was saved by the prefetching?
Well, prefetched pages have a TTFB of 0 because they are loaded from memory, so we measure the TTFB of non-prefetched pages and assume
the TTFB for prefetched pages would have been about the same if those would have loaded without prefetch over network.
</p>
<p>
The daily average TTFB for non-prefetched pages was between 233 ms and 442 ms, with an outlier of 898 ms on Oct 14.
</p>
<p>
In conclusion, Speculation Rules prefetch improved TTFB on our Cloudflare Workers website by ~300 ms for ~20% of eligible pageviews.
That's a good win, but we want to do better, so we'll continue to look into ways to improve TTFB and consequently FCP and LCP.
</p>
<h3>How We Measured Effects of Speculation Rules</h3>
<p>
In short:
</p>
<ul>
<li>The Cloudflare Worker script measures how long it takes to read HTML from edge cache or KV (edge read time) and exposes this to the browser in a <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Server-Timing" class="external-link">Server-Timing</a> header</li>
<li>JS in our pages gets the TTFB and Prefetched (yes/no) from the browser's built-in Performance API, as well as the edge read time</li>
<li>The slightly modified JS snippet for Google Analytics adds the metrics as custom parameters to the <code>page_view</code> event</li>
</ul>
<p>
The edge read time is not required to measure effectiveness of speculation rules, but we useful to understand what % of TTFB is made up of reading the resource from edge case or KV.
</p>
<p>
Keep an eye out for our upcoming blog post with all the details on how we are tracking effectiveness of speculation rules in Google Analytics.
</p>
<h2 id="cloudflare-prefetch-urls">Cloudflare's Prefetch URLs vs Speculation Rules</h2>
<p>
Cloudflare has a feature for Enterprise customers that pulls content from the origin into the edge cache.
</p>
<blockquote>
URL prefetching means that Cloudflare pre-populates the cache with content a visitor is likely to request next. This setting — when combined with additional setup — leads to a higher cache hit rate and thus a faster experience for the user.
</blockquote>
<p>
Surely some customers will find this useful, but it does not prefetch HTML and you need to supply a static list of files to prefetch.
Read more in the docs for <a href="https://developers.cloudflare.com/speed/optimization/content/prefetch-urls/" class="external-link">Cloudflare Prefetch URLs</a>.
</p>
<p>
Cloudflare Prefetch URLs is for prefetching website resources from origin to edge cache, while Speculation Rules prefetches HTML from edge cache to browser.
</p>
<h2 id="learnings">Learnings & Tips</h2>
<ul>
<li>Documentation is good. The articles on developer.chrome.com are great and the explainer provides more details/background where needed</li>
<li>The Network tab in Dev Tools signifies if a document was prefetched sucessfully: the Size column shows <code>(prefetch cache)</code></li>
<li>Getting the document rules right may take some time (URL patterns can be hard)</li>
<li>It's recommended to register your dev/staging env for the Origin Trial too, so you can validate it all works well before pushing to production</li>
<li>Good to know: early hints delivered for prefetched documents won't be processed by the browser</li>
</ul>
<p>
Questions? Feedback? Send it our way on <a href="https://twitter.com/cdnplanet" class="external-link">Twitter / X</a>.
We'd also love to hear about your experiences using the Speculation Rules API.
</p>
<hr>
<h2 id="faq">Speculation Rules FAQ</h2>
<ul>
<li><a href="#which-browsers-support-the-speculation-rules-api">Which browsers support the Speculation Rules API?</a></li>
<li><a href="#can-i-feature-detect-the-speculation-rules-api">Can I feature detect the Speculation Rules API?</a></li>
<li><a href="#is-it-possible-to-detect-prefetched-and-prerendered-pages">Is it possible to detect prefetched and prerendered pages?</a></li>
<li><a href="#is-it-safe-to-prefetch-or-prerender-any-page">Is it safe to prefetch or prerender any page?</a></li>
<li><a href="#how-long-does-chrome-keep-a-prefetched/prerendered-page-in-memory">How long does Chrome keep a prefetched/prerendered page in memory?</a></li>
<li><a href="#how-many-prefetched/prerendered-pages-can-chrome-keep-in-memory">How many prefetched/prerendered pages can Chrome keep in memory?</a></li></ul>
<h3 id="which-browsers-support-the-speculation-rules-api">Which browsers support the Speculation Rules API?</h3>
<p>
As of October 2023, the Speculation Rules API is supported in Chrome and Edge browsers version 109 and up and Opera 95. Certain features and options, including document rules, are landing in Chrome/Edge 120.<br>
<a href="https://developer.mozilla.org/en-US/docs/Web/API/Speculation_Rules_API#browser_compatibility" class="external-link">Browser compatibility table on MDN</a>
</p>
<h3 id="can-i-feature-detect-the-speculation-rules-api">Can I feature detect the Speculation Rules API?</h3>
<p>
Yes, use JavaScript to check if HTMLScriptElement supports "speculationrules".<br>
<a href="https://developer.mozilla.org/en-US/docs/Web/API/Speculation_Rules_API#speculation_rules_api_feature_detection" class="external-link">Speculation rules API feature detection (MDN)</a>
</p>
<h3 id="is-it-possible-to-detect-prefetched-and-prerendered-pages">Is it possible to detect prefetched and prerendered pages?</h3>
<p>
You can detect prefetched and prerendered pages server-side based on the Sec-Purpose request header. In the browser, use JavaScript to detect if a page was prefetched, prerendered or even if the page is <i>being</i> prerendered.<br>
<a href="https://developer.mozilla.org/en-US/docs/Web/API/Speculation_Rules_API#detecting_prefetched_and_prerendered_pages" class="external-link">Detecting prefetched and prerendered pages (MDN)</a>
</p>
<h3 id="is-it-safe-to-prefetch-or-prerender-any-page">Is it safe to prefetch or prerender any page?</h3>
<p>
No, under certain conditions it is unsafe to prefetch/prerender. For example, prefetching a page whose server-generated content depends on what the user does on the <i>current</i> page is potentially risky and could lead to confused users.<br>
<a href="https://developer.mozilla.org/en-US/docs/Web/API/Speculation_Rules_API#unsafe_speculative_loading_conditions" class="external-link">Unsafe speculative loading conditions (MDN)</a>
</p>
<h3 id="how-long-does-chrome-keep-a-prefetched/prerendered-page-in-memory">How long does Chrome keep a prefetched/prerendered page in memory?</h3>
<p>
The Chrome browser keeps prefetched/prerendered pages in memory for up to 5 minutes.
</p>
<h3 id="how-many-prefetched/prerendered-pages-can-chrome-keep-in-memory">How many prefetched/prerendered pages can Chrome keep in memory?</h3>
<p>
The limit is 5 for prefetched pages and 10 for prerendered pages.
</p>
Bunny Fonts or Google Fonts - Performance Comparison2023-09-20T02:00:00+02:00https://www.cdnplanet.com/blog/bunny-fonts-google-fonts-performance/Which webfont service is best from a site speed perspective? Read our in-depth comparison of Google Fonts and Bunny Fonts performance.
<div class="toc-container">
<div>
<p>
Early 2022, a court in Germany found a company guilty of violating users' privacy by using Google Fonts on their website: Google Fonts was ruled GDPR non-compliant!
Five months later, Bunny CDN launched a privacy-first, GDPR-compliant, drop-in replacement for Google Fonts, named Bunny Fonts.
</p>
<p>
Google stopped storing users' IP addresses later that year, making it on par with Bunny Fonts as to GDPR compliance, so how do you decide which webfonts service to use?
</p>
<p>
Site speed is more important than ever before, so let's dive into performance of Google Fonts and Bunny Fonts.
</p>
<p>
This article is an in-depth <strong>comparison of Google Fonts and Bunny Fonts performance</strong>.
</p>
</div>
<div class="toc">
<strong>Table of contents</strong>
<ul>
<li><a href="#summary">Summary</a></li>
<li><a href="#embed-code">Embed Code</a></li>
<li><a href="#file-size">Font File Size</a></li>
<li><a href="#caching">Caching</a></li>
<li><a href="#dns-tls-http">DNS, TLS and HTTP</a></li>
<li><a href="#network">CDN Network Locations</a></li>
<li><a href="#china">Blocked in China?</a></li>
<li><a href="#optimized-embed-code">Optimized Bunny Embed Code</a></li>
<!-- <li><a href="#self-host-fonts">Self Hosting Fonts is Best</a></li> -->
</ul>
</div>
</div>
<!--
the li must have position: absolute;
<img class="author-profile-image br-100 mr2" src="https://media.licdn.com/dms/image/D4E03AQF6WzkwxU7zvA/profile-displayphoto-shrink_100_100/0/1674830596103?e=1700697600&v=beta&t=YR8Dqfwz8nxStI7dQJBxAf0gXgH5EE8NiNUdPuuUxRM" alt="Aaron Peters" width="52" height="52" style="
border-radius: 50%;
position: relative;
top: -0.25em;
z-index: 100;
border: 2px solid var(--primary-700);
filter: drop-shadow(0px 1px 6px #00363c);
">
the next li has margin-left: 80px; and author name as contents
-->
<h2 id="summary">Comparison Summary</h2>
<p>
Bunny and Google score about same when it comes to CDN network size, DNS, TLS and caching headers.
</p>
<style>
table.comparison-with-icon-columns td:not(.icon) {
width: 30%;
padding: 0.5em 0.95em 0.5em 0;
}
table.comparison-with-icon-columns td.icon {
min-width: calc(24px + 1em);
padding-right: 0.3em;
text-align: right;
}
</style>
<table class="comparison-with-icon-columns td-border-top">
<thead>
<tr>
<th class="sticky-head"></th>
<th colspan="2" class="sticky-head" style="padding-left: 18px">Bunny Fonts</th>
<th colspan="2" class="sticky-head" style="padding-left: 18px">Google Fonts</th>
</tr>
</thead>
<tbody>
<tr>
<td>Embed code, default variant</td>
<td class="icon"><img alt="No" title="No" src="https://www.cdnplanet.com/static/img/close-circle.svg" width="24" height="24"></td>
<td>CSS @import</td>
<td class="icon"><img alt="Yes" title="Yes" src="https://www.cdnplanet.com/static/img/check-circle.svg" width="24" height="24"></td>
<td>Link in HTML</td>
</tr>
<tr>
<td>Preconnect in HTML embed code</td>
<td class="icon"><img alt="No" title="No" src="https://www.cdnplanet.com/static/img/close-circle.svg" width="24" height="24"></td>
<td>Wrong/ineffective</td>
<td class="icon"><img alt="Yes" title="Yes" src="https://www.cdnplanet.com/static/img/check-circle.svg" width="24" height="24"></td>
<td>Good</td>
</tr>
<tr>
<td>font-display</td>
<td class="icon"><img alt="Sortof/partially" title="Sortof/partially" src="https://www.cdnplanet.com/static/img/check-circle-partially.svg" width="24" height="24"></td>
<td>Supported, but not in embed code</td>
<td class="icon"><img alt="Yes" title="Yes" src="https://www.cdnplanet.com/static/img/check-circle.svg" width="24" height="24"></td>
<td>Yes</td>
</tr>
<tr>
<td>Font files size</td>
<td class="icon"><img alt="No" title="No" src="https://www.cdnplanet.com/static/img/close-circle.svg" width="24" height="24"></td>
<td>Heavy</td>
<td class="icon"><img alt="Yes" title="Yes" src="https://www.cdnplanet.com/static/img/check-circle.svg" width="24" height="24"></td>
<td>Light</td>
</tr>
<tr>
<td>Font files can be preloaded</td>
<td class="icon"><img alt="Yes" title="Yes" src="https://www.cdnplanet.com/static/img/check-circle.svg" width="24" height="24"></td>
<td>Yes</td>
<td class="icon"><img alt="No" title="No" src="https://www.cdnplanet.com/static/img/close-circle.svg" width="24" height="24"></td>
<td>No</td>
</tr>
<tr>
<td>Blocked in China</td>
<td class="icon"><img alt="Yes" title="Yes" src="https://www.cdnplanet.com/static/img/check-circle.svg" width="24" height="24"></td>
<td>No</td>
<td class="icon"><img alt="No" title="No" src="https://www.cdnplanet.com/static/img/close-circle.svg" width="24" height="24"></td>
<td>Sometimes</td>
</tr>
<tr>
<td>HTTP/3</td>
<td class="icon"><img alt="No" title="No" src="https://www.cdnplanet.com/static/img/close-circle.svg" width="24" height="24"></td>
<td>No</td>
<td class="icon"><img alt="Yes" title="Yes" src="https://www.cdnplanet.com/static/img/check-circle.svg" width="24" height="24"></td>
<td>Yes</td>
</tr>
<tr>
<td>Caching</td>
<td class="icon"><img alt="Yes" title="Yes" src="https://www.cdnplanet.com/static/img/check-circle.svg" width="24" height="24"></td>
<td>Good</td>
<td class="icon"><img alt="Yes" title="Yes" src="https://www.cdnplanet.com/static/img/check-circle.svg" width="24" height="24"></td>
<td>Good</td>
</tr>
</tbody>
</table>
<h2 id="embed-code">Embed Code</h2>
<p>
Bunny Fonts and Google Fonts allow you to browse and select fonts, to then copy & paste the embed code into your CSS or HTML.
The embed code comes in two flavours: HTML and CSS @import. Let's look at the embed code first and then see how each code performs.
</p>
<p>
Bunny Fonts shows the CSS @import code by default:
</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>style</span><span class="token punctuation">></span></span><span class="token style"><span class="token language-css"><br><span class="token atrule"><span class="token rule">@import</span> <span class="token url"><span class="token function">url</span><span class="token punctuation">(</span>https://fonts.bunny.net/css?family=roboto:300,400<span class="token punctuation">)</span></span><span class="token punctuation">;</span></span><br></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>style</span><span class="token punctuation">></span></span></code></pre>
<p>
</p>
<p>
Bunny Fonts HTML embed code:
</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>link</span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>preconnect<span class="token punctuation">"</span></span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>https://fonts.bunny.net<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>link</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>https://fonts.bunny.net/css?family=roboto:300,400<span class="token punctuation">"</span></span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>stylesheet<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></code></pre>
<p>
Google also presents both options and gives preference to the HTML embed code:
</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>link</span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>preconnect<span class="token punctuation">"</span></span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>https://fonts.googleapis.com<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>link</span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>preconnect<span class="token punctuation">"</span></span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>https://fonts.gstatic.com<span class="token punctuation">"</span></span> <span class="token attr-name">crossorigin</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>link</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>https://fonts.googleapis.com/css2?family=Roboto:wght@300;400&display=swap<span class="token punctuation">"</span></span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>stylesheet<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></code></pre>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>style</span><span class="token punctuation">></span></span><span class="token style"><span class="token language-css"><br><span class="token atrule"><span class="token rule">@import</span> <span class="token function">url</span><span class="token punctuation">(</span><span class="token property">https</span><span class="token punctuation">:</span>//fonts.googleapis.com/css2?family=<span class="token property">Roboto</span><span class="token punctuation">:</span>wght<span class="token atrule"><span class="token rule">@300</span><span class="token punctuation">;</span></span></span>400&display=swap<span class="token punctuation">)</span><span class="token punctuation">;</span><br></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>style</span><span class="token punctuation">></span></span></code></pre>
<h3>Performance Analysis</h3>
<p>
I've created a simple test page for each of the 4 embed codes and ran tests on WebPageTest.
The test pages with CSS @import have the import rule <em>in</em> the main CSS file as this is most common (vs in the HTML's head).
</p>
<table class="second-col-icon td-border-top">
<thead>
<tr>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="https://www.cdnplanet.com/uploads/bunny-fonts-css-import-embed.html">Bunny Fonts test page - CSS @import embed</a></td>
<td> <a href="https://www.webpagetest.org/result/230918_BiDc1Z_906/" class="external-link">WebPageTest results</a></td>
</tr>
<tr>
<td><a href="https://www.cdnplanet.com/uploads/bunny-fonts-html-embed.html">Bunny Fonts test page - HTML embed</a></td>
<td> <a href="https://www.webpagetest.org/result/230918_AiDcKV_8P1/" class="external-link">WebPageTest results</a></td>
</tr>
<tr>
<td><a href="https://www.cdnplanet.com/uploads/google-fonts-html-embed.html">Google Fonts test page - HTML embed</a></td>
<td><a href="https://www.webpagetest.org/result/230918_BiDcJ2_8X0/" class="external-link">WebPageTest results</a></td>
</tr>
<tr>
<td><a href="https://www.cdnplanet.com/uploads/google-fonts-css-import-embed.html">Google Fonts test page - CSS @import embed</a></td>
<td><a href="https://www.webpagetest.org/result/230918_BiDc4J_912/" class="external-link">WebPageTest results</a></td>
</tr>
</tbody>
</table>
<!-- <ul>
<li>
<a href="/uploads/bunny-fonts-css-import-embed.html">Bunny Fonts test page - CSS @import embed</a>,
<a href="https://www.webpagetest.org/result/230918_BiDc1Z_906/" class="external-link">WebPageTest results</a>
</li>
<li>
<a href="/uploads/bunny-fonts-html-embed.html">Bunny Fonts test page - HTML embed</a>,
<a href="https://www.webpagetest.org/result/230918_AiDcKV_8P1/" class="external-link">WebPageTest results</a>
</li>
<li>
<a href="/uploads/google-fonts-html-embed.html">Google Fonts test page - HTML embed</a>,
<a href="https://www.webpagetest.org/result/230918_BiDcJ2_8X0/" class="external-link">WebPageTest results</a>
</li>
<li>
<a href="/uploads/google-fonts-css-import-embed.html">Google Fonts test page - CSS @import embed</a>,
<a href="https://www.webpagetest.org/result/230918_BiDc4J_912/" class="external-link">WebPageTest results</a>
</li>
</ul> -->
<p>
The pages using Google Fonts or Bunny Fonts CSS @import code perform the worst.
The waterfall charts below show that nothing happens in parallel. Ugh.
The browser does not discover a new domain to connect to or load something from until it has finished loading <em>this</em> file.
</p>
<p>
<img src="https://www.cdnplanet.com/static/img/waterfall-bunny-fonts-css-import-embed.png" width="600" height="165" alt="waterfall bunny fonts css @import embed">
</p>
<p>
<img src="https://www.cdnplanet.com/static/img/waterfall-google-fonts-css-import-embed.png" width="600" height="165" alt="waterfall google fonts css @import embed">
</p>
<p>
Bunny Font's HTML embed code is a bit better.
The Bunny CSS file loads in parallel with the main CSS:
</p>
<p>
<img loading="lazy" src="https://www.cdnplanet.com/static/img/waterfall-bunny-fonts-html-embed.png" width="600" height="165" alt="waterfall bunny fonts html embed">
</p>
<p>
<strong>The best performance is delivered by Google Fonts using their HTML embed code</strong>.
Why? Because their preconnect for the font file domain works.
</p>
<p>
<img loading="lazy" src="https://www.cdnplanet.com/static/img/waterfall-google-fonts-html-embed.png" width="600" height="165" alt="waterfall google fonts html embed">
</p>
<p>
Takeaways:
</p>
<ul>
<li>CSS @import is bad for performance and this should come as <a href="https://calibreapp.com/blog/css-performance#avoid-import-in-css-files" class="external-link">no suprise</a></li>
<li>Bunny should fix the preconnect in their HTML embed code so font loading is faster</li>
</ul>
<p>
A <code>preconnect</code> for fonts requires the <code>crossorigin</code> attribute, and the Bunny Fonts code does not have that attribute.
That's surprising because it's been known for years (see our 2017 article <a href="https://www.cdnplanet.com/blog/faster-google-webfonts-preconnect/">Faster Google Fonts with Preconnect</a>).
</p>
<h2 id="file-size">File Size</h2>
<p>
The size of the Google CSS files are much smaller than Bunny's,
but the absolute difference in bytes is insignificant because the CSS files are small:
it doesn't matter if the browser downloads 600 bytes or 1.3 KB.
</p>
<p>
Google also serves much smaller font files and here the size difference is more than just a few hundred bytes.
The table below shows the byte size of 4 popular fonts (WOFF2, normal 400, latin-ext).
The Ubuntu font file is a whopping 28 KB heavier on Bunny Fonts 🤯
</p>
<table class="second-col-icon td-border-top">
<thead>
<tr>
<th>Font</th>
<th>Google Fonts file size</th>
<th>Bunny Fonts file size</th>
</tr>
</thead>
<tbody>
<tr>
<td>Roboto</td>
<td><a href="https://fonts.gstatic.com/s/roboto/v30/KFOmCnqEu92Fr1Mu7GxKKTU1Kvnz.woff2" class="external-link" rel="nofollow">7736 bytes</a></td>
<td><a href="https://fonts.bunny.net/roboto/files/roboto-latin-ext-400-normal.woff2" class="external-link" rel="nofollow">11872 bytes (+53%)</a></td>
<td></td>
</tr>
<tr>
<td>Lato</td>
<td><a href="https://fonts.gstatic.com/s/lato/v24/S6uyw4BMUTPHjxAwXiWtFCfQ7A.woff2" class="external-link" rel="nofollow">2892 bytes</a></td>
<td><a href="https://fonts.bunny.net/lato/files/lato-latin-ext-400-normal.woff2" class="external-link" rel="nofollow">5472 bytes (+89%)</a></td>
</tr>
<tr>
<td>Poppins</td>
<td><a href="https://fonts.gstatic.com/s/poppins/v20/pxiEyp8kv8JHgFVrJJnecnFHGPezSQ.woff2" class="external-link" rel="nofollow">5544 bytes</a></td>
<td><a href="https://fonts.bunny.net/poppins/files/poppins-latin-ext-400-normal.woff2" class="external-link" rel="nofollow">5544 bytes (+0%)</a></td>
</tr>
<tr>
<td>Ubuntu</td>
<td><a href="https://fonts.gstatic.com/s/ubuntu/v20/4iCs6KVjbNBYlgoKcQ72nU6AF7xm.woff2" class="external-link" rel="nofollow">17680 bytes</a></td>
<td><a href="https://fonts.bunny.net/ubuntu/files/ubuntu-latin-ext-400-normal.woff2" class="external-link" rel="nofollow">46796 bytes (+164%)</a></td>
</tr>
</tbody>
</table>
<p>
Further inspection reveals the unicode ranges are not same. That is odd. <br>
Ubuntu font:<br>
Google: <code>U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF</code><br>
Bunny: <code>U+0100-02AF, U+0300-0301, U+0303-0304, U+0308-0309 ,U+0323, U+0329, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF</code>
</p>
<h2 id="caching">Caching</h2>
<p>
Browsers may cache the CSS and font files served by Bunny for up to 30 days.
Google allows browsers to cache the font files for as long as a one year but the CSS for 'only' 24 hours.
Does this mean Bunny Fonts has better caching of the CSS? No.
Google serves the CSS with the <code>stale-while-revalidate</code> header so browsers will use the file from cache after it has expired
<em>and</em> in the background fetch the latest version from a Google server.
</p>
<h3>CSS Cache-Control</h3>
<ul>
<li>Bunny: <code>public, max-age=2592000</code></li>
<li>Google: <code>private, max-age=86400, stale-while-revalidate=604800</code></li>
</ul>
<h3>Font Files Cache-Control</h3>
<ul>
<li>Bunny: <code>public, max-age=2592000</code></li>
<li>Google: <code>public, max-age=31536000</code></li>
</ul>
<h2 id="dns-tls-http">DNS, TLS and HTTP</h2>
<p>
Bunny and Google both have good DNS, TLS and HTTP.
The only difference that matters here for performance is: Google has HTTP/3 and Bunny does not.
</p>
<h3>DNS</h3>
<p>
DNS lookups for both Bunny Fonts and Google Fonts will be fast most of the time because the close-by DNS resolvers can serve the answer from cache due to the popularity of the font service. Bunny Fonts has a lower Time-To-Live (TTL) so browsers will do a lookup for Bunny Fonts more frequently.
</p>
<p>
Bunny:
</p>
<pre>
fonts.bunny.net. 300 IN CNAME bunnyfonts.b-cdn.net.
bunnyfonts.b-cdn.net. 35 IN A 84.17.46.53
</pre>
<p>
Google:
</p>
<pre>
fonts.googleapis.com. 300 IN A 172.217.23.202
</pre>
<h3>TLS</h3>
<p>
Certificate size, TLS protocol and features support ... it's all about same for Bunny and Google.<br>
<a href="https://www.ssllabs.com/ssltest/analyze.html?d=fonts.bunny.net&latest" class="external-link">SSL Labs - fonts.bunny.net</a>,
<a href="https://www.ssllabs.com/ssltest/analyze.html?d=fonts.googleapis.com&latest" class="external-link">SSL Labs - fonts.googleapis.com</a>,
<a href="https://www.ssllabs.com/ssltest/analyze.html?d=fonts.gstatic.com&latest" class="external-link">SSL Labs - fonts.gstatic.com</a>
</p>
<h3>HTTP</h3>
<p>
The latest version of HTTP is HTTP/3, which has several <a href="https://www.linkedin.com/advice/0/what-benefits-drawbacks-using-http3-over-http2-skills-informatics#:~:text=HTTP%2F3%20offers%20several%20advantages,improve%20user%20experience%20and%20performance" class="external-link">benefits</a> over HTTP/2, including:
</p>
<ul>
<li>Faster connection establishment</li>
<li>Less effect of packet loss</li>
<li>No more bad performance when phone switches from WiFi to cellular data</li>
</ul>
<p>
Google servers talk HTTP/3, but, unfortunately, <strong>Bunny Fonts does not yet support HTTP/3</strong>.<br>
</p>
<h2 id="network">CDN Network Locations</h2>
<p>
Bunny and Google both have a widespreak CDN network with cache locations all around the globe.
Google is well-known for having one of the best networks, but Bunny has caching servers in more countries,
including Peru and Ecuador in LATAM, Latvia and Slovakia in Europe and Pakistan and New Zealand in Asia Pacific.
</p>
<p>
View the <a href="https://www.cdnplanet.com/cdns/bunnycdn/#network">Bunny CDN locations</a> and <a href="https://cloud.google.com/cdn/docs/locations" class="external-link">Google Cloud CDN locations</a> .
</p>
<h2 id="china">Blocked in China?</h2>
<p>
It <a href="https://www.webpagetest.org/result/230918_BiDcB4_9JT/" class="external-link">seems</a> Bunny CDN is not blocked in China.
Google services have a history of being blocked/unblocked for many years.
Currently, <a href="https://www.webpagetest.org/result/230918_BiDcCE_A2M/" class="external-link">Google Fonts are not blocked in China</a>
but you may expect the service to be blocked again in the future.
</p>
<h2 id="optimized-embed-code">Optimized Bunny Fonts Embed Code</h2>
<p>
No CSS @import, font-display swap and effective preconnect <em>or</em> preload of the main font file.
</p>
<pre class="language-html"><code class="language-html"><span class="token comment"><!-- Preconnect to Bunny Fonts server with CORS. Use this if not using preload --></span><br><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>link</span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>preconnect<span class="token punctuation">"</span></span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>https://fonts.bunny.net<span class="token punctuation">"</span></span> <span class="token attr-name">crossorigin</span><span class="token punctuation">></span></span><br><br><span class="token comment"><!-- Preload the main font file. Remove the preconnect when using this --></span><br><span class="token comment"><!-- Want to preload more font files? Limit to two to not slow down loading of more important resources --></span><br><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>link</span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>preload<span class="token punctuation">"</span></span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>https://fonts.bunny.net/roboto/files/roboto-latin-ext-400-normal.woff2<span class="token punctuation">"</span></span> <span class="token attr-name">as</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>font<span class="token punctuation">"</span></span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>font/woff2<span class="token punctuation">"</span></span> <span class="token attr-name">crossorigin</span><span class="token punctuation">></span></span><br><br><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>link</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>https://fonts.bunny.net/css?family=roboto:400,700&display=swap<span class="token punctuation">"</span></span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>stylesheet<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></code></pre>
<!-- <h2 id="self-host-fonts">Self Hosting Fonts is Best</h2>
<p>
But if you do want to use Google Fonts or Bunny Fonts, read Harry's article:
<a href="https://csswizardry.com/2020/05/the-fastest-google-fonts/" class="external-link">TODO</a>
(TODO: paste here Harry's final/best code, adapted for Bunny Fonts?)
</p> -->
<hr>
<h2 id="faq">Bunny Fonts Performance FAQ</h2>
<ul>
<li><a href="#will-bunny-fonts-make-my-website-faster">Will Bunny Fonts Make My Website Faster?</a></li>
<li><a href="#how-can-i-preload-bunny-fonts">How Can I Preload Bunny Fonts?</a></li>
<li><a href="#are-bunny-fonts-blocked-in-china">Are Bunny Fonts Blocked in China?</a></li>
<li><a href="#is-it-easy-to-replace-google-fonts-with-bunny-fonts">Is it Easy to Replace Google Fonts with Bunny Fonts?</a></li>
<li><a href="#how-many-fonts-are-available-on-bunny-fonts">How Many Fonts Are Available on Bunny Fonts?</a></li></ul>
<h3 id="will-bunny-fonts-make-my-website-faster">Will Bunny Fonts Make My Website Faster?</h3>
<p>
Probably not. It's better to serve webfonts from the same domain as your website because this uses an existing connection. No time is needed then to set up the new connection to the Bunny Fonts server. Only if your website is not on a CDN and your server response times are often high should you consider serving fonts from Bunny Fonts or Google Fonts.
</p>
<h3 id="how-can-i-preload-bunny-fonts">How Can I Preload Bunny Fonts?</h3>
<p>
The Bunny font files have static URLs and this allows for preloading. Simply add preload code to the head of HTML, right before the links to stylesheets. View our <a href="#optimized-embed-code">Optimized Bunny Fonts Embed Code</a><br>
<a href="https://web.dev/preload-critical-assets/" class="external-link">Preload critical assets to improve loading speed</a>
</p>
<h3 id="are-bunny-fonts-blocked-in-china">Are Bunny Fonts Blocked in China?</h3>
<p>
We've tested Bunny Fonts from a cloud server in Beijing, China on September 19 2023 and the fonts loaded just fine.
</p>
<h3 id="is-it-easy-to-replace-google-fonts-with-bunny-fonts">Is it Easy to Replace Google Fonts with Bunny Fonts?</h3>
<p>
Yes, but make sure to use the HTML embed code (not the CSS @import) for best performance and add the crossorigin attribute to the preconnect. For even better performance, use CDN Planet's Optimized Bunny Fonts embed code.<br>
<a href="#optimized-embed-code">Optimized Bunny Fonts Embed Code</a>
</p>
<h3 id="how-many-fonts-are-available-on-bunny-fonts">How Many Fonts Are Available on Bunny Fonts?</h3>
<p>
As of September 19 2023, Bunny Fonts provides access to 1510 font families.
</p>
Site Speed of CDN Websites2023-09-08T02:00:00+02:00https://www.cdnplanet.com/blog/cdn-websites-site-speed-compared/How fast are the websites of content delivery networks like Cloudflare, Fastly, Gcore and CacheFly? Let's find out using July 2023 Core Web Vitals data.
<div class="toc-container">
<div>
<p>
Content delivery networks sell solutions to improve website speed:
"Use our CDN and your website will load much faster!"
</p>
<p>
It seems obvious to expect the website of a CDN provider is fast,
because surely the CDN provider wants to make a good first impression and
<em>show</em> the visitor how fast their CDN is.
</p>
<p>
Let's compare <strong>CDN website speed</strong> using Google's real-user measurement data.
Do all CDN websites provide a fast user experience and score 'all green' for the Core Web Vitals?
</p>
</div>
<div class="toc">
<strong>On this page</strong>
<ul>
<li><a href="#comparison-table">Speed of CDN Websites Compared</a></li>
<li><a href="#fastest-slowest">Fastest & Slowest</a></li>
<li><a href="#ttfb">TTFB is Disappointing</a></li>
<li><a href="#fetchpriority-lcp">Fetchpriority for LCP Image</a></li>
<li><a href="#metrics">Site Speed Metrics Explained</a></li>
<li><a href="#treo">Treo.sh is Awesome</a></li>
</ul>
</div>
</div>
<h2 id="comparison-table">Speed of CDN Websites Compared</h2>
<p>
The table below shows the speed of CDN websites in July 2023, based on <a href="https://developer.chrome.com/docs/crux/" class="external-link">CRuX</a> data.
</p>
<ul>
<li>Main website (www) only, not the support/docs/kb or customer portal</li>
<li>All pages</li>
<li>Mobile</li>
<li>75<sup>th</sup> percentile</li>
</ul>
<style>
:root {
--bar-perc: 83%
}
@media screen and (min-width:600px){
:root {
--bar-perc: 93%;
}
}
.table-metrics-compared .bar-box {
position: relative;
height: 30px;
width: 100%;
}
.table-metrics-compared .chartvalue {
position: absolute;
color: var(--black);
font-size: 90%;
z-index: 30;
left: 5px;
bottom: 5px;
}
.table-metrics-compared .bar {
/* width and bgcolor must be set as inline style */
position: absolute;
display: block;
height: 18px;
margin: 4px 0px 0px 3px;
}
.table-metrics-compared .bar.none {
background-color: transparent;
}
.table-metrics-compared .bar.good {
background-color: #00998866;
}
.table-metrics-compared .bar.moderate {
background-color: #ee773399;
}
.table-metrics-compared .bar.poor {
background-color: #ee333380;
}
.table-metrics-compared .metric-value {
padding: 0.2em 0.3em 0.15em;
}
.metric-value.good {
background-color: var(--green);
color: var(--white)
}
.metric-value.moderate {
background-color: #ee9633;
color: var(--white)
}
.metric-value.poor {
background-color: var(--red);
color: var(--white)
}
</style>
<table class="table-responsive margin-bottom-small Xvalign-text-top td-border-top sticky-head table-metrics-compared">
<thead>
<tr>
<th>CDN</th>
<th>TTFB</th>
<th>FCP</th>
<th>LCP</th>
<th>CLS</th>
<th>INP</th>
</tr>
</thead>
<tbody>
<!-- <tr>
<td>Akamai</td>
<td>
<div class="bar-box">
<span class="chartvalue">0.8</span>
<span data-text="0.8 seconds" class="bar good" style="width: calc((0.8/3.8)*var(--bar-perc));"></span>
</div>
</td>
<td>
<div class="bar-box">
<span class="chartvalue">2.6</span>
<span data-text="2.6 seconds" class="bar moderate" style="width: calc((2.6/7.3)*var(--bar-perc));"></span>
</div>
</td>
<td>
<div class="bar-box">
<span class="chartvalue">3.1</span>
<span data-text="2.6 seconds" class="bar moderate" style="width: calc((3.1/7.3)*var(--bar-perc));"></span>
</div>
</td>
<td>
<div class="bar-box">
<span class="chartvalue">0</span>
<span data-text="0" class="bar good" style="width: calc((0.01/0.25)*var(--bar-perc));"></span>
</div>
</td>
<td>
<div class="bar-box">
<span class="chartvalue">400</span>
<span data-text="400" class="bar moderate" style="width: calc((400/750)*var(--bar-perc));"></span>
</div>
</td>
</tr> -->
<tr>
<td><a href="https://treo.sh/sitespeed/www.akamai.com?metricsMode=d&formFactor=phone" class="external-link">Akamai</a></td>
<td>
<span class="metric-value moderate">1.7</span>
</td>
<td>
<span class="metric-value moderate">2.6</span>
</td>
<td>
<span class="metric-value moderate">3.1</span>
</td>
<td>
<span class="metric-value good">0</span>
</td>
<td>
<span class="metric-value moderate">400</span>
</td>
</tr>
<tr>
<td><a href="https://treo.sh/sitespeed/aws.amazon.com?metricsMode=d&formFactor=phone" class="external-link">Amazon CloudFront</a></td>
<td>
<span class="metric-value moderate">1.3</span>
</td>
<td>
<span class="metric-value poor">3.2</span>
</td>
<td>
<span class="metric-value poor">4.1</span>
</td>
<td>
<span class="metric-value good">0</span>
</td>
<td>
<span class="metric-value poor">550</span>
</td>
</tr>
<tr>
<td><a href="https://treo.sh/sitespeed/bunny.net?metricsMode=d&formFactor=phone" class="external-link">bunny</a></td>
<td>
<span class="metric-value moderate">0.9</span>
</td>
<td>
<span class="metric-value moderate">3.0</span>
</td>
<td>
<span class="metric-value moderate">3.7</span>
</td>
<td>
<span class="metric-value good">0</span>
</td>
<td>
<span class="metric-value moderate">400</span>
</td>
</tr>
<tr>
<td><a href="https://treo.sh/sitespeed/www.cachefly.com?metricsMode=d&formFactor=phone" class="external-link">CacheFly</a></td>
<td>
<span class="metric-value moderate">1.9</span>
</td>
<td>
<span class="metric-value poor">4.5</span>
</td>
<td>
<span class="metric-value poor">4.7</span>
</td>
<td>
<span class="metric-value good">0.05</span>
</td>
<td>
<span class="metric-value moderate">375</span>
</td>
</tr>
<tr>
<td><a href="https://treo.sh/sitespeed/www.cdn77.com?metricsMode=d&formFactor=phone" class="external-link">CDN77</a></td>
<td>
<span class="metric-value moderate">1.3</span>
</td>
<td>
<span class="metric-value moderate">2.0</span>
</td>
<td>
<span class="metric-value moderate">2.7</span>
</td>
<td>
<span class="metric-value good">0</span>
</td>
<td>
<span class="metric-value moderate">300</span>
</td>
</tr>
<tr>
<td><a href="https://treo.sh/sitespeed/www.cdnetworks.com?metricsMode=d&formFactor=phone" class="external-link">CDNetworks</a></td>
<td>
<span class="metric-value poor">3.8</span>
</td>
<td>
<span class="metric-value poor">6.0</span>
</td>
<td>
<span class="metric-value poor">6.3</span>
</td>
<td>
<span class="metric-value good">0</span>
</td>
<td>
<span class="metric-value moderate">450</span>
</td>
</tr>
<tr>
<td><a href="https://treo.sh/sitespeed/www.cloudflare.com?metricsMode=d&formFactor=phone" class="external-link">Cloudflare</a></td>
<td>
<span class="metric-value good">0.7</span>
</td>
<td>
<span class="metric-value good">1.4</span>
</td>
<td>
<span class="metric-value good">1.6</span>
</td>
<td>
<span class="metric-value good">0</span>
</td>
<td>
<span class="metric-value poor">750</span>
</td>
</tr>
<tr>
<td><a href="https://treo.sh/sitespeed/www.edgenext.com?metricsMode=d&formFactor=phone" class="external-link">EdgeNext</a></td>
<td>
<span class="metric-value poor">2.7</span>
</td>
<td>
<span class="metric-value poor">5.3</span>
</td>
<td>
<span class="metric-value poor">6.1</span>
</td>
<td>
<span class="metric-value moderate">0.15</span>
</td>
<td>
<span class="metric-value poor">700</span>
</td>
</tr>
<tr>
<td><a href="https://treo.sh/sitespeed/edg.io?metricsMode=d&formFactor=phone" class="external-link">Edgio</a></td>
<td>
<span class="metric-value poor">2.7</span>
</td>
<td>
<span class="metric-value poor">4.7</span>
</td>
<td>
<span class="metric-value poor">5.2</span>
</td>
<td>
<span class="metric-value good">0</span>
</td>
<td>
<span class="metric-value moderate">325</span>
</td>
</tr>
<tr>
<td><a href="https://treo.sh/sitespeed/www.fastly.com?metricsMode=d&formFactor=phone" class="external-link">Fastly</a></td>
<td>
<span class="metric-value good">0.6</span>
</td>
<td>
<span class="metric-value good">1.6</span>
</td>
<td>
<span class="metric-value good">1.9</span>
</td>
<td>
<span class="metric-value good">0.05</span>
</td>
<td>
<span class="metric-value moderate">375</span>
</td>
</tr>
<tr>
<td><a href="https://treo.sh/sitespeed/gcore.com?metricsMode=d&formFactor=phone" class="external-link">Gcore</a></td>
<td>
<span class="metric-value moderate">1.6</span>
</td>
<td>
<span class="metric-value poor">3.2</span>
</td>
<td>
<span class="metric-value moderate">3.7</span>
</td>
<td>
<span class="metric-value good">0.05</span>
</td>
<td>
<span class="metric-value moderate">300</span>
</td>
</tr>
<tr>
<td><a href="https://treo.sh/sitespeed/www.medianova.com?metricsMode=d&formFactor=phone" class="external-link">Medianova</a></td>
<td>
<span class="metric-value poor">2.2</span>
</td>
<td>
<span class="metric-value poor">3.1</span>
</td>
<td>
<span class="metric-value moderate">3.1</span>
</td>
<td>
<span class="metric-value good">0</span>
</td>
<td>
<span class="metric-value good">175</span>
</td>
</tr>
<tr>
<td><a href="https://treo.sh/sitespeed/www.stackpath.com?metricsMode=d&formFactor=phone" class="external-link">StackPath</a></td>
<td>
<span class="metric-value poor">3.4</span>
</td>
<td>
<span class="metric-value poor">7.3</span>
</td>
<td>
<span class="metric-value poor">7.3</span>
</td>
<td>
<span class="metric-value moderate">0.25</span>
</td>
<td>
<span class="metric-value moderate">400</span>
</td>
</tr>
</tbody>
</table>
<h2 id="fastest-slowest">Fastest & Slowest</h2>
<p>
<strong>Fastly and Cloudflare have the fastest websites</strong>, with all metrics in the green except INP.
Funnily, Cloudflare posted an article on their blog in June titled <a href="https://blog.cloudflare.com/inp-get-ready-for-the-new-core-web-vital/" class="external-link">INP. Get ready for the new Core Web Vital</a>. Well, they should, indeed, get ready ;-)<br>
To my surprise, Fastly and Cloudflare are the only CDN providers that score a Good for TTFB, FCP and LCP 🤯<br>
</p>
<p>
Users on Medianova's website experience the lowest interaction latency (INP).
</p>
<p>
CDNetworks, EdgeNext, Edgio and StackPath all score a Poor for TTFB, FCP and LCP.
Visitors of their websites need to wait too long for anything to appear on the screen, let alone the (likely) most important content element.
</p>
<p>
<strong>StackPath has the worst overall website performance</strong>, with FCP and LCP p75 values at a very high 7.3 seconds.
CDNetworks has the worst TTFB and Cloudflare the worst INP.
</p>
<h2 id="ttfb">TTFB is Disappointing</h2>
<p>
TTFB is mostly about server response time and contributes to FCP and LCP.
Sadly, only 2 of the 13 CDN websites have a low TTFB: Cloudflare and Fastly.
The other CDNs are not able to deliver the HTML at high speed to site visitors 🙁
</p>
<p>
Why is the TTFB of most CDN websites poor?<br>
EdgeNext website is hosted on GoDaddy (ugh).<br>
The other websites are on the CDN's own platform, but some have edge caching disabled (CDNetworks, Gcore).<br>
The <a href="https://web.dev/bfcache/" class="external-link">back/forward cache</a> is not working for CDNetworks, Edgio, Fastly and Gcore.
</p>
<h2 id="fetchpriority-lcp">Fetch Priority for LCP Image</h2>
<p>
It's a site speed best practice to specify <code>fetchpriority="high"</code> on the LCP image element, to make the browser load the image sooner and so improve the user experience. Learn more on web.dev at <a href="https://web.dev/fetch-priority/" class="external-link">Optimizing resource loading with the Fetch Priority API</a>.
</p>
<p>
Only three CDN websites have <code>fetchpriority="high"</code> on HTML images or <code>preload</code> with <code>fetchpriority="high"</code> on CSS background image: CDNetworks, Medianova and StackPath.
Too bad all three do it in an ineffective way:<br>
CDNetworks has fetchpriority on an image that is not the LCP element.<br>
Medianova and StackPath use a lazy loading Wordpress plugin that sets <code>fetchpriority="high"</code> on the placeholder transparent SVG image (ugh).
Later during the page load/render flow, on the Medianova homepage <code>fetchpriority="high"</code> is set on the actual LCP image (too late) but on the StackPath homepage the image that gets <code>fetchpriority="high"</code> is not the LCP element ().
</p>
<p>
The following two snippets can be used in the browser's Dev Tools console to get a list of image URLs with <code>fetchpriority="high"</code>:
</p>
<pre class="language-js"><code class="language-js"><span class="token punctuation">[</span><span class="token operator">...</span><span class="token keyword">new</span> <span class="token class-name">Set</span><span class="token punctuation">(</span><span class="token function">$$</span><span class="token punctuation">(</span><span class="token string">'img[fetchpriority=high]'</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">map</span><span class="token punctuation">(</span><span class="token parameter">i</span> <span class="token operator">=></span> i<span class="token punctuation">.</span>src<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">]</span></code></pre>
<pre class="language-js"><code class="language-js"><span class="token punctuation">[</span><span class="token operator">...</span><span class="token keyword">new</span> <span class="token class-name">Set</span><span class="token punctuation">(</span><br> <span class="token function">$$</span><span class="token punctuation">(</span><span class="token string">'link[rel=preload][as=image]'</span><span class="token punctuation">)</span><br> <span class="token punctuation">.</span><span class="token function">filter</span><span class="token punctuation">(</span><span class="token parameter">link</span> <span class="token operator">=></span> link<span class="token punctuation">.</span>fetchpriority <span class="token operator">&&</span> link<span class="token punctuation">.</span>fetchpriority <span class="token operator">==</span> <span class="token string">'high'</span><span class="token punctuation">)</span><br> <span class="token punctuation">.</span><span class="token function">map</span><span class="token punctuation">(</span><span class="token parameter">link</span> <span class="token operator">=></span> link<span class="token punctuation">.</span>href<span class="token punctuation">)</span><br><span class="token punctuation">)</span><span class="token punctuation">]</span></code></pre>
<p>
Drag <a href="javascript:(function(doc){console.log('HTML images with fetchpriority=high :');[...new Set(Array.from(doc.querySelectorAll('img[fetchpriority=high]')).map(i => i.src).forEach(i => { console.log(i) }))];console.log('Preload images with fetchpriority=high :');[...new Set(Array.from(document.querySelectorAll('link[rel=preload][as=image]')).filter(link => link.fetchpriority && link.fetchpriority == 'high').map(link => link.href).forEach(i => { console.log(i) }))]})(document)">this link</a> to your browser favourites/bookmarks to one-click see output of those two snippets in console.
</p>
<h2 id="metrics">Site Speed Metrics Explained</h2>
<h3>What is TTFB?</h3>
<details>
<summary>
<a href="https://web.dev/ttfb/" class="external-link">Time to First Byte (TTFB)</a> is the time between sending a request and receiving the first byte from the server. A low TTFB signifies the server responds quickly. TTFB includes DNS, TCP, TLS and redirects.
</summary>
<blockquote class="margin-top">
Time to First Byte (TTFB) is a foundational metric for measuring connection setup time and web server responsiveness in both the lab and the field. It helps identify when a web server is too slow to respond to requests. In the case of navigation requests—that is, requests for an HTML document—it precedes every other meaningful loading performance metric.
</blockquote>
Source: web.dev
</details>
<h3>What is FCP?</h3>
<details>
<summary>
<a href="https://web.dev/fcp/" class="external-link">First Contentful Paint (FCP)</a> marks the time when the user can see anything on the screen.
</summary>
<blockquote class="margin-top">
First Contentful Paint (FCP) is an important, user-centric metric for measuring <a href="https://web.dev/user-centric-performance-metrics/#types-of-metrics" class="external-link">perceived load speed</a> because it marks the first point in the page load timeline where the user can see anything on the screen—a fast FCP helps reassure the user that something is <a href="https://web.dev/user-centric-performance-metrics/#questions" class="external-link">happening</a>.
</blockquote>
Source: web.dev
</details>
<h3>What is LCP?</h3>
<details>
<summary>
<a href="https://web.dev/lcp/" class="external-link">Largest Contentful Paint (LCP)</a> marks the time when the page's main content has likely loaded.
</summary>
<blockquote class="margin-top">
Largest Contentful Paint (LCP) is an important, stable Core Web Vital metric for measuring <a href="https://web.dev/user-centric-performance-metrics/#types-of-metrics" class="external-link">perceived load speed</a> because it marks the point in the page load timeline when the page's main content has likely loaded—a fast LCP helps reassure the user that the page is <a href="https://web.dev/user-centric-performance-metrics/#questions" class="external-link">useful</a>.
</blockquote>
Source: web.dev
</details>
<h3>What is CLS?</h3>
<details>
<summary>
<a href="https://web.dev/cls/" class="external-link">Cumulative Layout Shift (CLS)</a> quantifies how often users experience unexpected layout shifts.
</summary>
<blockquote class="margin-top">
Cumulative Layout Shift (CLS) is a stable Core Web Vital metric. It is an important, user-centric metric for measuring <a href="https://web.dev/user-centric-performance-metrics/#types-of-metrics" class="external-link">visual stability</a> because it helps quantify how often users experience unexpected layout shifts—a low CLS helps ensure that the page is <a href="https://web.dev/user-centric-performance-metrics/#questions" class="external-link">delightful</a>.
</blockquote>
Source: web.dev
</details>
<h3 id="inp">What is INP?</h3>
<details>
<summary>
<a href="https://web.dev/inp/" class="external-link">Interaction to Next Paint (INP)</a> measures interaction latency. It captures the full event time, from initial input until the next frame is painted and event handlers have run. For pages with many interactions, it uses the maximum duration with some approximations.
</summary>
<blockquote class="margin-top">
Interaction to Next Paint (INP) is a <a href="https://web.dev/vitals/#pending" class="external-link">pending</a> Core Web Vital metric that will <a href="https://web.dev/user-centric-performance-metrics/#types-of-metrics" class="external-link">replace First Input Delay (FID)</a> in March 2024. INP assesses responsiveness using data from the Event Timing API. When an interaction causes a page to become unresponsive, that is a poor user experience. INP observes the latency of all interactions a user has made with the page, and reports a single value which all (or nearly all) interactions were below. A low INP means the page was consistently able to respond quickly to all—or the vast majority—of user interactions.
</blockquote>
Source: web.dev
</details>
<h2 id="treo">We ❤️ Treo </h2>
<p>
Treo provides site speed monitoring, including competitive analysis based on real-users data.
The <a href="https://treo.sh/sitespeed" class="external-link">Site Speed Report</a> allows you to view historical CWV data for all websites tracked by Google's CrUX.
The data visualizations at Treo are really, really good.
</p>
<p>
<img loading="lazy" class="bordered" src="https://www.cdnplanet.com/static/img/treo-sh-site-speed-audit-screenshot.png" width="600" height="427" alt="Treo.sh site speed audit for Fastly screenshot">
</p>StackPath CDN Shut Down: Alternatives?2023-09-04T02:00:00+02:00https://www.cdnplanet.com/blog/stackpath-cdn-shut-down-alternatives/StackPath CDN is shut down November 2023. What are some good StackPath CDN alternatives?
<p>
StackPath is moving out of the CDN business.<br>
On August 24 2023, StackPath announced the shutdown of its CDN after seven years of accelerating thousands of websites from their global edge network. Customers were notified by email:
</p>
<blockquote>
We have some important news to share. StackPath has decided to commit its entire focus to being the industry's best cloud computing platform built at the Internet's edge.
<br><br>
With this decision, we will discontinue our StackPath CDN and legacy Highwinds CDN products. StackPath CDN and Highwinds CDN services will cease operations at 12:00 a.m. Central (UTC-6:00) on November 22, 2023.
</blockquote>
<p>
<a href="https://www.akamai.com/newsroom/press-release/akamai-acquires-stackpath-cdn-customers" class="external-link">Akamai has acquired approximately 100 enterprise accounts from StackPath</a>, but what about the many smaller customers?
</p>
<p>
Customers of StackPath CDN must migrate to a different content delivery network in the next few months.
Let's look at some <a href="#stackpath-cdn-alternatives">StackPath CDN alternatives</a>.
</p>
<h2 id="stackpath-cdn-alternatives">StackPath CDN Alternatives</h2>
<p>
StackPath CDN always had a focus on delivery of static content, typically website objects like images and scripts.
Every CDN can cache and deliver static content, but not all CDNs provide this service at a comparable price.
<a href="#cachefly">CacheFly</a>, <a href="#bunny">Bunny CDN</a>, <a href="#gcore">Gcore</a> and <a href="#cdn77">CDN77</a> are probably a good fit for most StackPath CDN customers.
They offer self-service signup, easy configuration and pricing is on par or better than what they are paying for StackPath CDN.
</p>
<h3 id="cachefly">
<img src="https://www.cdnplanet.com/static/img/cachefly-logo-purple.svg" width="195" height="65" alt="CacheFly " class="image-hide-mobile">
</h3>
<p>
CacheFly, based in US, has been around since 2002 and were the first CDN to use Anycast for routing traffic to the closest POP.
Over the years, they've steadily and silently built out the CDN (POPs, features, etc).
CacheFly is a good StackPath CDN alternative for companies that want excellent support and have 10+ TB/m traffic.
</p>
<strong id="cachefly-stackpath-offer">Special Offer for StackPath CDN Customers</strong>
<p>
StackPath customers switching to CacheFly are offered a special deal: 64 TB for $595.
Visit the <a href="https://www.cachefly.com/stackpath/?utm_source=cdnplanet&utm_medium=blog&utm_campaign=competitor_stackpath&utm_term=cdn&utm_content=blog_stackpath-cdn-shut-down" class="external-link">CacheFly for StackPath customers</a> page.
</p>
<strong>Pros & Cons of CacheFly versus StackPath CDN</strong>
<table class="table-striped second-col-icon">
<thead>
<tr>
<th></th>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
<tr>
<td>Support by senior engineers</td>
<td><img alt="Yes" title="Yes" src="https://www.cdnplanet.com/static/img/check-circle.svg" width="24" height="24" class="icon-in-table-cell-new"></td>
<td>CacheFly takes pride in acting as 'consultants to our customers' and go out of their way to help the customer. The CTO still works on support tickets.</td>
</tr>
<tr>
<td>More POPs</td>
<td><img alt="Yes" title="Yes" src="https://www.cdnplanet.com/static/img/check-circle.svg" width="24" height="24" class="icon-in-table-cell-new"></td>
<td>Especially in LATAM but on other continents as well, CacheFly has edge servers in (a lot) more locations.<br><a href="https://www.cdnplanet.com/compare/stackpath/cachefly/#network">Network comparison StackPath CDN - CacheFly</a></td>
</tr>
<tr>
<td>Smart Image Optimization</td>
<td><img alt="Yes" title="Yes" src="https://www.cdnplanet.com/static/img/check-circle.svg" width="24" height="24" class="icon-in-table-cell-new"></td>
<td>Not just convert and compress, but also watermarking, intelligent cropping images and more.<br><a href="https://www.cachefly.com/smart-image-optimization/?utm_source=cdnplanet&utm_medium=blog&utm_campaign=competitor_stackpath&utm_term=cdn&utm_content=blog_stackpath-cdn-shut-down" class="external-link">CacheFly Smart Image Optimization</a></td>
</tr>
<tr>
<td>Dynamic content delivery</td>
<td><img alt="Yes" title="Yes" src="https://www.cdnplanet.com/static/img/check-circle.svg" width="24" height="24" class="icon-in-table-cell-new"></td>
<td>Accelerate your full website, not just the images, fonts and other static assets</td>
</tr>
<tr>
<td>No on the fly compression</td>
<td><img alt="No" title="No" src="https://www.cdnplanet.com/static/img/close-circle.svg" width="24" height="24" class="icon-in-table-cell-new"></td>
<td>CacheFly edge servers can forward compressed content but don't compress on the fly</td>
</tr>
<tr>
<td>Cange propagation delay is higher</td>
<td><img alt="No" title="No" src="https://www.cdnplanet.com/static/img/close-circle.svg" width="24" height="24" class="icon-in-table-cell-new"></td>
<td>Changing the config on StackPath CDN takes seconds, while on CacheFly it takes 5 - 180s depending on the change</td>
</tr>
</tbody>
</table>
<p>
View our full <a href="https://www.cdnplanet.com/compare/stackpath/cachefly/">StackPath CDN - CacheFly comparison</a>
</p>
<h3 id="bunny">
<img src="https://www.cdnplanet.com/static/img/bunnynet-logo-svgomg.svg" width="195" height="65" alt="Bunny CDN" class="image-hide-mobile">
</h3>
<p>
Bunny has grown fast in the past years and is now an established CDN provider with a good network and feature set.
<br>
Looking for a good, low-cost CDN for website acceleration? Consider Bunny a good StackPath CDN alternative.
</p>
<strong>Pros & Cons of Bunny CDN versus StackPath CDN</strong>
<table class="table-striped second-col-icon">
<thead>
<tr>
<th></th>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
<tr>
<td>Cheaper</td>
<td><img alt="Yes" title="Yes" src="https://www.cdnplanet.com/static/img/check-circle.svg" width="24" height="24" class="icon-in-table-cell-new"></td>
<td>Low rates in NA/EU and APAC. Volume network (10 POPs) at $5 per TB.<br><a href="https://bunny.net/pricing/?utm_source=cdnplanet&utm_medium=blog&utm_campaign=competitor_stackpath&utm_term=cdn&utm_content=blog_stackpath-cdn-shut-down" class="external-link">Bunny CDN pricing</a></td>
</tr>
<tr>
<td>More POPs</td>
<td><img alt="Yes" title="Yes" src="https://www.cdnplanet.com/static/img/check-circle.svg" width="24" height="24" class="icon-in-table-cell-new"></td>
<td>120 POPs versus 42 POPs at StackPath CDN.<br><a href="https://www.cdnplanet.com/compare/stackpath/bunnycdn/#network">Network comparison StackPath CDN - Bunny CDN </a></td>
</tr>
<tr>
<td>Log files</td>
<td><img alt="Yes" title="Yes" src="https://www.cdnplanet.com/static/img/check-circle.svg" width="24" height="24" class="icon-in-table-cell-new"></td>
<td>All Bunny CDN customers get access to edge server log files, while StackPath offers streaming log files to customers with larger volume plans only</td>
</tr>
<tr>
<td>No phone support</td>
<td><img alt="No" title="No" src="https://www.cdnplanet.com/static/img/close-circle.svg" width="24" height="24" class="icon-in-table-cell-new"></td>
<td>Bunny CDN customers get 24/7 support but not by phone unless you buy Premium Support.</td>
</tr>
<tr>
<td>No WebSockets</td>
<td><img alt="No" title="No" src="https://www.cdnplanet.com/static/img/close-circle.svg" width="24" height="24" class="icon-in-table-cell-new"></td>
<td>
Bunny CDN does not support WebSockets.
<br><a href="https://www.cdnplanet.com/guides/websockets/">CDN Guide: WebSockets</a>
</td>
</tr>
</tbody>
</table>
<p>
View our full <a href="https://www.cdnplanet.com/compare/stackpath/bunnycdn/">StackPath CDN - Bunny CDN comparison</a>.
</p>
<h3 id="gcore">
<img src="https://www.cdnplanet.com/static/img/gcore-logo-svgomg.svg" width="195" height="65" alt="Gcore" class="image-hide-mobile">
</h3>
<p>
Gcore is a Europe based company offering CDN, DNS, Hosting and other related services.
Their CDN network is impressive and continues to grow fast.
<br>
Gcore makes for a good StackPath CDN alternative if you have users in Africa, Middle East and Russia & CIS.
</p>
<strong>Pros & Cons of Gcore versus StackPath CDN</strong>
<table class="table-striped second-col-icon">
<thead>
<tr>
<th></th>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
<tr>
<td>Cheaper</td>
<td><img alt="Yes" title="Yes" src="https://www.cdnplanet.com/static/img/check-circle.svg" width="24" height="24" class="icon-in-table-cell-new"></td>
<td>$0.0315/GB on the PAYG plan, $0.024/GB in the 1.5 TB and $37/m plan.<br><a href="https://gcore.com/pricing/edge-network?utm_source=cdnplanet&utm_medium=blog&utm_campaign=competitor_stackpath&utm_term=cdn&utm_content=blog_stackpath-cdn-shut-down" class="external-link">Gcore CDN pricing</a></td>
</tr>
<tr>
<td>More POPs</td>
<td><img alt="Yes" title="Yes" src="https://www.cdnplanet.com/static/img/check-circle.svg" width="24" height="24" class="icon-in-table-cell-new"></td>
<td>150+ POPs versus 42 POPs at StackPath CDN.<br><a href="https://www.cdnplanet.com/compare/stackpath/gcore/#network">Network comparison StackPath CDN - Gcore CDN</a></td>
</tr>
<tr>
<td>Brotli edge compression</td>
<td><img alt="Yes" title="Yes" src="https://www.cdnplanet.com/static/img/check-circle.svg" width="24" height="24" class="icon-in-table-cell-new"></td>
<td>
Gcore CDN supports Gzip and Brotli edge compression. Brotli only if Origin Shield (paid) is active.
<br><a href="https://gcore.com/docs/cdn/cdn-resource-options/compression/configure-gzip-and-brotli-compression" class="external-link">Configure GZip and Brotli compression</a>
<br><a href="https://www.cdnplanet.com/guides/origin-shield/">CDN Guide: Origin Shield</a>
<br><a href="https://www.cdnplanet.com/guides/compression/">CDN Guide: Compression</a>
</td>
</tr>
<tr>
<td>Phone support not in all price plans</td>
<td><img alt="No" title="No" src="https://www.cdnplanet.com/static/img/close-circle.svg" width="24" height="24" class="icon-in-table-cell-new"></td>
<td>24/7 phone support is included in the Pro plan ($100/m) and up. The lower plan is 8/5 email support only</td>
</tr>
<tr>
<td>Pay for requests</td>
<td><img alt="No" title="No" src="https://www.cdnplanet.com/static/img/close-circle.svg" width="24" height="24" class="icon-in-table-cell-new"></td>
<td>Gcore charges for requests. First 1 billion are free, more cost $0.0079 per 10,000 requests.</td>
</tr>
<tr>
<td>Origin Shield is not free</td>
<td><img alt="No" title="No" src="https://www.cdnplanet.com/static/img/close-circle.svg" width="24" height="24" class="icon-in-table-cell-new"></td>
<td>
Gcore charges extra for Origin Shield.
<br><a href="https://gcore.com/docs/cdn/cdn-resource-options/general/enable-and-configure-origin-shielding" class="external-link">Gcore CDN Origin Shield docs</a>
<br><a href="https://www.cdnplanet.com/guides/origin-shield/">CDN Guide: Origin Shield</a>
</td>
</tr>
</tbody>
</table>
<p>
View our full <a href="https://www.cdnplanet.com/compare/stackpath/gcore/">StackPath CDN - Gcore CDN comparison</a>.
</p>
<h3 id="cdn77">
<img src="https://www.cdnplanet.com/static/img/cdn77-logo-svgomg.svg" width="195" height="65" alt="CDN77" class="image-hide-mobile">
</h3>
<p>
Based in Europe, CDN77 is a safe bet for delivery of cacheable content around the globe.
</p>
<strong>Pros & Cons of CDN77 versus StackPath CDN</strong>
<table class="table-striped second-col-icon">
<thead>
<tr>
<th></th>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
<tr>
<td>Single global rate</td>
<td><img alt="Yes" title="Yes" src="https://www.cdnplanet.com/static/img/check-circle.svg" width="24" height="24" class="icon-in-table-cell-new"></td>
<td>$0.033 per GB in the smallest monthly plan, dropping to 1 cent per GB at 100 TB/m.</td>
</tr>
<tr>
<td>All security features included</td>
<td><img alt="Yes" title="Yes" src="https://www.cdnplanet.com/static/img/check-circle.svg" width="24" height="24" class="icon-in-table-cell-new"></td>
<td>DDoS, origin & hotlink protection, Secure tokens and more.</td>
</tr>
<tr>
<td>More POPs in EU and Middle East</td>
<td><img alt="Yes" title="Yes" src="https://www.cdnplanet.com/static/img/check-circle.svg" width="24" height="24" class="icon-in-table-cell-new"></td>
<td><a href="https://www.cdnplanet.com/compare/stackpath/cdn77/#network">Network comparison StackPath CDN - CDN77</a></td>
</tr>
<tr>
<td>Predefined monthly plans don't have all POPs</td>
<td><img alt="No" title="No" src="https://www.cdnplanet.com/static/img/close-circle.svg" width="24" height="24" class="icon-in-table-cell-new"></td>
<td>Predefined monthly plans include all PoPs in North & South America, Europe, Hong Kong, Jeddah, Singapore & Tokyo.<br><a href="https://www.cdn77.com/pricing/monthly-plans?utm_source=cdnplanet&utm_medium=blog&utm_campaign=competitor_stackpath&utm_term=cdn&utm_content=blog_stackpath-cdn-shut-down" class="external-link">Monthly plans</a></td>
</tr>
<tr>
<td>$199/m minimum</td>
<td><img alt="No" title="No" src="https://www.cdnplanet.com/static/img/close-circle.svg" width="24" height="24" class="icon-in-table-cell-new"></td>
<td>Smallest traffic monthly plan is 6 TB at $199/month.</td>
</tr>
</tbody>
</table>
<p>
View our full <a href="https://www.cdnplanet.com/compare/stackpath/cdn77/">StackPath CDN - CDN77 comparison</a>.
</p>
<hr>
<h2>StackPath CDN FAQ</h2>
<ul>
<li><a href="#is-stackpath-cdn-the-same-as-maxcdn">Is StackPath CDN the same as MaxCDN?</a></li>
<li><a href="#is-highwinds-cdn-shutting-down-too">Is Highwinds CDN shutting down too?</a></li>
<li><a href="#can-i-migrate-from-stackpath-cdn-to-stackpath-edge-compute">Can I migrate from StackPath CDN to StackPath Edge Compute?</a></li>
<li><a href="#how-can-i-contact-stackpath-support">How can I contact StackPath support?</a></li>
<li><a href="#were-stackpath-cdn-customers-happy">Were StackPath CDN customers happy?</a></li></ul>
<h3 id="is-stackpath-cdn-the-same-as-maxcdn">Is StackPath CDN the same as MaxCDN?</h3>
<p>
MaxCDN came to life in 2010 as a brand within the NetDNA company. MaxCDN was a simpler CDN for both small and large businesses. In 2013, NetDNA rebranded the company and its services as MaxCDN. StackPath acquired MaxCDN in July 2016. The MaxCDN part of StackPath CDN was retired on December 31, 2022.<br>
<a href="https://techcrunch.com/2016/07/25/security-as-a-service-startup-stackpath-nabs-180m-4-acquisitions-including-maxcdn/" class="external-link">MaxCDN acquisition [TechCrunch]</a>
</p>
<h3 id="is-highwinds-cdn-shutting-down-too">Is Highwinds CDN shutting down too?</h3>
<p>
Yes, the legacy Highwinds CDN is end of life on November 22, 2023. StackPath acquired Highwinds CDN soon after acquiring MaxCDN, in February 2017. StackPath integrated Highwinds services and platform with StackPath offerings and operations.<br>
<a href="https://en.wikipedia.org/wiki/Highwinds_Network_Group" class="external-link">Highwinds at Wikipedia</a>
</p>
<h3 id="can-i-migrate-from-stackpath-cdn-to-stackpath-edge-compute">Can I migrate from StackPath CDN to StackPath Edge Compute?</h3>
<p>
No, StackPath Edge Compute is not a good choice for serving cacheable static content because it simply (much) more expensive than a CDN.<br>
<a href="https://www.stackpath.com/products/edge-compute/" class="external-link">StackPath Edge Compute</a>
</p>
<h3 id="how-can-i-contact-stackpath-support">How can I contact StackPath support?</h3>
<p>
StackPath has different support packages, ranging from Standard to Platinum. StackPath CDN customers can log in to the StackPath customer portal to submit or manage support tickets.<br>
<a href="https://www.stackpath.com/resources/support/" class="external-link">StackPath Support overview page</a>,
<a href="https://control.stackpath.com/" class="external-link">StackPath customer portal</a>
</p>
<h3 id="were-stackpath-cdn-customers-happy">Were StackPath CDN customers happy?</h3>
<p>
Based on Trustpilot StackPath reviews in 2023, it seems customers of StackPath CDN were far from satisfied. StackPath had been increasing prices significantly and sometimes without prior warning. Customers also report support was going downhill.<br>
<a href="https://www.trustpilot.com/review/stackpath.com" class="external-link">Trustpilot StackPath reviews</a>,
<a href="https://www.trustpilot.com/review/stackpath.com" class="external-link">Link text</a>
</p>
Introducing CDN Purge Speed Stats2022-09-01T02:00:00+02:00https://www.cdnplanet.com/blog/introducing-cdn-purge-speed-stats/How fast you can purge a cached object on Amazon CloudFront, CacheFly and other CDNs? Our CDN Purge Speed Stats has the answer based on hourly, global measurements.
<p>
We're excited to announce the all new <a href="https://www.cdnplanet.com/stats/cdn-purge-speed/">CDN Purge Speed Stats</a>!
</p>
<p>
CDN Purge Speed Stats provides insight into how fast you can <a href="https://www.cdnplanet.com/guides/purge/">purge</a> (invalidate or delete) a single cached object on various content delivery networks, based on hourly measurements from 3 global locations.
</p>
<p>
Many CDNs claim purging is 'instant', but is it really?
Is the information provided by the CDN about purge speed correct?
CDN Purge Speed Stats has the answers using extensive, accurate, real-world data.
</p>
<p>
<!-- (United States, United Kingdom and Hong Kong). -->
<!-- View the 25/50/90/99 percentile <a href="#purge-time">Purge Time</a> for the last 7 days (updated hourly). -->
<!-- The 25/50/90/99 percentile <a href="#purge-time">Purge Time</a> for the last 7 days is updated hourly. -->
<!-- As a CDN user or when you are in the process of selecting a new CDN,
it's good to know what performance you can expect from the CDN for an important feature like purge. -->
</p>
<p>
We currently track purge speed of Amazon CloudFront, CacheFly, Edgio and Gcore,
with more CDNs coming soon.
</p>
<!-- <div class="notice-msg info">
Learn more about purging in our <a href="/guides/purge/">CDN Purge Guide</a>.
</div> -->
<!-- <p>
The CDN Purge Speed Stats data is accurate
What makes our data awesome?
- Accurate
- Recent and historical
- (Continuous updates, hourly)
- Depth
- Hourly
- 3 locations
- last7 and last30, 4 percentiles
</p> -->
<p>
Read on to learn more about <a href="#purge-time-definition">what is our definition of Purge Time</a> and <a href="#how-measure-purge-speed">how we measure purge speed</a>, or skip all that and view the <a href="https://www.cdnplanet.com/stats/cdn-purge-speed/">CDN Purge Speed Stats</a>.
</p>
<h2 id="purge-time-definition">What is the Definition of Purge Time?</h2>
<p>
Purge Time is the time between sending the purge request to the CDN API and sending the request to the CDN that returned a cache MISS.
More details are in the next FAQ item.
</p>
<h2 id="how-measure-purge-speed">How We Measure CDN Purge Speed</h2>
<p>
Our main application runs in the United States and there is a worker application in 3 global locations: United States, United Kingdom and Hong Kong.
These applications live on the Fly.io network.
</p>
<p>
Every hour, for each CDN, the main app takes the following steps:
</p>
<ol>
<li>Instruct the worker apps to prime the CDN cache (= get the test object into the CDN cache); log the CDN POP/server that served the cache HIT response</li>
<li>Send a single file purge request to the CDN API; log the time of sending the purge request as the <code>PurgeStartTime</code></li>
<li>For each location, instruct the worker apps to send requests to the CDN for the test object until cache MISS (= CDN serves the object after a fresh origin pull, as observed from A) the CDN response header(s) that signify the cache status (HIT/MISS/...) and B) the <code>x-cdnp-time</code> response header the origin serves to CDN and CDN sends through to client which has the timestamp of when origin served the response to CDN; log the time the main app sent the last request to CDN (via worker) as the <code>PurgeEndTime</code></li>
<li>Calculate the Purge Time for each location from the delta of the location's <code>PurgeEndTime</code> and <code>PurgeStartTime</code>, rounded to a zero-decimal number in seconds</li>
</ol>
<p>
From a CDN user's perspective, a purge starts when submitting the purge request and so the PurgeStartTime is when the main app sends the request to the CDN's API.
After purge, for each location, the main app instructs the worker app to send a request to the CDN until a cache MISS from CDN is observed.
The PurgeEndTime for the location is set to <em>right before sending</em> that last request from main app to worker app because the round-trip time
between main app and origin (via worker and CDN) should not be part of the measured Purge Time.
</p>
<p>
The worker apps send requests to the CDN in phase 3 (until cache MISS) per the following schedule:
</p>
<ul>
<li>Immediately, so it's possible to measure a Purge Time of 0 seconds</li>
<li>Every 1 second the next 5 seconds</li>
<li>Every 2 seconds the next 10 seconds</li>
<li>Every 5 seconds the next 45 seconds</li>
</ul>
<p>
The schedule has exponential backoff because when evaluating and comparing Purge Time of CDNs, a median value of e.g. 25 seconds provides the same insight as a median value of 26 or 27 seconds. In our opinion, it's fine for the precision to decrease as Purge Time increases.
</p>
<p>
If the worker app has not observed a cache-MISS within 60 seconds after purge, the CDN Purge Time is set to 65 seconds.
</p>
<p>
We selected US, UK and HK as locations for the worker apps primarily because we want to measure Purge Time from different continents.
Another benefit is that all major CDNs have POPs here and our origin lives in these locations too,
so the probability of worker <-> CDN requests and CDN <-> origin requests timing out is very low.
</p>
<!-- <p>
<img loading="lazy" src="/static/img/purge-speed-stats-diagram-w600.png" width="600" height="360" alt="CDN Purge Speed Stats - How it Works - Diagram">
</p> -->
<h2>Coming Soon</h2>
<p>
We'll continuously improve CDN Purge Speed Stats based on user feedback, analysis of the CDN Purge Speed Stats logs and our creative minds :)
</p>
<p>
Currently on the roadmap:
</p>
<ul>
<li>Add more CDNs!</li>
<li>Provide detailed insights for each CDN</li>
<li>Track performance of purge requests to CDN API (response time, error/timeout rate)</li>
<!-- <li>Track availability and speed of purge requests to CDN API</li> -->
<li>Improve visualization of the data</li>
<li>Measure 'purge all' too</li>
</ul>
<p>
Have an idea for how we can make CDN Purge Speed Stats even better? Let us know on <a href="https://twitter.com/cdnplanet" class="external-link">Twitter</a>
</p>
Cloudflare Browser Insights Review2021-04-13T02:00:00+02:00https://www.cdnplanet.com/blog/cloudflare-browser-insights-review/In-depth review of Cloudflare Browser Insights, a site speed analytics service for Core Web Vitals and more.
<div class="toc-container">
<div>
<p>
Starting May 2021, Google will take <a href="https://web.dev/vitals/" class="external-link">Core Web Vitals</a> into account to determine rankings of pages in Google search results.
Core Web Vitals is set of three page experience metrics: Largest Contentful Paint (LCP), First Input Delay (FID) and Cumulative Layout Shift (CLS).
</p>
<p>
Pages with poor Core Web Vitals scores may take a big hit and see traffic drop significantly.
Site owners should have a firm grip on Core Web Vitals metrics and improve site speed where needed.
</p>
<p>
Cloudflare's site speed analytics service <a href="https://support.cloudflare.com/hc/en-us/articles/360033929991-Cloudflare-Browser-Insights" class="external-link">Browser Insights</a> provides insight into Core Web Vitals (and more) to all customers for free.
</p>
<p>
This article is an <strong>in-depth review of Cloudflare Browser Insights</strong>.
</p>
</div>
<div class="toc">
<strong>Table of contents</strong>
<ul>
<li><a href="#summary">Summary</a></li>
<li><a href="#intro">Introduction to Browser Insights</a></li>
<li><a href="#js">JS Snippet & Code</a></li>
<li><a href="#metrics-insights">Metrics & Insights</a></li>
<li><a href="#improvement-points">Improvement Points</a></li>
<li><a href="#closing-notes">Closing Notes</a></li>
</ul>
</div>
</div>
<h2 id="summary">Summary</h2>
<p>
Browser Insights is a free site speed analytics service by Cloudflare, providing website owners insight into Core Web Vitals and page load time.
Positive features are ease of use and data freshness.
The bad parts include the inability to view trends over time and no differentiation between desktop and mobile.
</p>
<table class="table-striped valign-middle">
<thead>
<tr>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
<tr>
<td>Overall score</td>
<td>
<div class="stars three">
<div class="star"></div>
<div class="star"></div>
<div class="star"></div>
<div class="star"></div>
<div class="star"></div>
</div>
</td>
</tr>
<tr>
<td>Usefulness</td>
<td>
<div class="stars three">
<div class="star"></div>
<div class="star"></div>
<div class="star"></div>
<div class="star"></div>
<div class="star"></div>
</div>
</td>
</tr>
<tr>
<td>Ease of use</td>
<td>
<div class="stars five">
<div class="star"></div>
<div class="star"></div>
<div class="star"></div>
<div class="star"></div>
<div class="star"></div>
</div>
</td>
</tr>
</tbody>
</table>
<h2 id="intro">Installation and Configuration</h2>
<blockquote>
Browser insights is a new tool that lets you understand the performance of your web pages as experienced by your site visitors
</blockquote>
<p>
Browser Insights provides insight into page load time and Core Web Vitals, based on data collected in the field (Real-User Measurements), currently from Chrome and Edge browsers only.
Metrics are available per URL and for the whole site.
</p>
<h3>Benefits of using Cloudflare Browser Insights</h3>
<p>
Browser Insights tackles three major downsides of Google's Core Web Vitals data:
</p>
<table class="table-striped second-col-icon">
<thead>
<tr>
<th></th>
<th>Google</th>
<th>Cloudflare Browser Insights</th>
</tr>
</thead>
<tbody>
<tr>
<td>Data freshness</td>
<td>Metric values are updated daily</td>
<td>Metrics are updated as new data arrives</td>
</tr>
<tr>
<td>Time range</td>
<td>Past 28 days (rolling window)</td>
<td>Several time ranges can be selected, incl. last 6 hours/24 hours/week/month</td>
</tr>
<tr>
<td>Traffic threshold</td>
<td>No data available for low-traffic websites/pages</td>
<td>Low-traffic websites/pages get insights too</td>
</tr>
</tbody>
</table>
<h3>Installation</h3>
<p>
Activating Browser Insights is a simple as clicking a toggle in the Cloudflare dashboard.
Once the toggle is set to ON, Cloudflare automagically injects a JavaScript snippet into the page to collect the data.
</p>
<h3>Browser Insights Rules</h3>
<p>
Customers on the Pro plan and higher can use up to 100 rules to include/exclude requests that are measured with Browser Insights.
This feature supports wildcard matching and allows for granular control over the data being collected.
</p>
<p>
Free plan customers can't configure rules: Browser Insights is simply on or off for your zone.
</p>
<h2 id="js">JS Code & Snippet</h2>
<p>
Adding more JavaScript to your website should always be a concern, especially if it's code from a third party.
Third-party code snippets are known to be a major cause of page slowness.
</p>
<p>
Good news! The Cloudflare Browser Insights code is as good as it gets.
</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>script</span> <span class="token attr-name">defer</span> <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>https://static.cloudflareinsights.com/beacon.min.js<span class="token punctuation">"</span></span> <span class="token attr-name">data-cf-beacon</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">'</span>{"rayId":"626137cecba11fe2","version":"2021.4.0","si":10}<span class="token punctuation">'</span></span><span class="token punctuation">></span></span><span class="token script"></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>script</span><span class="token punctuation">></span></span></code></pre>
<p>
This script tag is injected at the bottom of the HTML and has the <code>defer</code> attribute, causing all browsers to load the JS file in a non-blocking way. Chrome/Edge browsers load and execute the script file with the <a href="https://medium.com/dev-channel/javascript-loading-priorities-in-chrome-57c54cfa6672" class="external-link">lowest priority</a>.
</p>
<p>
The Browser Insights JS code executes late during the page loading process so more important tasks finish first.
The code itself takes little time to execute, even on low-end mobile devices.
</p>
<p>
Using Browser Insights on your website should not have a significant negative impact on how fast your pages load and respond to user input.
</p>
<h3>Compression and Caching</h3>
<p>
Cloudflare gets many things right with the Browser Insights JS code, but suprisingly fails to serve the JS code in an performance optimal way:
</p>
<ol>
<li>No Brotli compression, just GZip</li>
<li>No caching headers: browsers <em>may</em> not cache the file</li>
<li>No Last-modified or ETag: browsers can't conditionally check expiry of the file</li>
</ol>
<p>
Hopefully someone at Cloudflare reads this article and fixes these three issues soon.
</p>
<h2 id="metrics-insights">Metrics & Insights</h2>
<h3>Core Web Vitals</h3>
<h4>Metrics and Statistics</h4>
<p>
Browser Insights shows data for the three Core Web Vitals and uses the same buckets as Google: Good, Needs Improvement and Poor.
The buckets in Browser Insights use the same <a href="https://support.google.com/webmasters/answer/9205520?ref_topic=7440006#about_data&zippy=" class="external-link">thresholds</a> as Google.
</p>
<p>
<img loading="lazy" class="bordered" src="https://www.cdnplanet.com/static/img/cloudflare-browser-insights-screenshot-w600.png" width="600" height="515" alt="Cloudflare Browser Insights screenshot">
</p>
<p>
Unfortunately, Browser Insights does not show the 75<sup>th</sup> percentile (P75) for LCP, FID and CLS.
Google's Core Web Vitals data in Google Search Console is based on the P75 and this should be interpreted as a strong signal the P75 is an important value to keep an eye on from an SEO perspective.
</p>
<h4>Sampling and Delays</h4>
<p>
The sample rate in Browser Insights is 10% of all page loads and this can't be changed, not even for Enterprise customers.
For websites with low traffic, the 10% sample rate can be a real problem: the insights are not representative (= wrong).
</p>
<p>
The metrics in Cloudflare Browser Insights are updated in near real-time, enabling customers to see the impact of website changes on Core Web Vitals early. This is excellent and a big win over the data available from Google.
</p>
<h4>Dimensions and Filtering</h4>
<p>
Browser Insights provides some basic options for selecting data of interest and getting the desired insights.
</p>
<table class="table-striped second-col-icon">
<thead>
<tr>
<th>Feature</th>
<th></th>
<th>Notes</th>
</tr>
</thead>
<tbody>
<tr>
<td>Aggregate summary stats</td>
<td><img alt="Sortof/partially" title="Sortof/partially" src="https://www.cdnplanet.com/static/img/check-circle-partially.svg" width="24" height="24" class="icon-in-table-cell-new"></td>
<td>Only the three buckets, not the P75</td>
</tr>
<tr>
<td>View trends over time</td>
<td><img alt="No" title="No" src="https://www.cdnplanet.com/static/img/close-circle.svg" width="24" height="24" class="icon-in-table-cell-new"></td>
<td>This is a major downside. You can't get the answer to a question like "did our LCP improve in the past 30 days?"</td>
</tr>
<tr>
<td>Predefined time ranges</td>
<td><img alt="Yes" title="Yes" src="https://www.cdnplanet.com/static/img/check-circle.svg" width="24" height="24" class="icon-in-table-cell-new"></td>
<td>Last 30min/6h/12h/24h. Paid plan customers also get last week and last month</td>
</tr>
<tr>
<td>Custom time range</td>
<td><img alt="Yes" title="Yes" src="https://www.cdnplanet.com/static/img/check-circle.svg" width="24" height="24" class="icon-in-table-cell-new"></td>
<td>Max 7 days for customers on the Free plan, longer for paid plan customers</td>
</tr>
<tr>
<td>Daily granularity</td>
<td><img alt="No" title="No" src="https://www.cdnplanet.com/static/img/close-circle.svg" width="24" height="24" class="icon-in-table-cell-new"></td>
<td>No, only the aggregate for the selected time range (the three buckets)</td>
</tr>
<tr>
<td>Hourly granularity</td>
<td><img alt="No" title="No" src="https://www.cdnplanet.com/static/img/close-circle.svg" width="24" height="24" class="icon-in-table-cell-new"></td>
<td>Only relevant to high-traffic sites</td>
</tr>
<tr>
<td>Filter on URL</td>
<td><img alt="Yes" title="Yes" src="https://www.cdnplanet.com/static/img/check-circle.svg" width="24" height="24" class="icon-in-table-cell-new"></td>
<td>Select options are: equal, does not equal, contains</td>
</tr>
<tr>
<td>More filters</td>
<td><img alt="Yes" title="Yes" src="https://www.cdnplanet.com/static/img/check-circle.svg" width="24" height="24" class="icon-in-table-cell-new"></td>
<td>Browser, OS and Country</td>
</tr>
</tbody>
</table>
<h4>Data Visualization & Exporting</h4>
<p>
The UI is easy to understand and works well.
+1 to Cloudflare for using a color palette that's friendly to people with color blindness.
</p>
<p>
The "Print report" feature works well (great looking printout) but missing is a 'Save as PNG' option for the charts and being able to export the data as JSON or CSV.
</p>
<h4>Documentation</h4>
<p>
The <a href="https://support.cloudflare.com/hc/en-us/articles/360033929991-Cloudflare-Browser-Insights" class="external-link">Cloudflare Browser Insights docs</a> is a long page in the Cloudflare Help Center. The page provides sufficient information about enabling and configuring Browser Insights and explains how Page Metrics data is collected and what all the Page Metrics metrics mean. The Core Web Vitals explainer section is (too) short.
</p>
<p>
A real problem in the docs is the mention of Content Security Policy (CSP):
</p>
<blockquote>
The Browser Insights performance beacon loads from https://static.cloudflareinsights.com/beacon.min.js and uses a third-party domain so that the script is cached across many website loads. You may need to update your Content Security Policy (CSP) settings to load this script.
</blockquote>
<p>
This advice is incomplete: customers must also add <code>https://cloudflareinsights.com</code> to the <code>connect-src</code> directive to allow browsers to send the site speed data back to Cloudflare with <code>navigator.sendBeacon</code>.
</p>
<h3>Page Metrics</h3>
<p>
This section provides insight into total page load time (PLT) and into how long each loading phase takes (DNS, TCP, Request, Response, Processing).
Data is collected using the browser's <a href="https://www.w3.org/TR/navigation-timing-2/" class="external-link">Navigation Timing API</a>.
</p>
<p>
<img loading="lazy" class="bordered" src="https://www.cdnplanet.com/static/img/cloudflare-browser-insights-page-metrics-screenshot-w600.png" width="600" height="515" alt="Cloudflare Browser Insights Page Metrics screenshot">
</p>
<p>
PLT is an old and deprecated site speed metric.
It's generally agreed on in the web performance community PLT adds no value in understanding the quality of user experiences on webpages.
It's useless, and in our opinion showing PLT data is worse than <i>not</i> showing it because making the data available implies it has value.
In its current form, Page Metrics weakens Browser Insights.
</p>
<p>
Is there not any value in the data about each of the phases? Potentially yes, but not in Browser Insights because you can't view data for just one of the phases (ugh).
</p>
<p>
<strong>DNS</strong>. Measures how long a DNS query takes. Includes queries answered from browser/OS/resolver cache, so tells you nothing about how fast Cloudflare's DNS is.
</p>
<p>
<strong>TCP</strong>. Measures how long it takes to establish a connection with the server. Includes the TLS handshake time, making it harder to understand spikes and diagnose issues. Connect is a better name.
</p>
<p>
<strong>Request and Response</strong>. Time between sending out the request and receiving first byte (Request) and time between first byte and last byte (Response). Both metrics <em>can</em> be useful for spotting slow loading HTML responses, but - again - not here in Browser Insights because you can't zoom in on just one metric.
</p>
<p>
<strong>Processing</strong>. Measures how long it took to render the page, including the loading of any resources that block page rendering. For most pages on the Web, this is the phase where the browser spends the most time. Without any further details about what happened during this phase and how long each task took (layout, painting, JS execution, etc), just the processing time is useless.
</p>
<p>
In the docs, Cloudflare hints on improving Page Metrics:
</p>
<blockquote>
Cloudflare plans to provide more detailed analytics for the Processing metric in the future.
</blockquote>
<p>
Waterfall charts showing timing data for everything that loaded on the page? JS execution time?
We'll have to wait and see what Cloudflare comes up with.
</p>
<h2 id="improvement-points">Key Improvement Points</h2>
<h3>Core Web Vitals</h3>
<ul>
<li>Show trends over time</li>
<li>Show the P75 value</li>
<li>Differentiate between desktop and mobile</li>
<li>Use a higher sample rate for sites with low/medium traffic levels (customer can set the sample rate, or Cloudflare auto-tunes self)</li>
<li>Add metrics like First Contentful Paint. Cloudflare's JS is not on par with the JS that Google provides (<a href="https://github.com/GoogleChrome/web-vitals/" class="external-link">open source</a>) to measure all of the Core Web Vitals as well as all the other Web Vitals that can be measured in the field</li>
</ul>
<h3>Page Metrics</h3>
<p>
Remove it. Page Metrics is a distraction.
</p>
<h2 id="closing-notes">Closing Notes</h2>
<p>
It's unclear what Cloudflare's ambition is with Browser Insights.
If Cloudflare wants to provide small/medium-sized customers with a nice little freebie then Browser Insights is perhaps good enough (minus the Page Metrics), but if the ambition is to help large/enterprise customers have solid, actionable RUM powered insights into Core Web Vitals/site speed then a lot of work needs to be done to get the service to a competitive level.
</p>
Our Cloudflare Workers and Workers KV Wishlist2020-12-08T01:00:00+01:002022-11-21T01:00:00+01:00https://www.cdnplanet.com/blog/our-cloudflare-workers-kv-wishlist/New capabilities, changes to limits and analytics improvements: ten things we want from Cloudflare to make Workers and Workers KV even better.
<div class="toc-container">
<div>
<p>
All of CDN Planet lives entirely in Cloudflare's edge platform.
The website uses <a href="https://workers.cloudflare.com/sites" class="external-link">Workers Sites</a> and our <a href="https://www.cdnplanet.com/tools/cdnfinder/">CDN Finder tool</a>, and its underlying API, is built with <a href="https://developers.cloudflare.com/workers/" class="external-link">Workers</a> and <a href="https://developers.cloudflare.com/workers/runtime-apis/kv" class="external-link">Workers KV</a>.
</p>
<p>
Everything runs smoothly, costs are low and the developer experience is great.
</p>
<p>
However, we <i>do</i> have our frustrations and a (growing) list of things that are missing/not possible that will prevent us from building and running all new upcoming services entirely in Cloudflare.
</p>
</div>
<div class="toc">
<strong>Our wishlist</strong>
<ul>
<li><a href="#1"><s>Durable Objects</s></a></li>
<li><a href="#2">Long running functions</a></li>
<li><a href="#3">Send requests to a specific POP</a></li>
<li><a href="#4">Run scheduled event from a specific POP</a></li>
<li><a href="#5">Raise Workers limits for subrequests</a></li>
<li><a href="#6"><s>Intra zone subrequests</s></a></li>
<li><a href="#7">Send subrequest to a specific IP</a></li>
<li><a href="#8"><s>Even better Workers analytics</s></a></li>
<li><a href="#9">Even better Workers KV analytics</a></li>
<li><a href="#10"><s>Search KV object in dashboard</s></a></li>
</ul>
</div>
</div>
<h2 id="1">1. Durable Objects</h2>
<p>
<a href="https://blog.cloudflare.com/introducing-workers-durable-objects/" class="external-link">Durable Objects</a> provides <strong>strongly consistent</strong> key-value storage at the edge.
Multiple Workers instances can write to the same Durable Object simultaneously without any loss of data.
It's the new hot thing in Workers.
</p>
<p>
We plan to use Durable Objects in several upcoming services.
For some services it's a must-have to build the service entirely in Cloudflare.
</p>
<p>
The first use case is as simple as it is important: counters.
The Durable Objects announcement blog post shows the example of an atomic counter and that is <i>exactly</i> what we need.
</p>
<blockquote>This counter is consistent even when receiving simultaneous requests from multiple clients -- none of the increments or decrements will be lost.</blockquote>
<p>
We have signed up for the Durable Objects limited beta
<s>. Unfortunately, we have not been granted access yet.</s> and were granted access a few months later.
We're excited to get our hands dirty with Durable Objects.
</p>
<h2 id="2">2. Long running functions</h2>
<p>
Theoretically, a Workers script can take a long time to execute. The <a href="https://developers.cloudflare.com/workers/platform/limits#cpu-runtime" class="external-link">Workers documentation</a> states:
</p>
<blockquote>
There is no limit on the real runtime for a Workers script. As long as the client that sent the request remains connected, the Workers script can continue processing, making subrequests, and setting timeouts on behalf of that request. When the client disconnects, all tasks associated with that client request are canceled. You can use <code>event.waitUntil()</code> to delay cancellation for another 30 seconds or until the promise passed to <code>waitUntil()</code> completes.
</blockquote>
<p>
We want to trigger a Worker to run and perform tasks for tens of minutes, or several hours even.
Is this already achievable today with a scheduled event?
Maybe it is, because scheduled events don't have a client that can disconnect and end the worker process.
But this still would not satisfy our need for long running worker processes triggered by an HTTP request.
</p>
<p>
Cloudflare <a href="https://blog.cloudflare.com/introducing-workers-unbound/" class="external-link">announced</a> Workers Unbound in July 2020.
</p>
<blockquote>
We are extending our CPU limits to allow customers to bring all of their workloads onto Workers, no matter how intensive.
</blockquote>
<p>
Unbound seems to be what we need. <s>We're eagerly waiting on the response to our Unbound beta signup.</s>. Now that we have beta access, Unbound is actually not a good fit with our use case: Unbound has a maximum <em>duration</em> of 30 seconds and this includes the worker waiting for network requests.
</p>
<p>
In normal Workers (not Unbound), theoretically, one way to achieve long running functions is to use multiple, chained invocations of the <code>event.waitUntil()</code> method, each extending the life time of the event up to 30 seconds. We have not tested this because it seems like an ugly solution.
</p>
<p>
Following more testing, we now know, using Workers with the Bundled usage model (no limit on duration), a script that consumes little CPU time can run just fine for several minutes, even without <code>event.waitUntil()</code>.
</p>
<h2 id="3">3. Send requests to a specific POP</h2>
<p>
Using Anycast technology, Cloudflare routes traffic to the closest datacenter.
This is great in general, but we'd like to have the ability to send HTTP requests to a specific POP.
</p>
<p>
Several upcoming tools and data services on CDN Planet require triggering a Workers script to run in specific countries.
Given our assumption Cloudflare will not provide this capability, we're already investigating alternative ways to satisfy our needs.
</p>
<h2 id="4">4. Run scheduled event from a specific POP</h2>
<p>
This item on our wishlist is similar to the previous one, but relates to scheduled events instead of fetch events.
</p>
<p>
Cloudflare runs scheduled events on "underutilized machines to make the best use of our capacity and route traffic efficiently" [source: <a href="https://developers.cloudflare.com/workers/platform/cron-triggers" class="external-link">Cron Triggers documentation</a>].
</p>
<p>
So, the scheduled event can run from any of the 200+ locations where Cloudflare has a POP?
That will not work for us. We need control over the location.
</p>
<p>
We propose letting the customer select the country where the scheduled event must run.
If Cloudflare does not list all countries where they have a POP, that's fine.
A few countries per continent should be sufficient to give customers at least one suitable option.
</p>
<h2 id="5">5. Raise Workers limits for subrequests</h2>
<p>
A worker can make up to 50 subrequests per event.
</p>
<p>
This <a href="https://developers.cloudflare.com/workers/platform/limits#subrequests" class="external-link">limit</a> is in place to prevent a worker from DDoS-ing a remote service.
Makes sense, but for our use case (fetch data from remote services, e.g. DNS, for thousands of domains) it's a limit that directly increases the complexity in our overall architecture because of how we need to work around the limit.
</p>
<p>
We'd love to see this limit be replaced by 'Not more than X subrequests per Y seconds'.
This allows a long running worker to do many subrequests without the risk of the worker causing problems for a remote service.
</p>
<h2 id="6">6. Intra zone subrequests</h2>
<p>
<strong>Update</strong> Cloudflare Workers now has <a href="https://developers.cloudflare.com/workers/runtime-apis/service-bindings/" class="external-link">Service bindings</a> :
</p>
<blockquote>
Service bindings are an API that facilitate Worker-to-Worker communication via explicit bindings defined in your configuration. A Service binding allows you to send HTTP requests to another Worker without those requests going over the Internet.
</blockquote>
<p>
<s>Currently, a worker cannot send a subrequest to another worker in the same zone (a zone is a domain, e.g. cdnplanet.com).
This limit is in place to prevent infinite loops/redirects that may exhaust resources at Cloudflare.</s>
</p>
<p>
<s>Although working around this limitation is not a big deal - add a second zone and split functionality in Workers across zones - it does, like the other workarounds, add complexity.</s>
</p>
<p>
<s>Our suggestion: allow intra zone subrequests but with a limit on how many intra zone subrequests a worker can make (see previous item).</s>
</p>
<h2 id="7">7. Send subrequest to a specific IP</h2>
<p>
Imagine you're getting complaints from users in Europe.
Apparently, your site is slow and you want to find out if the CDN is causing the slowness.
You'll want to quickly gain insight into the current performance and behaviour of the CDN POPs in Europe.
</p>
<p>
What do you do?
Fetch and parse CDN server logs? Kick off instant tests with your synthetic monitoring service?
Quickly collecting all the right data can be quite a challenge.
</p>
<p>
We're building a service to help troubleshoot CDN content delivery issues.
One of the required capabilities in this service is sending an HTTP request to a specific CDN POP.
</p>
<p>
For CDNs using Anycast routing technology (e.g. Cloudflare and StackPath) this can only be done if the CDN provides either the unicast IPs of its POPs or an FQDN to send the request to.
For non-Anycast CDNs - CloudFront, CDN77 and others - we need to get the IP of the POP and be able to send an HTTP request to that IP.
</p>
<p>
Getting the IPs of POPs of non-Anycast CDNs is whole topic in itself and not covered in this article.
Let's get into the part of actually sending an HTTP request to a specific IP address from inside a worker.
</p>
<p>
The example here is the Amazon.com website.
Amazon uses multiple CDNs - Akamai, Fastly and CloudFront - and we'll work with CloudFront.
</p>
<p>
The IP address <code>99.86.238.192</code> currently maps to the Amazon CloudFront POP in Vienna, Austria.
The code in a Workers script to send a request to that POP could look like this:
</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">const</span> response <span class="token operator">=</span> <span class="token keyword">await</span> <span class="token function">fetch</span><span class="token punctuation">(</span><span class="token string">"https://99.86.238.192/"</span><span class="token punctuation">,</span> <span class="token punctuation">{</span><br> headers<span class="token operator">:</span> <span class="token punctuation">{</span><br> Host<span class="token operator">:</span> <span class="token string">"www.amazon.com"</span><br> <span class="token punctuation">}</span><br><span class="token punctuation">}</span><span class="token punctuation">)</span></code></pre>
<p>
This will throw an error in the Cloudflare worker.
Fetch does not allow setting the <code>Host</code> header. It's one of the <a href="https://developer.mozilla.org/en-US/docs/Glossary/Forbidden_header_name" class="external-link">forbidden header names</a> that cannot be modified programmatically.
</p>
<p>
Workers has the <a href="https://developers.cloudflare.com/workers/runtime-apis/request#requestinitcfproperties" class="external-link">request property</a> <code>resolveOverride</code> and from it's name one might think this is exactly what we need.
Unfortunately, it is not, because the property does not take an IP address as its value.
</p>
<blockquote>
The value of <code>resolveOverride</code> specifies an alternate hostname which will be used when determining the origin IP address, instead of using the hostname specified in the URL ... resolveOverride will only take effect if both the URL host and the host specified by resolveOverride are within your zone ... If you need to direct a request to a host outside your zone (while keeping the Host header pointing within your zone), first create a CNAME record within your zone pointing to the outside host, and then set resolveOverride to point at the CNAME record.
</blockquote>
<p>
Perhaps the solution lies in using Cloudflare's <a href="https://support.cloudflare.com/hc/en-us/articles/206190798-Using-Resolve-Override-in-Page-Rules" class="external-link">Resolve Override</a> Page Rule. This rule configures the Cloudflare CDN to override the DNS lookup by specifying the hostname Cloudflare must use to resolve to an IP address. The rule only takes a hostname that exists in Cloudflare DNS in your zone. We'd create an A record pointing <code>www.amazon.com.cdnplanet.com</code> to <code>99.86.238.192</code>.
</p>
<p>
The big question here is: do Page Rules apply to subrequests from inside a worker?
Cloudflare's community site has a <a href="https://community.cloudflare.com/t/how-do-workers-combine-with-page-rules/20830/10" class="external-link">conversation</a> about this, where
<a href="https://community.cloudflare.com/t/how-do-workers-combine-with-page-rules/20830/10" class="external-link">Kenton Varda</a> (Cloudflare Workers tech lead) writes:
</p>
<blockquote>
security features run "in front of" workers, and everything else runs "behind", i.e. on subrequests made with <code>fetch()</code>
</blockquote>
<p>
So yes, Resolve Override should apply to subrequests from inside a worker but even if it indeed works as expected it does not help us:
</p>
<ol>
<li>Resolve Override in Page Rules is for Enterprise customers only</li>
<li>We need to dynamically override DNS resolution from inside the worker</li>
</ol>
<p>
Solving problem 1 is simply about money.
The solution to problem 2 lies in updating the Page Rule via the Cloudflare API from the worker right before doing the subrequest fetch.
This should work but it's not efficient and we may run into the API rate limit.
</p>
<p>
We'd love to have a simple, native solution in workers: a request property to specify the remote IP address:
</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">const</span> response <span class="token operator">=</span> <span class="token keyword">await</span> <span class="token function">fetch</span><span class="token punctuation">(</span><span class="token string">"https://www.amazon.com/"</span><span class="token punctuation">,</span> <span class="token punctuation">{</span><br> <span class="token punctuation">{</span> cf<span class="token operator">:</span> <br> <span class="token punctuation">{</span> setRemoteIp<span class="token operator">:</span> <span class="token string">"99.86.238.192"</span> <span class="token punctuation">}</span><br> <span class="token punctuation">}</span><br><span class="token punctuation">}</span><span class="token punctuation">)</span></code></pre>
<p>
Basically, something to similar to what the popular command line tool <a href="https://curl.se/" class="external-link">cURL</a> provides with the <code>--resolve</code> option:
</p>
<pre class="language-shell"><code class="language-shell"><span class="token function">curl</span> -svo /dev/null <span class="token string">'https://www.amazon.com/'</span> --resolve www.amazon.com:443:99.86.238.192</code></pre>
<h2 id="8">8. Even better Workers analytics</h2>
<p>
The dashboard shows metrics per worker: Requests, CPU Time and Invocation Statuses.
We use these metrics mostly to spot problems, for example by keeping an eye on how often the worker exceeded the CPU time limit.
</p>
<p>
<img loading="lazy" class="bordered" src="https://www.cdnplanet.com/static/img/cloudflare-dashboard-workers-metrics-screenshot-w600.png" width="600" height="553" alt="Cloudflare dashboard Workers metrics">
</p>
<p>
We'd like to see Cloudflare improve the dashboard by enabling customers to:
</p>
<ol>
<li>Select a time window larger than 7 days</li>
<li>View invocation statuses over time</li>
<li>View details about errors encountered during execution</li>
<li>View the top 10 most requested URL paths</li>
</ol>
<p>
<strong>Update:</strong> Cloudflare released Workers Analytics Engine in beta in May 2022 and it looks very promising. <a href="https://blog.cloudflare.com/workers-analytics-engine/" class="external-link">Read the announcement blog post</a> or the <a href="https://developers.cloudflare.com/analytics/analytics-engine/" class="external-link">docs</a>
</p>
<blockquote>
Workers Analytics Engine is a new way to quickly get analytics about anything using Cloudflare Workers.
</blockquote>
<h2 id="9">9. Even better Workers KV analytics</h2>
<p>
The dashboard gives little insight into Workers KV usage: Requests, Bandwidth and Storage per account.
</p>
<p>
We miss three things in the dashboard:
</p>
<ol>
<li>View metrics <em>per namespace</em></li>
<li>View total number of objects (account wide and per namespace)</li>
<li>View total number of objects in a list</li>
</ol>
<p>
We can get 1 and 2 from the Cloudflare API, but it would be nice to have these numbers in the dashboard as well.
Item 3 is about making it much easier to track list growth over time.
Some lists can grow to millions of objects and the API returns max 1000 object keys per list request.
So, counting the number of objects in a big list requires doing many, many API calls :(
</p>
<h2 id="10">10. Search KV object in dashboard</h2>
<p>
Sometimes it's just easier to search and view objects in a nice UI instead of on the command line.
Currently, the Cloudflare dashboard enables customers to view a list of objects and add an object to storage.
<s>We'd like to see Cloudflare add a simple search field: enter the object key (prefixed or in full), hit the search button and get the object back.
Easy.</s> <strong>Update:</strong> Cloudflare now also enables searching in KV by prefix.
</p>
The All New CDN Finder2020-06-24T02:00:00+02:00https://www.cdnplanet.com/blog/all-new-cdn-finder/Reliable, fast and accurate. The popular CDN Finder tool has been completely rebuilt in 2020 and now delivers excellent results.
<p>
<a href="https://www.cdnplanet.com/tools/cdnfinder/">CDN Finder</a> is a tool that enables you to quickly and easily find out which CDN(s) a website is using, or identify the CDN behind a hostname.
</p>
<p>
CDN Finder has been quite popular since its initial launch in 2011, but unfortunately, for the past few years the CDN Finder tool did not provide a great service.
Too often, the tool was broken and users would get the "Oops, something went wrong" message :(
</p>
<p>
Also, when the tool <i>did</i> work, the results were not always good: it either wrongfully didn't detect a CDN, identified the wrong CDN or presented the old name of the CDN (e.g. Level 3 instead of Lumen).
</p>
<p>
The good news is: all those problems are gone now!
The CDN Finder tool has been completely rebuilt from scratch, resulting in a reliable, fast and accurate service.
</p>
<p>
Read on to learn how we made CDN Finder better.
</p>
<h2>CDN Finder is Closer To The User</h2>
<p>
Not only the CDN Finder, but the whole CDN Planet website was rebuilt in May 2020 and runs on <a href="https://workers.cloudflare.com/sites" class="external-link">Cloudflare Workers Sites</a>.
</p>
<p>
<a href="https://www.cdnplanet.com/cdns/cloudflare/#network">Cloudflare</a> has POPs in 200 cities across 90+ countries, so most CDN Planet users are just milliseconds away from the nearest edge location.
</p>
<p>
<img src="https://www.cdnplanet.com/static/img/cloudflare-network-map-screenshot-w600.png" width="600" height="360" alt="Cloudflare Network Map">
</p>
<p>
The CDN Finder runs entirely at the edge in a Worker and needs less than 1 millisecond to execute, so the total time of a CDN Finder run really depends on how fast the Worker can fetch responses from remote websites/services.
</p>
<h2>Fast, Efficient Page Analysis</h2>
<p>
The old CDN Finder loaded and rendered the page in a headless browser.
This was nice because CDN Finder had full visibility into everything that was loaded into the page, including third-party content/trackers/widgets/ads/etc. As result, CDN Finder could provide an answer to the question "are all resources on this page served from a CDN?".
</p>
<p>
While the answer to that question provides value to some users, the full page render step adds technical complexity to CDN Finder, makes the tool more error prone and - as it turns out - is not absolutely required to provide a good answer to the <i>primary</i> question: <strong>"which CDN is this website using?"</strong>.
</p>
<p>
The new CDN Finder leverages the power of <a href="https://developers.cloudflare.com/workers/reference/apis/html-rewriter/" class="external-link">Cloudflare's HTMLRewriter</a>, an awesome and fast API to parse the HTML of a remote web page.
The HTMLRewriter finds all external page resources of type image, stylesheet, JavaScript, font, video and iframe that are referenced directly in the HTML, and has some logic to not count the same page resource twice (e.g. a JS file that is referenced in both a <code>preload</code> and a <code>script</code> element).
Next, CDN Finder uses this list of page resources to identify the CDN for each unique hostname:
</p>
<ul>
<li>Fetch the page resource and inspect the response headers</li>
<li>Perform a DNS lookup and inspect the full CNAME chain</li>
</ul>
<p>
In most cases, this is sufficient to accurately detect the CDN, but only after we greatly improved our 'Header-to-CDN' and 'CNAME-to-CDN' lists:
</p>
<h2>Improved CDN Matching Lists</h2>
<p>
First, we removed CDNs that not longer existed (e.g. Instartlogic) and added new CDNs like <a href="https://www.cdnplanet.com/cdns/arvancloud/">ArvanCloud</a> and <a href="https://www.cdnplanet.com/cdns/kingsoftcloud/">Kingsoft Cloud</a></p>
<p>
Next, we needed to tackle the 'server header problem': because CDN Finder now runs on Cloudflare in a Worker, every URL that is fetched by CDN Finder comes back into the Worker through the Cloudflare CDN cache and so the response always has the Cloudflare CDN headers, including <code>server: cloudflare</code>. This is unfortunate because for quite a few CDNs (including Cloudflare!) the CDN Finder relied on the value of that header to identify the CDN.
</p>
<p>
We solved this problem for all impacted CDNs by finding a different uniquely identifying header (e.g. <code>lswcdn-country-code</code> for <a href="https://www.cdnplanet.com/cdns/leaseweb/">Leaseweb</a>) or by improving our CNAME chain inspection logic. But for Cloudflare, we needed something extra.
</p>
<h2>Identify the CDN by IP Address</h2>
<p>
When CDN Finder can't detect the CDN from the response headers or from the CNAME chain,
the last step is to lookup the name of the network the server IP(s) belong to.
</p>
<p>
CDN Finder uses IPtoASN, a <a href="https://iptoasn.com/" class="external-link">free IP address to ASN database</a> service with a public API, built by <a href="https://github.com/jedisct1/" class="external-link">Frank Denis</a> and updated hourly.
</p>
<p>
IPtoASN also runs on Cloudflare so lookups from CDN Finder are super fast.
</p>
<p>
Soon after implementing this IP-to-ASN lookup and test driving the new CDN Finder with many different sites, we discovered this is not just a solution for detecting Cloudflare, but a necessary step in accurately detecting other CDNs in specific cases.
</p>
<p>
Our current ASN-to-CDN list is this:
</p>
<table class="table-condensed table-striped">
<thead>
<tr>
<th>ASNs</th>
<th>CDN</th>
</tr>
</thead>
<tbody>
<tr>
<td>13335</td>
<td>Cloudflare</td>
</tr>
<tr>
<td>15133</td>
<td>Edgio</td>
</tr>
<tr>
<td>16625, 20940</td>
<td>Akamai</td>
</tr>
<tr>
<td>20446</td>
<td>StackPath</td>
</tr>
<tr>
<td>22822</td>
<td>Limelight</td>
</tr>
<tr>
<td>54113</td>
<td>Fastly</td>
</tr>
</tbody>
</table>
<h2>On The Roadmap</h2>
<p>
We'll continuously improve CDN Finder based on user feedback, analysis of the CDN Finder logs and market developments (e.g. a new CDN enters the market).
</p>
<p>
Three items are currently on our todo list:
</p>
<ul>
<li>Identify popular hosting services (e.g. AWS S3) and label these as 'not a CDN'</li>
<li>Better inform the user what happened in case of error</li>
<li>Publish a changelog</li>
</ul>
<p>
Have an idea for how we can make CDN Finder even better? Let us know on <a href="https://twitter.com/cdnplanet" class="external-link">Twitter</a>
</p>
<!-- <h2>API Access</h2>
<p>
CDN Finder is the first service that is built on top of the CDN Planet API.
</p>
<p>
Do you want API access to CDN Finder, for example to do batch processing? Send an email to <a href="mailto:info@cdnplanet.com">info@cdnplanet.com</a>
</p> -->
Introducing the CDN Performance Checker tool2018-03-12T01:00:00+01:00https://www.cdnplanet.com/blog/introducing-cdn-performance-checker-tool/Check your CDN's performance from 10 residential locations around the world, including Brazil, France and India.
<p>
Getting complaints about your site being slow or your app not loading?<br>
Validate your CDN's performance with our new <a href="https://www.cdnplanet.com/tools/cdn-performance-check/">CDN Performance Checker</a> tool!
</p>
<p>
The <a href="https://www.cdnplanet.com/tools/cdn-performance-check/">CDN Performance Checker tool</a> instantly checks if an object will load fast from your CDN.
The tests run from 10 <del>residential</del> <ins>datacenter</ins> locations across the globe, including Brazil, France and India.
</p>
<div class="notice-msg info">
The original CDN Performance Checker tested from residential locations (the last mile), powered by the TurboBytes Pulse service (now offline).
</div>
CDN Debugging Tips: Part 12017-08-11T02:00:00+02:00https://www.cdnplanet.com/blog/cdn-debugging-tips-part-1/In this first article in our series about CDN debugging, we share three tips for collecting useful data. Good data is essential when trying to find the problem with your CDN's content delivery.
<p>
This article is a first in a series of three on the topic of CDN debugging.
Here we will share three tips for collecting useful data, which really is the first thing you want to do when you believe something is wrong with your CDN.
</p>
<h2 id="1">curl -svo /dev/null</h2>
<p>
Curl (or cURL) is a command line tool widely used for testing and troubleshooting client-server data transfer over HTTP.
The free tool comes preinstalled on Linux and Mac OS X machines and <a href="https://curl.haxx.se/download.html" class="external-link">curl for Windows</a> is available too.
</p>
<p>
When using curl to debug CDN behavior, don't ever use <code>curl -I</code>.
Using the <code>I</code> flag results in sending a HEAD request and that is often pointless and unintended.
Your users will send GET requests not HEAD requests and the CDN may treat HEAD requests very differently from GET requests.
So remember: don't use <code>curl -I</code>, ever.
</p>
<p>
Your curl commands should start with <code>curl -svo /dev/null</code>.
The <code>-svo /dev/null</code> is a series of flags:
</p>
<p><code>-s</code> enables silent mode; don't show progress meter or error messages.</p>
<p><code>-v</code> enables verbose output</p>
<p><code>-o /dev/null</code> sends output to a file, but not really because the /dev/null item does not save anything (note: on Windows, use <code>-o NUL</code></p>
<p>
So, <code>-svo /dev/null</code> gives you a nice and clean display of information, most importantly the request and response headers.
</p>
<p>
The <code>-H</code> flag can be used to send an arbitrary request header, for example a specific User-Agent string: <code>-H 'User-Agent: Mozilla/5.0 (iPhone; CPU iPhone OS 6_0 like Mac OS X) AppleWebKit/536.26 (KHTML, like Gecko) Version/6.0 Mobile/10A5376e Safari/8536.25</code>
</p>
<p>
Below you see an example curl command for fetching the (gzip) compressed version of <code>https://edition.cnn.com/css/2.33.0/global.css</code>.
</p>
<pre>
curl -svo /dev/null --compressed 'https://edition.cnn.com/css/2.33.0/global.css'
</pre>
<!-- <p><a href="/blog/cdn-debugging-tips-part-2/">CDN Debugging Tips: Part 2</a> shows how the <code>-H</code> flag comes in handy for getting extra debug info back from the CDN.
</p> -->
<p>
Learn more about curl and all the flags by reading the <a href="https://curl.haxx.se/docs/manpage.html" class="external-link">curl manual page</a>.
</p>
<h2 id="2">Don't test against just one POP</h2>
<p>
When debugging your CDN, make sure you get don't fall in the trap of looking into the behavior of just one POP.
It's important you find out if the problem is POP specific or not and the only way to do that is to test against several/many POPs.
But how do you do that?
</p>
<p>
Services like Pingdom or Catchpoint have test machines across the globe, and this is great because you can perform tests against POPs in many locations.
However, the visibility you can get using such services is suboptimal.
For example: in France, Level 3 CDN has POPs in Marseille and Paris, but Pingdom only has servers in a datacenter in Strasbourg.
When testing from the Pingdom servers in Strasbourg, you'll likely hit the same one of two Level 3 POPs every time and consequently know nothing about the other POP.
</p>
<p>
Ideally, your CDN provides you a way to send a request to a specific POP, so you can run curl tests against each and every POP comfortably from your office computer. Unfortunately, not many CDNs provide this feature.<br>
As far as we know, Edgio and Highwinds (part of StackPath) enable you to debug a specific POP.
Fastly enables <a href="https://community.fastly.com/t/ways-to-test-your-caching-with-curl/480/4" class="external-link">sending requests to a specific cache node</a> (e.g. cache-lax1427) which is nice but only useful if you know the cache node that is serving your content. In our opinion, it's more useful to be able to send a request to 'the Amsterdam POP'.
</p>
<p>
To send a request to a specific target endpoint (e.g. a CDNetworks server in Amsterdam), use the <code>-H</code> flag to add a Host header with your domain:
</p>
<pre>
curl -svo /dev/null -H 'Host: cdn.yourdomain.com' 'https://91.194.205.21/path/to/file'
</pre>
<h2 id="3">Test from real user networks, not datacenters</h2>
<p>
Even if you can run curl tests against specific CDN POPs from your office location, or from datacenters across the globe, you're still somewhat in the dark.
Real users don't live in your office or in datacenters. They are at home, at work and on the go and connected to last-mile networks.
</p>
<p>
It's not uncommon for a CDN's performance to go bad only for users on one particular last-mile network.
The problem may be a congested network path, suboptimal routing (e.g. users on AS9143 (Ziggo) in NL are routed to New York instead of Amsterdam) or the ISP's DNS resolvers can't reach the CDN's DNS nameservers.
The only way to spot these kind of issues is by testing from devices connected to the last-mile (or: eyeball) networks.
</p>
<!--<p>
Read <a href="/blog/cdn-debugging-tips-part-2/">CDN Debugging Tips: Part 2</a> ... <strong>3 tips to <i>prevent</i> problems with your CDN.</strong>
</p>-->CDN market news and announcements in June 20172017-07-05T02:00:00+02:00https://www.cdnplanet.com/blog/cdn-market-news-announcements-june-2017/Learn what happened in the CDN market in June 2017.
<p>
Learn what happened in the CDN market in June 2017: a new product offering by CDNetworks, new price plans at CDN77 and the all new Cloudflare Apps.
</p>
<h2>CDNetworks launches Cloud Security 2.0</h2>
<small class="pub-date">June 13 2017</small>
<p>
CDNetworks has announced the launch of Cloud Security 2.0. The newly released application includes next generation behavioral web application firewall (WAF) technology integrated into CDNetworks' global content delivery network.
</p>
<h2>CDN77 offers new monthly plans</h2>
<small class="pub-date">June 6 2017</small>
<p>
The new monthly plans are month-to-month based plans that come with PoPs in North America, Europe (incl. Russia and Ukraine), Istanbul, Hong Kong, Singapore and Seoul. Customers can cancel any time and switch back and forth between Pay-As-You-Go and mont-to-month.
</p>
<p>
<a href="https://www.cdn77.com/pricing/medium-volumes/monthly-plans" class="external-link">CDN77 monthly plans</a>
</p>
<h2>Cloudflare announces the next generation Apps</h2>
<small class="pub-date">June 27 2017</small>
<blockquote>
Cloudflare Apps is an open platform of tools to build a high quality website. It's a place where every website owner can select from a vast catalog of Apps which can improve their websites and internet properties in every way imaginable. Selected apps can be previewed and installed instantly with just a few clicks, giving every website owner the power of technical expertise, and every developer the platform only Cloudflare can provide.
</blockquote>
<p>
Read the <a href="https://blog.cloudflare.com/cloudflare-apps-2/" class="external-link">announcement on Cloudflare blog</a>
</p>
CDN market news and announcements in May 20172017-06-15T02:00:00+02:00https://www.cdnplanet.com/blog/cdn-market-news-announcements-may-2017/A new product offering, network expansion and a big funding round. Learn what happened in the CDN market in May 2017.
<p>
Learn what happened in the CDN market in May 2017, including a new product offering by Cloudflare, network expansion by CloudFront and a big funding round for Fastly.
</p>
<h2>Limelight Networks promises to reduce video rebuffer rates by 10% or more</h2>
<small class="pub-date">May 16 2017</small>
<p>
Limelight Networks announced new performance and functionality advancements to the Limelight Orchestrate Platform, resulting in significant reduction in video sessions experiencing rebuffers. Limelight also introduced a money-back guarantee to reduce online video rebuffer rates for new customers.
</p>
<p>
Read the <a href="https://investors.limelight.com/file/Index?KeyFile=2000602234" class="external-link">full press release</a> or visit the
<a href="https://www.limelight.com/video-rebuffer-promo/" class="external-link">video rebuffer promo page</a>.
</p>
<h2>Cloudflare launches Argo, a "virtual backbone" for the modern Internet</h2>
<small class="pub-date">May 18 2017</small>
<p>
Argo analyzes and optimizes routing decisions across the global Internet in real-time.
Cloudflare claims Argo can "deliver content across our network with dramatically reduced latency, increased reliability, heightened encryption, and reduced cost vs. an equivalent path across the open Internet".<br>
Argo is priced at $5/domain monthly, plus $0.10 per GB of transfer from Cloudflare to end users.
</p>
<p>
Read the <a href="https://blog.cloudflare.com/argo/" class="external-link">press release</a>.
</p>
<h2>Fastly raises $50 million</h2>
<small class="pub-date">May 23 2017</small>
<p>
Fastly has announced $50 million in funding led by Sorenson Capital with participation from additional new investor Sapphire Ventures. The funding will be used to meet growing demand for cloud services at the edge and to ensure long-term independence.
</p>
<p>
Read the press release <a href="https://www.fastly.com/press/press-releases/fastly-closes-50-million-meet-demand-edge-cloud-services" class="external-link">here</a>.
</p>
<h2>CloudFront adds edge locations in Seattle, Dallas/Fort Worth and Tokyo.</h2>
<small class="pub-date">May 23 2017</small>
<p>
Amazon CloudFront has expanded its network with new edge locations in two cities in the United States (Seattle and Dallas/Fort Worth) and in Japan (Tokyo).
This brings the total number of CloudFront locations to 88 (including 77 points of presence and 11 regional edge cache locations).
</p>
<p>
Read the announcements <a href="https://aws.amazon.com/about-aws/whats-new/2017/05/announcing-second-edge-location-in-seattle-washington-for-amazon-cloudfront/" class="external-link">here</a> and <a href="https://aws.amazon.com/about-aws/whats-new/2017/05/amazon-cloudfront-adds-our-fourth-edge-location-in-tokyo-japan-and-our-third-in-dallas-fort-worth-texas/" class="external-link">here</a>.
</p>
CDN market news and announcements in April 20172017-05-01T02:00:00+02:00https://www.cdnplanet.com/blog/cdn-market-news-announcements-april-2017/New product offerings, network expansions and an acquisition. Learn what happened in the CDN market in April 2017.
<p>
Learn what happened in the CDN market in April 2017, including new product offerings, network expansions and an acquisition.
</p>
<h2>Fastly unveils edge cloud platform, introduces three new products</h2>
<small class="pub-date">Apr 18 2017</small>
<blockquote>
Today, Fastly publicly unveiled its edge cloud platform, which empowers the world’s most popular businesses to deliver consistently secure, fast and personalized digital experiences. Fastly is also debuting three new services, available today on its edge cloud platform: the Fastly Web Application Firewall (WAF), Image Optimizer, and Load Balancer.
</blockquote>
<p>
Read the full press release: <a href="https://www.fastly.com/press/press-releases/fastly-builds-on-content-delivery-network-heritage-unveils-edge-cloud-platform" class="external-link">Fastly Builds on Content Delivery Network Heritage, Unveils Edge Cloud Platform</a>
</p>
<h2>Limelight introduces Security Alert and WAF Express</h2>
<small class="pub-date">Apr 18 2017</small>
<p>
Limelight Networks launches two new additions to its Cloud Security Services that enhance protection against attacks on websites and unauthorized access or theft of content.
<br>
Read the full press release: <a href="https://www.limelight.com/news/cloud-security-enhancements/" class="external-link">New Limelight cloud security services offer scalable protection to safeguard websites and apps from online attacks</a>
</p>
<h2>Akamai acquires SOASTA</h2>
<small class="pub-date">Apr 7 2017</small>
<p>
SOASTA is a well-known player in the Digital Performance Management market, offering web performance load testing, performance monitoring (RUM) and analytics.
<br>
Read the full press release: <a href="https://www.akamai.com/us/en/about/news/press/2017-press/akamai-completes-acquisition-of-soasta.jsp" class="external-link">Akamai Completes Acquisition Of SOASTA</a>
</p>
<h2>Cloudflare launches 6 new POPs</h2>
<small class="pub-date">Apr 2017</small>
<p>
<a href="https://www.cdnplanet.com/cdns/cloudflare/">Cloudflare</a> expanded its global network with new POPs in Kansas City (US), Curaçao, Djibouti, Belgrade (Serbia), Munich (Germany) and Budapest (Hungary). The Cloudflare network now has 111 datacenters.
<br>
View the <a href="https://www.cloudflare.com/network/" class="external-link">Cloudflare network map</a> or read the new POP announcements on the <a href="https://blog.cloudflare.com/" class="external-link">Cloudflare blog</a>
</p>
<h2>Edgio adds a POP in Auckland, New Zealand</h2>
<small class="pub-date">Apr 4 2017</small>
<p>
Edgio (formerly Verizon Media and Edgecast) has collaborated with Megaport (Australia) Pty Ltd to expand the <a href="https://www.cdnplanet.com/cdns/edgio/">Edgio CDN</a> with a new POP in Auckland, New Zealand.
<br>
Read the full press release: <a href="https://www.tvtechnology.com/the-wire-blog/7943-507943" class="external-link">Verizon Digital Media Services collaborates with Megaport to expand the Edgio Content Delivery Network into New Zealand</a>
</p>
10 Tips to Optimize CDN Performance2017-04-26T02:00:00+02:00https://www.cdnplanet.com/blog/10-tips-optimize-cdn-performance/What can you do to optimize CDN performance? Learn about ten ways to effectively enhance content delivery speed and reliability.
<div class="toc-container">
<div>
<p>
For the most part, your CDN is responsible for the performance of your content delivery.
The CDN controls its network, load balancers, caching servers and all else involved in delivering your content from the edge to your users.
</p>
<p>
So what can <i>you</i> do to contribute to excellent content delivery performance?
</p>
<p>
Read our <strong>10 tips to optimize CDN performance</strong>.
</p>
</div>
<div class="toc">
<strong>Table of contents</strong>
<ul>
<li><a href="#1">Use high performance DNS</a></li>
<li><a href="#2">Move your origin close to your CDN</a></li>
<li><a href="#3">Have IPv6 connectivity</a></li>
<li><a href="#4">Tune your initcwnd</a></li>
<li><a href="#5">Keep connections alive forever</a></li>
<li><a href="#6">Reduce TLS connection time</a></li>
<li><a href="#7">Minimize byte size</a></li>
<li><a href="#8">Be a Cache-Control master</a></li>
<li><a href="#9">Enable conditional requests</a></li>
<li><a href="#10">Be careful with the Vary header</a></li>
</ul>
</div>
</div>
<h2 id="1">1. Use high performance DNS</h2>
<p>
Imagine you're using <code>static.example.org</code> on the CDN and <code>origin.example.org</code> as your origin.
If the authoritative DNS for <code>example.org</code> suffers from low availability and high latency, this surely has a bad impact on CDN performance.
</p>
<p>
Don't use the DNS service of your hosting provider just because it is included in the deal.
It's easy and cheap to use high performance, global anycast DNS from providers like AWS, NS1, Cloudflare, Google or Azure.
</p>
<p>
Also, make sure to use high TTLs (Time-To-Live) on your DNS records, so resolvers can cache the records for a long time.
</p>
<h2 id="2">2. Move your origin close to your CDN</h2>
<p>
If most of your users are in Asia, do not place your origin in a far-away location like Amsterdam or New York but in ... Asia.
</p>
<p>
Keeping the latency low between CDN and origin is an effective way to optimize CDN performance for cache miss responses.<br>
</p>
<p>
If you can't host your origin close to the CDN, consider using an Origin Shield.
Origin Shield is extra caching layer between the CDN edge servers and your origin.
The shield helps offload your origin and speed up cache miss responses.
Read our <a href="https://www.cdnplanet.com/guides/origin-shield/">CDN Guide: Origin Shield</a> to learn more and find out which CDNs offer origin shielding.
</p>
<h2 id="3">3. Have IPv6 connectivity</h2>
<p>
Facebook has done a lot of research into the impact of IPv6 on performance and concluded the effects are positive:
</p>
<blockquote>
We've observed that accessing Facebook can be 10-15 percent faster over IPv6.
</blockquote>
<p>
Can your CDN connect to your origin over IPv6? If yes, consider moving your origin to an IPv6-enabled hosting environment.<br>
<a href="https://aws.amazon.com/blogs/aws/now-available-ipv6-support-for-amazon-s3/" class="external-link">AWS S3 supports IPv6</a>. Unfortunately, <a href="https://cloud.google.com/compute/docs/networks-and-firewalls" class="external-link">Google Compute Engine currently does not support IPv6</a>.
</p>
Further reading:
<ul>
<li><a href="https://code.facebook.com/posts/1192894270727351/ipv6-it-s-time-to-get-on-board/" class="external-link">IPv6: It's time to get on board (Facebook)</a></li>
<li><a href="https://www.internetsociety.org/deploy360/blog/2015/04/facebook-news-feeds-load-20-40-faster-over-ipv6/" class="external-link">Facebook News Feeds Load 20-40% Faster Over IPv6</a></li>
</ul>
<h2 id="4">4. Tune your initcwnd</h2>
<p>
The initial congestion window parameter (initcwnd) on your origin server likely has a value of 10.
This means the server sends out 10 packets in the first round trip over a fresh connection.
</p>
<p>
A value of 10 is not bad, but a higher initcwnd likely has a significant positive effect on TCP performance, resulting in faster content transfer between origin and CDN.
</p>
<p>
Some CDNs have a initcwnd of 10, other CDNs have a (much) higher value. Read more: <a href="https://www.cdnplanet.com/blog/initcwnd-settings-major-cdn-providers/">Initcwnd settings of major CDN providers</a>.
</p>
<p>
Use our <a href="https://www.cdnplanet.com/tools/initcwndcheck/">Initcwnd Checker tool</a> to check your current initcwnd.
</p>
<p>
<span class="danger">Important:</span> do not 'simply' increase the initcwnd on your server(s) and assume that will work out well. It may not. A good starting point is the blog article <a href="https://www.cdnplanet.com/blog/tune-tcp-initcwnd-for-optimum-performance/">Tuning initcwnd for optimum performance</a>.
</p>
<h2 id="5">5. Keep connections alive forever</h2>
<p>
When the CDN needs to pull content from your origin, a TCP connection must exist between the two servers.
Ideally, that connection is already there and can be reused, saving roundtrips and precious milliseconds to establish a fresh connection.
</p>
<p>
The CDN or the origin may terminate the connection.
You have no control over how long the CDN keeps a connection alive, but you <i>do</i> control the keep-alive behavior on your origin.
</p>
<p>
Don't close the connection on the origin side, ever.<br>
Worried about the many CDN servers establishing TCP connections with your origin and the origin not being able to handle that?
Set up an <a href="https://www.cdnplanet.com/guides/origin-shield/">Origin Shield</a>.
</p>
<h2 id="6">6. Reduce TLS connection time</h2>
<p>
Do you have a secure HTTPS origin? If yes, there are several optimizations you can do to improve CDN performance.
To name a few: TLS False Start, TLS session resumption and TLS record size optimization.
</p>
<p>
Before getting to work with these TLS optimizations, ask your CDN if anything is needed from their side to make these optimizations have effect.
</p>
Futher reading:
<ul>
<li><a href="https://istlsfastyet.com/" class="external-link">Is TLS Fast Yet?</a></li>
<li><a href="https://hpbn.co/transport-layer-security-tls/#optimizing-for-tls" class="external-link">Optimizing for TLS</a></li>
</ul>
<h2 id="7">7. Minimize byte size</h2>
<p>
Reducing the byte size or 'weight' of your content is very effective in speeding up content delivery performance.
The less bytes over the wire, the faster your content arrives at your users.
</p>
<p>
There are many ways you can minimize byte size to enhance CDN performance
Compression is the most effective and often easiest to implement.
Read our <a href="https://www.cdnplanet.com/guides/compression/">CDN Guide: Compression</a> to learn more about Gzip, Brotli and which content types should be served compressed.
The Google Developers site has a good in-depth article about <a href="https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/optimize-encoding-and-transfer#text_compression_with_gzip" class="external-link">Text compression with GZIP</a>.
</p>
<p>
Two other techniques to reduce byte size are <a href="https://blog.stackpath.com/glossary/minification/" class="external-link">minification</a> and <a href="https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/image-optimization" class="external-link">image optimization</a>.
</p>
<h2 id="8">8. Be a Cache-Control master</h2>
<p>
How can you make the CDN cache your objects for as long as you want and maximize the cache hit ratio?<br>
Out of the box, most or all CDNs will follow the caching instructions your origin server sends by way of the <code>Cache-Control</code> header.
It's a very effective tool to improve CDN performance.
</p>
<p>
Some examples:
</p>
<p>
<code>Cache-Control: max-age=86400</code> tells the CDN and browsers the object may be cached for 86400 seconds.
</p>
<p>
<code>Cache-Control: max-age=3600, s-maxage=86400</code> informs the CDN it may cache the object for 24 hours and browsers should cache the object no longer than 1 hour. Note: not all CDNs support <code>s-maxage</code> by default.
</p>
<p>
<code>Cache-Control: max-age=86400, stale-while-revalidate=300</code> instructs the CDN and browsers to cache the object for 24 hours and, at the end of those 24 hours, the CDN may serve the stale response for up to 300 seconds while new content is being fetched from origin.
</p>
Learn more about Cache-Control:
<ul>
<li><a href="https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/http-caching" class="external-link">HTTP Caching | Web | Google Developers</a></li>
<li><a href="https://www.incapsula.com/cdn-guide/cdn-caching.html" class="external-link">The essential guide to CDN: CDN Caching (Incapsula)</a></li>
<li><a href="https://www.cdnplanet.com/guides/serve-stale/">CDN Guide: Serve Stale</a></li>
</ul>
<h2 id="9">9. Enable conditional requests</h2>
<p>
When a CDN receives a request for a stale object (in cache, but expired), the CDN must first contact the origin before sending a response to the user.
If the cached object has a <code>Last-Modified</code> and/or <code>ETag</code> header, the CDN can make the request to origin conditional by adding <code>If-Modified-Since</code> and/or <code>If-None-Match</code> headers. The origin can decide to respond with a lightweight <code>304 Not Modified</code> response (headers only) or send a heavy <code>200 OK</code> response (headers and body).
</p>
<p>
Obviously, the <code>304 Not Modified</code> response is better for performance than the <code>200 OK</code> response because the size of the response is much smaller and so less roundtrips are needed between CDN and origin.
</p>
<p>
Configure your origin server to always send a <code>Last-Modified</code> and/or <code>ETag</code> header, as this greatly helps improve cache miss performance.
</p>
<h2 id="10">10. Be careful with the Vary header</h2>
<p>
Your origin should not serve objects to the CDN with a header like <code>Vary: Referer</code>, <code>Vary: User-Agent</code>, <code>Vary: Cookie</code> or <code>Vary: User-Agent, Cookie</code>. These Vary headers may have a big negative effect on cache hit ratio and CDN performance. <!-- because they instruct the CDN to store a separate copy of the object for each unique value of the Referer, User-Agent and/or Cookie in the client-to-CDN request-->
</p>
<p>
Is your origin sending these Vary headers for no purpose? Remove them!
</p>
<strong>Important:</strong>
<ul>
<li>Don't ever use <code>Vary: *</code>. An object with that header will never be stored in a CDN cache</li>
<li>Always send the <code>Vary: Accept-Encoding</code> header with (Gzip or Brotli) compressed content</li>
</ul>
<p>
Further reading: <a href="https://www.fastly.com/blog/best-practices-for-using-the-vary-header" class="external-link">Best Practices for Using the Vary Header</a>
</p>Faster Google Fonts with Preconnect2017-04-24T02:00:00+02:002022-11-28T01:00:00+01:00https://www.cdnplanet.com/blog/faster-google-webfonts-preconnect/Improve the speed and performance of Google Fonts. Learn what the preconnect hint is and how it works.
<h2>Summary</h2>
<ul>
<li>Load the Google font files faster by adding the <code>preconnect</code> hint</li>
<li>The <code>preconnect</code> hint is supported by Chrome, Opera, Firefox and Android browsers</li>
<li>Don't forget to add the <code>crossorigin</code> attribute!</li>
</ul>
<h2>The Google Fonts performance problem</h2>
<p>
Google Fonts is a free and easy-to-use service for using high-quality fonts on your website.
Many websites use Google Fonts and its network performance is generally great (the service lives on Google's global CDN).
But yet, there is one problem with Google Fonts performance: the font files start downloading late in the browser.
</p>
<p>
The waterfall chart below shows the performance problem in action:
</p>
<p>
<img class="img-no-max-width" src="https://www.cdnplanet.com/static/img/waterfall-cdnplanet-before-preconnect.png" width="600" height="350" alt="waterfall cdnplanet before preconnect">
</p>
<div class="text-muted text-smaller margin-bottom-medium">
The CDN Planet website used the Roboto font until early 2020.
</div>
<p>
The CSS loads first, then the font files.
This behavior is not so great for performance but quite logical: the URLs of the font files are in the CSS file, so the browser must first download and parse the CSS.
</p>
<p>
Luckily, Google always serves the fonts from the same domain, <code>fonts.gstatic.com</code>, and you can instruct the browser to 'preconnect' to that domain while it is loading the CSS!
</p>
<h3>The preconnect hint</h3>
<p>
Preconnect is one of the Resource Hints as defined in the <a href="https://www.w3.org/TR/resource-hints/#preconnect" class="external-link">W3C Working Draft</a>:
</p>
<blockquote>
The preconnect link relation type is used to indicate an origin that will be used to fetch required resources. Initiating an early connection, which includes the DNS lookup, TCP handshake, and optional TLS negotiation, allows the user agent to mask the high latency costs of establishing a connection.
</blockquote>
<p>
Preconnect is <a href="https://caniuse.com/#feat=link-rel-preconnect" class="external-link">supported by most browsers</a> and improves Google Fonts performance.
</p>
<h2>Preconnect done wrong</h2>
<p>
Implementing preconnect is simple: add the <code>preconnect</code> hint to the <code>head</code> section of the HTML:
</p>
<pre>
<link rel="preconnect" href="https://fonts.gstatic.com/">
</pre>
<p>
<img loading="lazy" class="img-no-max-width" src="https://www.cdnplanet.com/static/img/waterfall-cdnplanet-after-preconnect-no-crossorigin.png" width="600" height="350" alt="waterfall cdnplanet after preconnect no crossorigin">
</p>
<p>
Hmmm, Chrome performs the DNS lookup only. Why does the browser not also establish the (secure) connection?
</p>
<p>
The answer is simple: I did not add the <code>crossorigin</code> attribute to the <code>link</code> element.
</p>
<p>
Ilya Grigorik explains why this attribute is needed in his excellent article <a href="https://www.igvita.com/2015/08/17/eliminating-roundtrips-with-preconnect/" class="external-link">Eliminating Roundtrips with Preconnect</a>
</p>
<blockquote>The <a href="https://www.w3.org/TR/css3-fonts/#font-fetching-requirements" class="external-link">font-face specification requires</a> that fonts are loaded in "anonymous mode", which is why we must provide the crossorigin attribute on the preconnect hint: the browser maintains a separate pool of sockets for this mode.</blockquote>
<h2>Preconnect done right!</h2>
<pre>
<link rel="preconnect" href="https://fonts.gstatic.com/" crossorigin>
</pre>
<p>
<img loading="lazy" class="img-no-max-width" src="https://www.cdnplanet.com/static/img/waterfall-cdnplanet-after-preconnect-with-crossorigin.png" width="600" height="350" alt="waterfall cdnplanet after preconnect with crossorigin">
<br><br>
</p>
<p>
Excellent!<br>
DNS, TCP and TLS happens in parallel with the loading of the Google Fonts CSS file.
We effectively improved Google Fonts performance by reducing the load time by ~ 100 milliseconds.<br>
</p>
<p>
The official Google Fonts embed code now includes the <code>preconnect</code> hint.
If you are still using the embed code without preconnect, head over to <a href="https://fonts.google.com/" class="external-link">Google Fonts</a>, find your font and grab the latest embed code.
</p>
<p>
Interested in learning about how you improve performance of Google Fonts even more?
Read <a href="https://sia.codes/posts/making-google-fonts-faster/" class="external-link">Making Google Fonts Faster in 2022</a> by web performance consultant Sia Karamalegos.
</p>
<h2>Learn more about webfont performance</h2>
<ul>
<li><a href="https://calendar.perfplanet.com/2016/loading-web-fonts-asynchronously/" class="external-link">Loading web fonts asynchronously</a></li>
<li><a href="https://www.filamentgroup.com/lab/font-events.html" class="external-link">How We Load Web Fonts Progressively</a></li>
<li><a href="https://www.zachleat.com/web/comprehensive-webfonts/" class="external-link">A Comprehensive Guide to Font Loading Strategies</a></li>
</ul>
CDN behavior testing with Dummy Origin2017-04-05T02:00:00+02:00https://www.cdnplanet.com/blog/cdn-behavior-testing-dummy-origin/Dummy Origin makes it easier to evaluate the (caching) behavior of a CDN. Open source, easy to install and a joy to use.
<div class="toc-container">
<div>
<p>
Before you sign the contract with your new CDN you want to know <i>for a fact</i> the CDN can do what you want and behave as you need it to behave. Right?
Sure, you can read the CDN documentation and chat with support, but if you're like us, that is not good enough.
You want to test rigorously, <i>observe</i> the CDN's behavior and match it to your functional requirements.
Dummy Origin helps you do this better and with greater ease.
</p>
</div>
<div class="toc">
<strong>Table of contents</strong>
<ul>
<li><a href="#intro">Introduction</a></li>
<li><a href="#features">Features of Dummy Origin</a></li>
<li><a href="#install">Installation & configuration</a></li>
<li><a href="#testing">Testing CDN behavior</a></li>
<li><a href="#github">It's open source!</a></li>
</ul>
</div>
</div>
<p id="intro">
Dummy Origin is a simple yet special server, written in Go. It was built specifically for the purpose of evaluating CDNs in an origin pull setup.
Dummy Origin is easy to install, lightweight, stable and fast. And <a href="#github">open source</a>!
</p>
<p>
Why use Dummy Origin and not simply put the CDN in front of your <i>real</i> origin with your <i>real</i> content?<br>
The short answer: flexibility and ease-of-use.<br>
</p>
<ul>
<li>No changes to your stack</li>
<li>Ultimate flexibility in which response headers the origin sends and seeing how the CDN then behaves</li>
<li>Easy to test a scenario like 'my origin is serving 502 responses, does the CDN serve stale?'</li>
</ul>
<h2 id="features">Features of Dummy Origin</h2>
<p>
The key feature of Dummy Origin is arbitrary response header injection.
In short: whatever you send as a query string parameter in the request will be served as a header with the response.
</p>
<p>
Send <code>?ETag="41a0544696e19971383984fdaaeb5aed"</code> in the request and get the <code>ETag:"41a0544696e19971383984fdaaeb5aed"</code> header back.
</p>
<p>
Sending a request to get back multiple headers is just as easy:<br>
<code>?Cache-Control=no-cache&Referer=https://www.cdnplanet.com/</code>
</p>
<p>
The arbitrary response header injection gives you great flexibility in testing the CDN's behavior and there is no need to constantly change the server config and restart it.
</p>
<p>
The other features of Dummy Origin:
</p>
<table class="table-striped table-three-col-333333">
<thead>
<tr>
<th>Feature</th>
<th>Description</th>
<th>Benefits</th>
</tr>
</thead>
<tbody>
<tr>
<td>Gzip</td>
<td>Compress the response with default GZip compression level. Not based on <code>Content-Type</code> but file extension. Currently supports <code>.html</code>, <code>.css</code> <code>.js</code> and <code>.json</code> extensions only but very easy to add more</td>
<td>You want to know if the CDN requests files from origin compressed or uncompressed</td>
</tr>
<tr>
<td>Conditional requests</td>
<td>Server always sends <code>Last-Modified</code> header with 200 responses and sends a 304 or 200 response if it gets a <code>If-Modified-Since</code> request (as per <a href="https://tools.ietf.org/html/rfc7234#section-4.3" class="external-link">RFC7234 section 4.3</a>)</td>
<td>Enables testing if the CDN sends conditional requests after a file in cache has expired</td>
</tr>
<tr>
<td>Range requests</td>
<td>Send the requested byte range in a 206 partial response + the appropriate <code>Content-Length</code> header</td>
<td>Essential for serving large files through the CDN, for example MP4 videos</td>
</tr>
<tr>
<td>404 and 301</td>
<td>Serve a 404 if resource does not exist, serve a 301 if request is for <code>/index.html</code></td>
<td>Does the CDN cache 404 responses? Does the CDN forward the 301 or does it follow the redirect?</td>
</tr>
<tr>
<td>Special error generator</td>
<td>When the server receives request in the format <code>GET /err/<code></code> it returns with http status <code><code></code>. <code><code></code> must be between 400 and 599</td>
<td>Handy for seeing how the CDN behaves in case of variour origin error responses. E.g. does the CDN cache 404 responses?</td>
</tr>
<tr>
<td>X-Tb-Time</td>
<td>Server always sends current time in the <code>X-Tb-Time</code> header</td>
<td>Helps determine if CDN served the response from cache and how long it has been in CDN cache</td>
</tr>
<tr>
<td>Loggly support</td>
<td>The request and response details are written to Loggly if <code>LOGGLY_TOKEN</code> environment variable is set (optional)</td>
<td><a href="https://www.loggly.com/" class="external-link">Loggly</a> gives you a near real-time view on the origin logs. This is very handy for seeing the CDN to origin requests</td>
</tr>
</tbody>
</table>
<h3>Missing in Dummy Origin</h3>
<ul>
<li>HTTPS</li>
<li>Brotli</li>
<li>Can't serve different content/headers based on the value of <code>User-Agent</code> or <code>Accept-Language</code> requests headers</li>
</ul>
<h2 id="install">Installation & configuration</h2>
<p>
Install the server: <a href="https://github.com/turbobytes/dummyorigin/blob/master/README.md" class="external-link">README.md on Github</a>.
</p>
<p>Put some files on the server. These should be similar to what you want to serve through the CDN.
You may want to use dummy files, for example
<a href="https://en.wikipedia.org/wiki/File:Joseph_Dudley.jpg?cdnp" class="external-link">15kb.jpg</a>,
<a href="https://en.wikipedia.org/wiki/File:Procnias_tricarunculata.jpg?cdnp" class="external-link">100kb.jpg</a>,
<a href="https://en.wikipedia.org/wiki/File:BlueBellsOfScotland.PNG?cdnp" class="external-link">15kb.png</a>,
<a href="https://ajax.googleapis.com/ajax/libs/webfont/1.6.26/webfont.js?cdnp" class="external-link">13kb.js</a>,
<a href="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.1/angular.min.js?cdnp" class="external-link">160kb.js</a>,
<a href="https://archive.org/download/UCONN_2014_Parade_in_Hartford_CT/UCONN_2014_Parade_in_Hartford_CT.mp4" class="external-link">108mb.avi</a>.
</p>
<div class="notice-msg info">
Do NOT put an <code>index.html</code> on the server so you can always see what files are on the origin by simply visiting the origin domain in your browser.
</div>
<p>Set things up on the CDN.
Configure the CDN to treat query strings as unique (<code>file.jpg?123</code> is not the same as <code>file.jpg?456</code>). This is often the default behavior of a CDN but make sure to check.
Also, make sure the CDN forwards all headers from origin to client including the <code>X-Tb-Time</code> header.
</p>
<p>
Configure your DNS. Use a low TTL for the origin host (e.g. 300 seconds) as this will come in handy later when you want to find out how the CDN behaves in case the origin is down (more on this later).
</p>
<h2 id="testing">Testing CDN behavior: example use cases</h2>
<p>
In these examples, <code>dummy.mydomain.com</code> is the CDN host and <code>dummy.origin.com</code> is the origin host.<br>
Our tool of choice for testing is cURL. Our cURL commands always start with <code>curl -vo /dev/null</code> so we send a GET request and can see the request and response headers, but not be bothered with the response body.
</p>
<p>
Do not use the <code>-I</code> flag to send a HEAD request to the CDN. The CDN may treat HEAD requests very differently from GET requests (and remember: your users will send GET requests not HEAD requests).
</p>
<p>
Learn more about cURL by reading <a href="https://support.cloudflare.com/hc/en-us/articles/203118044#h_0c7f48b3-fc29-4266-8c63-477fe61a11c4" class="external-link">Using cURL when Troubleshooting with Cloudflare</a> or <a href="https://curl.haxx.se/docs/manpage.html" class="external-link">the cURL manual</a>.
</p>
<h3>Don't cache and always fetch full file from origin</h3>
<p>
Not the typical CDN use case, but if this is what you need for your 'uncacheable' content, it needs to work well.
The question here is: which Cache-Control directive(s) must be used to make the CDN behave as desired?
Based on what is in <a href="https://tools.ietf.org/html/rfc7234#section-5.2.2">RFC 7234</a> it seems logical to use <code>Cache-Control:no-store</code>.
The RFC states:
<cite>The "no-store" response directive indicates that a cache MUST NOT store any part of either the immediate request or response. This directive applies to both private and shared caches.</cite>
Ok, let's try that
</p>
<pre>curl -vo /dev/null 'https://dummy.mydomain.com/15kb.jpg?Cache-Control=no-store'</pre>
<p>
Below you see the response headers from our tests with <a href="https://www.cdnplanet.com/cdns/fastly/">Fastly</a>. Surprisingly, Fastly does not honor the <code>no-store</code> and serves the file from cache after a first initial cache miss.
</p>
<pre>
HTTP/1.1 200 OK
Access-Control-Allow-Origin: *
Cache-Control: no-store
Content-Type: image/jpeg
Last-Modified: Tue, 21 Mar 2017 10:13:25 GMT
X-Tb-Time: 2017-03-30 14:39:11.358951299 +0000 UTC
Content-Length: 15887
Accept-Ranges: bytes
Date: Thu, 30 Mar 2017 14:39:23 GMT
Via: 1.1 varnish
Age: 12
X-Served-By: cache-ams4136-AMS
X-Cache: HIT
X-Cache-Hits: 1
X-Timer: S1490884763.605627,VS0,VE0
Vary: Accept-Encoding
</pre>
<p>
Should we use <code>no-cache</code> or <code>max-age=0</code>? We don't want the CDN to cache the response and send conditional requests to the origin. It is important the CDN does not store the response in cache.
</p>
<p>
Testing shows <code>no-cache</code> does not work (again, Fastly caches the responses) but <code>max-age=0</code> does work.
</p>
<h3>Cache for 300 seconds, then do origin validation</h3>
<p>
Getting a CDN to cache a file for 300 seconds is easy: <code>Cache-Control=max-age=300</code>.
The key question here is: after the file expires in cache, will the CDN send a conditional or unconditional request to origin?
One can argue the conditional request is optimal, because if the file on the origin has not changed, the origin can send a lightweight 304 response ("No, the file has not changed, it is fine to serve the cached object!") and the CDN can quickly respond to the client. This is faster than when the origin sends the full file to CDN.
</p>
<p>
RFC 7234 does <i>not</i> state a cache <i>must</i> send a conditional request for cache validation. Therefore it should be not a big surprise if the CDN sends unconditional requests (although not great for performance).
</p>
<pre>curl -vo /dev/null 'https://dummy.mydomain.com/15kb.jpg?Cache-Control=max-age=300'</pre>
<p>
<a href="https://www.cdnplanet.com/cdns/fastly/">Fastly</a> and <a href="https://www.cdnplanet.com/cdns/cdnetworks/">CDNetworks</a> play nice and send conditional requests. <a href="https://www.cdnplanet.com/cdns/stackpath/">StackPath</a> however sends unconditional requests to origin, meaning their caches always fetch the full file. We could see this in the origin logs and by inpecting the value of the <code>X-Tb-Time</code> header in the CDN to client response. We also tried with <code>Cache-Control=max-age=300,must-revalidate</code> but no luck.
</p>
<pre>
HTTP/1.1 200 OK
<strong>Date: Fri, 31 Mar 2017 11:20:16 GMT</strong>
Content-Type: image/jpeg
Content-Length: 33956
Access-Control-Allow-Origin: *
Cache-Control: max-age=20,must-revalidate
Last-Modified: Fri, 31 Mar 2017 09:49:42 GMT
<strong>X-Tb-Time: 2017-03-31 11:20:16.091275242 +0000 UTC</strong>
Server: NetDNA-cache/2.2
Accept-Ranges: bytes
<strong>X-Cache: EXPIRED</strong>
Connection: keep-alive
</pre>
<p>
We contacted StackPath about this behavior and they quickly responded to say the CDN can be configured to send conditional requests if the customer wants that.
</p>
<h3>Cache for 5 minutes, may serve stale for 15 minutes</h3>
<pre>curl -vo /dev/null 'https://dummy.mydomain.com/15kb.jpg?Cache-Control=max-age=300&stale-while-revalidate=900&stale-if-error=900'</pre>
<p>
As far as we know, <a href="https://www.cdnplanet.com/cdns/fastly/">Fastly</a> and <a href="https://www.cdnplanet.com/cdns/cdn77/">CDN77</a> are the only CDNs that support the <code>stale-while-revalidate</code> and <code>stale-if-error</code> Cache-Control extensions. We tested the behavior of Fastly in three situations:
</p>
<p>
1. Origin host does not resolve in DNS (NXDOMAIN)<br>
In summary: long after the DNS TTL of our origin must have expired in Fastly caches/systems, Fastly still served stale content. That's nice.
</p>
<p>
More interestingly, Fastly also kept fetching fresh content from our origin that had not yet made it to their caches.
Does Fastly remember the last known IP address of the origin and use that in case of origin resolution failure? Or was this behavior due to Fastly keeping a long-lived connection with the origin? We're not sure and need to dig in deeper.
</p>
<p>
2. Origin serves 400 response<br>
Something goes wrong at the origin and it starts serving <code>400 Bad request</code> responses.
We expected the <code>stale-if-error</code> to kick in here but Fastly simply forwarded the 400 response to the client.
As it turns out, our expectation was wrong because <a href="https://tools.ietf.org/html/rfc5861" class="external-link">RFC 5861: HTTP Cache-Control Extensions for Stale Content</a> states
<cite>an error is any situation that would result in a 500, 502, 503, or 504 HTTP response status code being returned</cite>
</p>
<p>
3. Origin drops all incoming traffic<br>
In our third scenario we changed our origin DNS record to point to 72.66.115.13. This is the IP address behind blackhole.webpagetest.org.
<a href="https://twitter.com/patmeenan" class="external-link">Pat Meenan</a>, creator of <a href="https://www.webpagetest.org/" class="external-link">WebPageTest.org</a> set this up back in 2011 to enable <a href="https://blog.patrickmeenan.com/2011/10/testing-for-frontend-spof.html" class="external-link">testing for frontend SPOFs in WPT</a>. The WPT blackhole will not return any response to any request.
</p>
<p>
Did Fastly serve stale? No. Fastly served 503 responses after ~ 1 second and these 503 responses were served with the <code>Retry-After: 0</code> header. This is actually to be expected with Fastly out of the box. Their <a href="https://docs.fastly.com/guides/performance-tuning/serving-stale-content#serving-stale-content-on-errors" class="external-link">Serving stale content</a> document describes the default behavior:
<cite>If Varnish cannot contact the origin for any reason, a 503 error will be generated</cite>
The doc then shows and explains the custom VCL code to have Fastly serve stale in this case (and two other cases).
</p>
<p>
FYI: StackPath can serve stale content based on customer configured status codes (404, 502, 503 etc). This works as expected but StackPath does not serve stale in case of NXDOMAIN or blackhole.<br>
Read more about serve stale in our <a href="https://www.cdnplanet.com/guides/serve-stale/" class="external-link">Serve Stale CDN Guide</a>.
</p>
<h2 id="github">It's open source!</h2>
<p>
<a href="https://github.com/turbobytes/dummyorigin" class="external-link">Dummy Origin is on Github</a> with MIT license. Fork away!
</p>
Introducing CDN Guides2017-03-12T01:00:00+01:00https://www.cdnplanet.com/blog/introducing-cdn-guides/CDN Planet introduces CDN Guides: informational articles about Content Delivery Network features like compression, purge and serve stale.
<p>
As part of the relaunch of CDN Planet, we're introducing a new content section: CDN Guides.<br>
A CDN Guide is an article packed with information about one content delivery network feature. The first three CDN Guides are about Compression, Purge and Serve Stale.
</p>
<p>
The CDN Guide will help you get a solid understanding of the feature and - perhaps most importantly - show the differences between the various CDNs. You will also find detailed specifics per CDN and links to relevant docs/content on the CDN websites and support portals.
</p>
<h2>Compression</h2>
<p>
Do all CDNs pull compressed from the origin? Which CDNs can do gzipping on the edge? What are content types that should be served compressed? What is Brotli and does your CDN support it?
<br>View the <a href="https://www.cdnplanet.com/guides/compression/">CDN Guide: Compression</a>
</p>
<h2>Purge</h2>
<p>
What is purging? Which CDNs invalidate objects and which CDNs delete objects? Can all CDNs purge by directory or content type? Is purging free and without rate limits?
<br>View the <a href="https://www.cdnplanet.com/guides/purge/">CDN Guide: Purge</a>
</p>
<h2>Serve Stale</h2>
<p>What is stale content? Do all CDNs serve stale content in case your origin is down or serving errors? Is 'serve stale' a free feature or do CDNs charge extra for it? Do any CDNs support the HTTP Cache-Control Extensions for Stale Content?
<br>View the <a href="https://www.cdnplanet.com/guides/serve-stale/">CDN Guide: Serve Stale</a>
</p>
<div class="notice-msg info">
Let us know in the comments or on <a href="https://twitter.com/cdnplanet" class="external-link">Twitter</a> what feature should be in our next CDN Guide!
</div>Root domain on CDN and performance2017-03-11T01:00:00+01:00https://www.cdnplanet.com/blog/root-domain-on-cdn-performance/When using your root/apex domain on a CDN, don't simply expect excellent performance.
<p>
This is a repost of the article I wrote for the 2016 Performance Calendar on PerfPlanet.
The original article is here: <a href="https://calendar.perfplanet.com/2016/root-domain-cdn-performance/" class="external-link">https://calendar.perfplanet.com/2016/root-domain-cdn-performance/</a>.
</p>
<hr>
<p>Nowadays it is not uncommon to publish your site on the root domain (aka 'naked domain' or 'domain apex'). Instead of using <code>www.awesomesite.com</code> you go for <code>awesomesite.com</code>. Looks good, right? Yeah.</p>
<p>Running the whole site through a CDN is also the modern thing to do and can help give your users an excellent experience. The CDN will give you an endpoint to point to in DNS and now you run into a roadblock: you can't use a CNAME on the root domain (per <a href="https://tools.ietf.org/html/rfc2181" class="external-link">RFC2181</a>). Now what?</p>
<p>Luckily for you, there are DNS providers that have a solution, usually called <code>ANAME</code> or <code>ALIAS</code>, allowing you to point to the CDN endpoint on the root domain. <!-- The 'magic' in these solutions is that the DNS provider resolves the CDN endpoint on their nameservers and then responds to the ISP or open resolver with an A record. --></p>
<p><a href="https://ns1.com/articles/cname-alias-and-linked-records" class="external-link">NS1</a>, <a href="https://www.dnsmadeeasy.com/services/anamerecords/" class="external-link">DNS Made Easy</a>, <a href="https://support.constellix.com/support/solutions/articles/47001101231-aname-as-an-alias-record" class="external-link">Constellix</a>, <a href="https://support.dnsimple.com/articles/alias-record/" class="external-link">DNSimple</a> and <a href="https://blog.cloudflare.com/introducing-cname-flattening-rfc-compliant-cnames-at-a-domains-root/" class="external-link">Cloudflare</a> have this <code>ANAME</code>/<code>ALIAS</code> solution. <a href="https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/routing-to-cloudfront-distribution.html" class="external-link">AWS Route 53</a> has it too, but you can only use it with their own CDN (CloudFront).</p>
<p>So you pick one of these DNS providers, set things up and be done with it, right? Not so fast.</p>
<h2>The problem</h2>
<p>The <code>ANAME</code>/<code>ALIAS</code> is resolved by your DNS provider's nameserver instead of by a recursive resolver (ISP, Google Public DNS, OpenDNS or other) and this may lead to end users being routed to a far-away CDN node and consequently getting a poor experience.</p>
<h3>Let's take a look at how ANAME/ALIAS resolution works:</h3>
<p>Imagine Thailand is an important market for your company, <code>awesomesite.com</code>. Logically, you work with a CDN that has a POP in Thailand. Your authoritative DNS provider has POPs in Hong Kong and Singapore, but not in Thailand.</p>
<p>The DNS server of an ISP in Thailand sends a query for <code>awesomesite.com</code> to your DNS provider and that query is routed to their Hong Kong POP. The authority's nameserver in Hong Kong now resolves the ANAME/ALIAS, by sending a query to the CDN's DNS server.</p>
<p>You can probably guess what happens: the CDN's DNS server sees a query coming in from Hong Kong and it hands out the IPs for the CDN POP in Hong Kong. As a result, people in Thailand visiting <code>awesomesite.com</code> get the content from the CDN in Hong Kong (100 ms RTT) instead of from Thailand (30 ms RTT).</p>
<p>So far, this is just theory. Let's see this in action in the real world!</p>
<p><a href="https://www.conversant.tv/content-delivery-network/" class="external-link">SwiftServe CDN</a> has POPs in Thailand (TH) and Hong Kong (HK). NS1 has POPs in Asia, including in HK and SG, but not in TH. For this test, I set up our domain <code>startrender.com</code> in NS1, pointing to the SwiftServe endpoint (<code>edge.conversant.swiftserve.com</code>) with an ALIAS record on the root domain and with a normal CNAME on <code>www.startrender.com</code>.</p>
<p>What happens for users in HK and TH on Google Public DNS and OpenDNS? Both open resolvers support <a href="https://tools.ietf.org/html/draft-ietf-dnsop-edns-client-subnet-03" class="external-link">EDNS-Client-Subnet</a> which means they send part of the client IP in the query to the CDN's DNS server, so that server can make a more informed decision and hand out the IP addresses for the best POP.</p>
<p>We use the free tool TurboBytes Pulse (DNS, HTTP and Traceroute diagnostics from 80+ machines around the globe - now offline) to first see what the IPs are of the SwiftServe POPs. We need this later as a reference.</p>
<p>Next, we run tests on the Pulse agents located in HK and TH to find out what happens in both cases: <code>www.startrender.com</code> (CNAME) and <code>startrender.com</code> (ALIAS). Guess what? </p>
<table class="table-striped table-responsive margin-bottom-small">
<thead>
<tr>
<th>Domain</th>
<th>User location</th>
<th>CDN POP location</th>
</tr>
</thead>
<tbody>
<tr>
<td>www.startrender.com</td>
<td>HK</td>
<td>HK</td>
</tr>
<tr>
<td>www.startrender.com</td>
<td>TH</td>
<td>TH</td>
</tr>
<tr>
<td>startrender.com</td>
<td>HK</td>
<td>SG</td>
</tr>
<tr>
<td>startrender.com</td>
<td>TH</td>
<td>GB</td>
</tr>
</tbody>
</table>
<p>As the table shows, using Google Public DNS and OpenDNS from HK and TH, <code>www.startrender.com</code> resolves to the desired CDN POPs. However, for <code>startrender.com</code>, users on Google Public DNS and OpenDNS in HK will end up hitting the SwiftServe POP in SG (not so bad) and users on those resolvers in TH are routed to the SwiftServe POP in London (ugh!)</p>
<p>The impact of this problem really depends on where your users are and where the POPs of your DNS and CDN providers are. If you only care about US, you're probably in the clear. Asia, LATAM, Middle East, Russia? Assume this problem exists for you.</p>
<p>Any solutions to this problem? Yes!</p>
<h2>Solution A: DNS providers do a better job</h2>
<p>The authoritative DNS provider for your domain should be smarter when resolving the ANAME/ALIAS from their nameservers, by 'doing the EDNS-Client-Subnet thing': send part of the IP address of the originating query source in its query to the CDN's DNS server.</p>
<p>If your authoritative DNS provider received the query from OpenDNS or Google Public DNS, it should pass along the client IP address subnet. If the query came from an ISP resolver, the authority server should send the /24 subnet of that resolver's IP in the query to the CDN's DNS server.</p>
<p>I'm quite sure NS1, DNS Made Easy/Constellix and DNS Simple don't send EDNS-Client-Subnet queries when resolving <code>ANAME</code>/<code>ALIAS</code>. I don't know why these DNS providers don't do this. Maybe it's because this would require caching much more responses, as responses can now be different for different recursive/client IP subnets.</p>
<h2>Solution B: get integrated authoritative DNS and CDN</h2>
<p>AWS, Akamai and CDNetworks are companies that operate a global CDN <em>and</em> provide authoritative DNS services. They can be smart and have their authoritative DNS and CDN work together, meaning the auth DNS always knows what the best CDN IPs are for whatever query comes in. Bye bye root domain problem.</p>
<p><a href="https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/routing-to-cloudfront-distribution.html" class="external-link">AWS Route 53/CloudFront</a> is doing this smart thing today. <a href="https://www.cdnplanet.com/cdns/akamai/">Akamai</a> and <a href="https://www.cdnplanet.com/cdns/cdnetworks/">CDNetworks</a>: I'm not sure but likely.</p>
<p> Note: <a href="https://www.cdnplanet.com/cdns/fastly/">Fastly</a> and <a href="https://ns1.com/" class="external-link">NS1</a> have a special integration so that's an option too.</p>
<h2>Solution C: use an Anycast CDN</h2>
<p>
Highwinds. <a href="https://www.cdnplanet.com/cdns/tata-communications/">Tata</a>. <a href="https://www.cdnplanet.com/cdns/cachefly/">CacheFly</a>. MaxCDN (without their Flex POPs). <a href="https://www.cdnplanet.com/cdns/cloudflare/">Cloudflare</a>. These are CDNs that use <a href="https://en.wikipedia.org/wiki/Anycast" class="external-link">Anycast</a> for routing users to the best POP. All POPs globally have the same IP address and users are 'magically' routed to the POP that is closest from a network topology perspective (shortest network path). For example, <code>www.cloudflare.com</code> points to <code>198.41.{214,215}.162</code> everywhere.</p>
<p>Since all POPs have the same IP address, the root domain problem does not exist.</p>
<p>Note: <a href="https://docs.fastly.com/guides/basic-configuration/using-fastly-with-apex-domains" class="external-link">Fastly</a> does not use Anycast for most customers, but can provide Anycast IP addresses if you must use their service on the root domain.</p>
<p>Note: Edgecast (now <a href="https://www.cdnplanet.com/cdns/edgio/">Edgio</a>) uses Anycast too but per region (Americas, EMEA, APAC) so there still is a chance, although small, of suboptimal routing.</p>
How to protect your CDN origin server2013-03-18T01:00:00+01:00https://www.cdnplanet.com/blog/how-protect-your-cdn-origin-server/How can you protect your CDN origin servers from malicious attacks, incl. DDoS attacks? We describe a few options, with one being the clear winner.
<p>
A website needs to be up & running at all times. In today's world, this means you need to be prepared for malicious attacks, possibly a <a href="https://en.wikipedia.org/wiki/Distributed_denial-of-service" class="external-link">DDoS attack</a>, which is <cite>"an attempt to make a machine or network resource unavailable to its intended users ... One common method of attack involves saturating the target machine with external communications requests, so much so that it cannot respond to legitimate traffic, or responds so slowly as to be rendered essentially unavailable"</cite>
</p>
<p>
Using a CDN helps combat a DDoS attack. CDNs have many high-capacity servers, so they can handle the peak in requests much better than your servers could. Also, CDNs tend to be better prepared for DDoS attack mitigation, with experienced staff, technology and processes. Nevertheless, behind the CDN sits your own server(s), commonly known as the CDN origin, that serves content to the CDN when needed. The attackers might go after the origin server and if they bring that down the CDN will not be able to download content anymore and as a result not serve the content to your end users: your site is broken.
</p>
<p>
In this post I outline two options for protecting your CDN origin.
Your goal is to only receive (and allow) requests from the CDN. Requests from everybody else must be prevented. Not countered, prevented.
</p>
<h2>IP rate limiting</h2>
<p>
One way to protect a server is to do IP rate limiting: only allow X number of requests from an IP address in a given timeframe. This will not work with a CDN because a CDN will do many valid requests from a small number of IP addresses and you want all of these to go through.
</p>
<h2>Whitelisting</h2>
<p>
Another possible method for allowing only requests from your CDN is to whitelist the CDN. This should work well in theory, but in practice it is difficult to do effectively. You can whitelist IP addresses or some unique identifier in a request header.
</p>
<h3>Option A: whitelist IP addresses</h3>
<p>
The challenge with whitelisting on IP address is that you need to always have the IPs of all the CDN edge servers that may hit your origin. This is bound to fail. Many CDNs will not give you the list of IPs and if they do, it will surely happen that they add an IP address and forget to tell you.
</p>
<h3>Whitelist a unique identifier in a request header</h3>
<p>
The idea is simple: the CDN sends something unique in the requests to the origin that you can use on the origin to identify the CDN and allow the requests. You would have to ask your CDN provider about the possibilities in order to find out if this is a viable option for you. But, even if they support this, it is not bulletproof. Request headers can be freely set by attackers. If they know you use a certain CDN and they know how that CDN identifies itselt on the origin, they can easily spoof this.
</p>
<h2>Option B: unguessable origin hostname</h2>
<p>
This is a simple trick and it is also the best solution.
Create some random, long set of alphanumeric characters and use that as the subdomain.
Example: <code>205ck07023nckhfsh92485.example.com</code>.
This hostname will then be only known to the CDN, the owner of the origin and the origin's DNS provider(s). Can it be guessed? Yes, but highly unlikely. Can it leak? Yes, but again: highly unlikely.
</p>
<p>
Simply whitelist for requests that have this hostname in the <code>Host</code> header and you're done.
</p>Real-world CDN performance for Google DNS and OpenDNS users2012-10-18T02:00:00+02:00https://www.cdnplanet.com/blog/real-world-cdn-performance-googledns-opendns-users/Number 3 in a series of 3 blog posts gives insight in the relative performance the CDNs deliver to Google DNS and OpenDNS users in different countries.
<p><strong>This is article 3 in a series of three blog posts.</strong></p>
<p>
Two days ago we published <a href="https://www.cdnplanet.com/blog/google-dns-opendns-and-cdn-performance/">Google DNS and OpenDNS usage stats</a> and in yesterday's article we explained edns-client-subnet, gave an overview of <a href="https://www.cdnplanet.com/blog/which-cdns-support-edns-client-subnet/">which CDNs support edns-client-subnet</a> and stated that the impact of <i>not</i> supporting edns-client-subnet on CDN performance is high for several CDNs, including Akamai. Today we present real-world performance data that backs this up and gives good insight in how CDN performance for Google DNS and OpenDNS users compares against all users.
</p>
<h2>Performance impact for small file static object</h2>
<p>
As mentioned before, we collect performance data at TurboBytes for many CDNs using a custom RUM (Real User Monitoring) solution. In a nutshell: JavaScript code in webpages, executes after page finishes loading and then fetches a 15 KB file from one of the CDNs. The timing data gets beaconed back to our server. Our DNS tests are initiated from the same JavaScript code and that enables us to gain insight in CDN performance for users on Google DNS or OpenDNS, versus all users.
</p>
<p>
Below you see four charts, one for each of these countries: United States, Italy, Indonesia and Brazil. Each chart shows, for several CDNs, by how much median total time to download that 15 KB file improved or got worse for users on Google DNS, OpenDNS or either one of these public DNS services, versus all users.<br>
The charts can help answer questions like:
<ul>
<li>In the US, do OpenDNS users get better or worse performance on Akamai?
</li><li>Is CDN performance better on Google DNS or OpenDNS in Italy?
</li></ul>
</p><p>Note: a positive percentage means performance is worse, negative means performance is better.
</p>
<h3>United States</h3>
<p>
<img class="img-no-max-width" src="https://www.cdnplanet.com/static/img/CDN-loadtime-median-delta-publicdns-vs-other-US-Oct2012.png" width="598" height="589" alt="Delta in median total loadtime for 15 kb object - Public vs other DNS resolvers - US - Oct 2012"><br><br>
Akamai performance degrades by 20% - 40% on both public DNS services and by that is the CDN that is most negatively impacted.
Performance of Internap, Edgecast and CloudFront for Google DNS users is much worse than for all users, while it actually improves a bit for OpenDNS users.
ChinaCache needs to look into how they route OpenDNS users. Something is going horribly wrong there.
With the exception of the Edgecast/Google DNS combination, for all CDNs that do anycast for HTTP (and Fastly), performance is slightly better for the users of the two public DNS services versus all users.
<br>
We analyzed ~280,000 measurements per CDN, except for Akamai (~80,000).The lower count for Akamai does not impact the data shown here. We validated that by looking at different 80,000 sized samples for other CDNs.
</p>
<h3>Italy</h3>
<p>
<img loading="lazy" class="img-no-max-width" src="https://www.cdnplanet.com/static/img/CDN-loadtime-median-delta-publicdns-vs-other-IT-Oct2012.png" width="594" height="588" alt="Delta in median total loadtime for 15 kb object - Public vs other DNS resolvers - Italy - Oct 2012"><br><br>
For most CDNs, performance improves a bit, on average circa 10%. But there are three CDNs who give Google DNS and OpenDNS users a relatively bad performance: Internap, Level3 and ChinaCache. Internap shows the same picture as for US: performance is much worse for Google DNS users, while slightly better for OpenDNS users. Level3 has no issues in the US, but in Italy OpenDNS users get almost a 100% worse performance compared to all users. ChinaCache seems to have the same issue(s) as in the US: fine for Google DNS users but a whopping ~170% worse performance for OpenDNS users.
<br>
Measurements per CDN is ~30,000 (Akamai ~20,000).
</p>
<h3>Indonesia</h3>
<p>
<img loading="lazy" class="img-no-max-width" src="https://www.cdnplanet.com/static/img/CDN-loadtime-median-delta-publicdns-vs-other-ID-Oct2012.png" width="592" height="593" alt="Delta in median total loadtime for 15 kb object - Public vs other DNS resolvers - Indonesia - Oct 2012"><br><br>
Google DNS and OpenDNS are very popular in Indonesia, so CDNs that want to deliver good performance there should make sure all is good for users of those public DNS services. The chart clearly shows that is far from true. Let's start positive: CacheFly and Bitgravity are the only CDNs that delivers better performance to users of both public DNS services. Performance of Highwinds, CloudFront, NetDNA and Fastly is only impacted a little bit and Edgecast and CDN77 are not so bad either. Internap, CDNetworks, Level3, ChinaCache and Akamai are the ones that need to take action, and especially Akamai and Internap. They have the biggest, negative performance delta for Google DNS users (30%+) and Google DNS is far more popular than OpenDNS in Indonesia.
<br>
Measurements per CDN is ~80,000 (Akamai ~45,000).
</p>
<h3>Brazil</h3>
<p>
<img loading="lazy" class="img-no-max-width" src="https://www.cdnplanet.com/static/img/CDN-loadtime-median-delta-publicdns-vs-other-BR-Oct2012.png" width="590" height="593" alt="Delta in median total loadtime for 15 kb object - Public vs other DNS resolvers - Brazil - Oct 2012"><br><br>
The situation in Brazil is ... different.
There is really no CDN that delivers significantly better performance to Google DNS or OpenDNS users.
Only Highwinds delivers a mere 4% better performance to Google DNS users.
Highwinds has a local POP in Brazil and Google DNS has resolvers in Brazil and apparently this works out.
CloudFront, Akamai, CDNetworks and CDN77 also have POP(s) in Brazil, but they deliver relatively bad performance to Google DNS users, with Akamai performance being impacted most (+57% !).
Edgecast stands out too with a ~40% worse performance for Google DNS users. That is odd, because Edgecast does not have a POP in Brazil or in any other country in South-America. They always serve into Brazil from the US. So why is performance 40% worse for Google DNS users from Brazil? Are they serving from a different POP in the US? We don't know.
CloudFront is also likely serving from US instead of from their POP in Brazil.
<br>
Measurements per CDN is ~80,000 (Akamai ~45,000).
</p>
<h3>The big table</h3>
<p>
Select a CDN from the dropdown menu to view a table with the total time median delta for Google DNS, OpenDNS or the aggregate of these public DNS services, per country.
The column 'Pct' shows what % of users in that country used that DNS service and the column 'Delta' shows by how much CDN performance differs from performance all users get. By default the table is sorted on percentage of users on a public DNS service, from high to low, but you can sort on any column by clicking one of the small arrows.
</p>
<div data-ng-controller="BlogPostCtrl">
<h4 data-ng-hide="true">Akamai</h4>
<p>A positive percentage means performance is worse, negative means performance is better.</p>
<div data-angulartable="" data-ng-model="tbl_by_cdn" class="datatable">
<table class="angulartable" style="border:solid"><thead><tr><th colspan="1">Region</th><th colspan="1">Count</th><th colspan="2">OpenDNS</th><th colspan="2">Google DNS</th><th colspan="2">All Public DNS</th></tr><tr><th colspan="1"></th><th colspan="1"></th><th colspan="1">Pct</th><th colspan="1">Delta</th><th colspan="1">Pct</th><th colspan="1">Delta</th><th colspan="1">Pct</th><th colspan="1">Delta</th></tr></thead><tbody><tr class="odd"><td>Turkey</td><td>11,445</td><td>11.94 %</td><td>4.67 %</td><td>26.10 %</td><td>13.27 %</td><td>38.03 %</td><td>10.65 %</td></tr><tr class="even"><td>Viet Nam</td><td>7,833</td><td>5.34 %</td><td>85.70 %</td><td>27.75 %</td><td>6.01 %</td><td>33.09 %</td><td>16.35 %</td></tr><tr class="odd"><td>Indonesia</td><td>46,051</td><td>5.28 %</td><td>27.91 %</td><td>16.41 %</td><td>23.84 %</td><td>21.69 %</td><td>24.97 %</td></tr><tr class="even"><td>Malaysia</td><td>38,382</td><td>2.06 %</td><td>144.82 %</td><td>10.07 %</td><td>-32.96 %</td><td>12.13 %</td><td>-6.59 %</td></tr><tr class="odd"><td>Italy</td><td>20,715</td><td>2.92 %</td><td>7.35 %</td><td>7.21 %</td><td>-2.13 %</td><td>10.13 %</td><td>0.24 %</td></tr><tr class="even"><td>Thailand</td><td>9,963</td><td>1.89 %</td><td>218.66 %</td><td>7.50 %</td><td>16.13 %</td><td>9.38 %</td><td>32.49 %</td></tr><tr class="odd"><td>Brazil</td><td>46,212</td><td>1.67 %</td><td>61.25 %</td><td>7.65 %</td><td>57.23 %</td><td>9.32 %</td><td>58.12 %</td></tr><tr class="even"><td>India</td><td>31,979</td><td>2.65 %</td><td>96.11 %</td><td>6.19 %</td><td>76.42 %</td><td>8.84 %</td><td>81.87 %</td></tr><tr class="odd"><td>Russian Federation</td><td>12,003</td><td>0.90 %</td><td>32.83 %</td><td>7.76 %</td><td>-14.13 %</td><td>8.66 %</td><td>-12.46 %</td></tr><tr class="even"><td>Poland</td><td>28,595</td><td>1.06 %</td><td>-7.44 %</td><td>7.39 %</td><td>9.93 %</td><td>8.46 %</td><td>7.44 %</td></tr><tr class="odd"><td>Czech Republic</td><td>6,498</td><td>0.54 %</td><td>-11.68 %</td><td>7.88 %</td><td>0.34 %</td><td>8.42 %</td><td>0.34 %</td></tr><tr class="even"><td>Spain</td><td>20,286</td><td>0.88 %</td><td>28.87 %</td><td>6.66 %</td><td>21.78 %</td><td>7.54 %</td><td>22.31 %</td></tr><tr class="odd"><td>Mexico</td><td>45,615</td><td>0.67 %</td><td>40.18 %</td><td>6.42 %</td><td>83.71 %</td><td>7.10 %</td><td>77.01 %</td></tr><tr class="even"><td>Netherlands</td><td>14,347</td><td>1.98 %</td><td>-3.21 %</td><td>5.04 %</td><td>20.18 %</td><td>7.02 %</td><td>13.30 %</td></tr><tr class="odd"><td>United States (Illinois)</td><td>9,969</td><td>2.73 %</td><td>56.39 %</td><td>4.21 %</td><td>129.07 %</td><td>6.94 %</td><td>105.29 %</td></tr><tr class="even"><td>United States (Michigan)</td><td>6,040</td><td>3.56 %</td><td>41.94 %</td><td>3.33 %</td><td>104.84 %</td><td>6.89 %</td><td>80.65 %</td></tr><tr class="odd"><td>Canada (Quebec)</td><td>11,585</td><td>3.79 %</td><td>90.91 %</td><td>3.05 %</td><td>42.15 %</td><td>6.84 %</td><td>75.62 %</td></tr><tr class="even"><td>Argentina</td><td>11,708</td><td>1.39 %</td><td>56.50 %</td><td>5.36 %</td><td>48.23 %</td><td>6.75 %</td><td>49.41 %</td></tr><tr class="odd"><td>Canada (Ontario)</td><td>29,566</td><td>3.73 %</td><td>114.95 %</td><td>2.95 %</td><td>97.20 %</td><td>6.68 %</td><td>107.48 %</td></tr><tr class="even"><td>Morocco</td><td>8,048</td><td>0.48 %</td><td>-1.78 %</td><td>6.10 %</td><td>46.96 %</td><td>6.59 %</td><td>44.44 %</td></tr></tbody></table></div>
</div>
<h2>In summary</h2>
<p>
Many people use the Google DNS or OpenDNS services. From our data the combined 'market share' is ~8% globally, and in some (big) countries <a href="https://www.cdnplanet.com/blog/google-dns-opendns-and-cdn-performance/#ms">much higher</a>. Both public DNS services allow CDNs to have the edns-client-subnet be passed on to them, so they can determine with more accuracy what the geolocation of the end-user is. In consequence, the CDNs can provide a better user experience by serving content from a POP closeby over a low-latency connection. We listed <a href="https://www.cdnplanet.com/blog/which-cdns-support-edns-client-subnet/#cdns">9 CDNs that would benefit from supporting/using edns-client-subnet</a>, and only two actually support edns-client-subnet: CDN77 and ChinaCache. Others, including Akamai, Internap and CDNetworks, do not currently. This really is too bad, because from the performance data we collected, it is clear these CDNs deliver (much) worse performance currently in many countries to Google DNS and OpenDNS users. We believe performance can greatly improve if they would support edns-client-subnet. But edns-client-subnet is not a magic bullet. The performance data clearly shows that some non-anycast CDNs (ChinaCache) need to do more and in some countries the anycast CDNs are not servicing the public DNS users as well as they should (Edgecast).
Our final message is to Google DNS and OpenDNS: please make an effort to publish an uptodate map/list of the resolver locations, so CDNs can use that to keep their systems uptodate. Even better would be to proactively inform them of location changes. Maybe you do this already, but ... please crank it up a notch. Txs.
</p>
<script type="text/javascript">
(function() {
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
ga.src = '/scripts/compagg.js';
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
})();
</script>Which CDNs support edns-client-subnet?2012-10-17T02:00:00+02:002021-08-23T02:00:00+02:00https://www.cdnplanet.com/blog/which-cdns-support-edns-client-subnet/Number 2 in a series of 3 blog posts: what is edns-client-subnet, how is it relevant to CDN performance and which CDNs currently support it?
<div class="notice-msg info">
This article is 9 years old and needs an update. The explanation of the problem and possible solutions are still valid today and the table <a href="#cdns">Which CDNs support edns-client-subnet</a> is uptodate, but the section "CDNs that need to take action" is outdated.
</div>
<p><strong>This is article two in a series of three blog posts.</strong></p>
<p>
Yesterday we published <a href="https://www.cdnplanet.com/blog/google-dns-opendns-and-cdn-performance/">Google DNS and OpenDNS usage stats</a> that we generated from the data of millions of end-user tests. Interesting as this is, the real goals of those tests was to a) find out which CDNs support edns-client-subnet and b) gain insight in the impact of edns-client-subnet on CDN performance. In this article, we explain what edns-client-subnet is, its relevance to CDN performance and show which CDNs currently support it. In article 3, to be published tomorrow, we will show real-world performance data and make clear how edns-client-subnet (significantly) impacts CDN performance.
</p>
<ul>
<li><a href="#p">The problem</a>
</li><li><a href="#s">Solutions</a>
</li><li><a href="#cdns">Which CDNs support edns-client-subnet</a>
</li><li><a href="#testing">Test methodology</a>
</li></ul>
<h2 id="p">The problem</h2>
<p>
Some CDNs use DNS to determine the geographical location of the user. They can't use the IP address of the client for this, because it is masked by the DNS resolver, and so the CDNs use the IP address of the DNS resolver instead. In case of the Google DNS or OpenDNS servers, for many end users those servers are not close to them, simply because these providers don't have servers in every country and every ISP's network. For example, OpenDNS does not have DNS servers in South-America (<a href="https://www.opendns.com/data-center-locations/" class="external-link">network map</a>). Someone in Brazil using OpenDNS will likely hit their resolver in Florida. The CDN will then think the user is in Florida and as a result it will serve content to the user from a server far away (Florida, not Brazil) resulting in a slow experience.
</p>
<p>
The two diagrams below illustrate how things work for a user in Thailand who wants to connect to a hostname on Akamai, using either the DNS resolver of his ISP or the resolver of OpenDNS.
</p>
<p>
<img class="img-no-max-width" src="https://www.cdnplanet.com/static/img/cdn-without-edns-isp-resolver.png" width="577" height="309" alt="CDN without edns-client-subnet support - normal ISP resolver"><br><br>
The user ends up hitting a low-latency Akamai edge server in Thailand because Akamai's DNS server can accurately detect the user's geolocation. This is good.
</p>
<p>
<img loading="lazy" class="img-no-max-width" src="https://www.cdnplanet.com/static/img/cdn-without-edns.png" width="601" height="355" alt="CDN without edns-client-subnet support"><br><br>
Now Akamai cannot accurately detect the user's geolocation and believes the user is in Singapore. The user will connect to a high-latency Akamai edge server in Singapore. This is bad.
</p>
<h2 id="s">Solutions</h2>
<p>
There are two solutions a CDN can implement to counter this problem. They can either use anycast for HTTP or support edns-client-subnet. We'll take a quick look at anycast for HTTP but focus on edns-client-subnet.
</p>
<h3>Anycast for HTTP</h3>
<p>
CDNs that use anycast for HTTP are not affected by the problems with geo targetting using DNS since they resolve to the same IP for all users (or all users in one region; Edgecast). They rely on BGP to direct users to the closest server based on preferred path. Using anycast for HTTP may come with its own set of problems, but that is beyond the scope of this post.
</p>
<p>
<img loading="lazy" class="img-no-max-width" src="https://www.cdnplanet.com/static/img/cdn-with-anycast.png" width="598" height="339" alt="CDN with Anycast for HTTP"><br><br>
The CDN's authoritative name server (ADNS) will respond with an IP address regardless of the user's location and that will lead the user to a nearby edge server wherever the user is.
</p>
<h3>Support edns-client-subnet</h3>
<p>
To mitigate the problem of DNS based geo-targetting, Google proposed a technical solution to the issue in an IETF draft <a href="https://tools.ietf.org/html/draft-vandergaast-edns-client-subnet-01" class="external-link">Client subnet information in DNS requests</a>. This is an experimental DNS extension that allows DNS resolvers to pass the client's IP address (or part of) to compatible authoritative DNS servers. The CDN's DNS server can then use this information to better determine where the end user is. Google DNS and OpenDNS implemented this solution as part of the <a href="https://afasterinternet.com/" class="external-link">Global Internet Speedup</a> initiative in August 2011.
</p>
<p>
The drawback is the experimental nature of the spec and limited support in existing DNS server software. Only OpenDNS and Google Public DNS seems to support it on the resolver side. With both these providers, one must apply to be "whitelisted" in order to receive client's subnet with the query. The whitelisting procedure is <a href="https://tools.ietf.org/html/draft-vandergaast-edns-client-subnet-01#page-22" class="external-link">pretty straightforward</a>. Contact OpenDNS or Google, tell them your hostnames, nameservers IPs and they will probably whitelist you within a couple of days without any fuss.
</p>
<p>
Our test DNS server supported edns-client-subnet since the start of this month, and no resolver apart from Google and OpenDNS sent us queries with client subnet information.
</p>
<p>
The following illustration shows how supporting edns-client-subnet should result in getting a low-latency connection to the CDN.
</p>
<p>
<img loading="lazy" class="img-no-max-width" src="https://www.cdnplanet.com/static/img/cdn-with-edns.png" width="590" height="359" alt="CDN with edns-client-subnet support">
</p>
<h2 id="cdns">Which CDNs support edns-client-subnet</h2>
<p>Last updated: August 23, 2021</p>
<p>Does <i>your</i> CDN support EDNS Client Subnet? Find out with a quick <a href="https://www.cdnplanet.com/tools/edns-client-subnet-checker/">EDNS Client Subnet test</a>.
<table class="table-striped table-two-col-5050 valign-text-top">
<thead>
<tr>
<th>CDN</th>
<th>Supports edns-client-subnet?</th>
</tr>
</thead>
<tbody>
<tr>
<td data-hostname="www.akamai.com">Akamai</td>
<td><img loading="lazy" src="https://www.cdnplanet.com/static/img/check-circle.svg" width="24" height="24" class="icon-in-table-cell" alt="Only for Google and OpenDNS"> Only for Google and OpenDNS</td>
</tr>
<tr>
<td data-hostname="">ArvanCloud</td>
<td><img loading="lazy" src="https://www.cdnplanet.com/static/img/check-circle.svg" width="24" height="24" class="icon-in-table-cell" alt="Anycast"> Anycast</td>
</tr>
<tr>
<td data-hostname="azure-perfops.azureedge.net">Azure CDN (Microsoft)</td>
<td><img loading="lazy" src="https://www.cdnplanet.com/static/img/check-circle.svg" width="24" height="24" class="icon-in-table-cell" alt="Yes"> Yes</td>
</tr>
<tr>
<td data-hostname="zh.baishancloud.com">BaishanCloud</td>
<td><img loading="lazy" src="https://www.cdnplanet.com/static/img/check-circle.svg" width="24" height="24" class="icon-in-table-cell" alt="Yes"> Yes</td>
</tr>
<tr>
<td data-hostname="bunny.net">Bunny CDN</td>
<td><img loading="lazy" src="https://www.cdnplanet.com/static/img/check-circle.svg" width="24" height="24" class="icon-in-table-cell" alt="Yes"> Yes</td>
</tr>
<tr>
<td data-hostname="www.cachefly.com">CacheFly</td>
<td><img loading="lazy" src="https://www.cdnplanet.com/static/img/check-circle.svg" width="24" height="24" class="icon-in-table-cell" alt="Anycast"> Anycast</td>
</tr>
<tr>
<td data-hostname="www.cdn77.com">CDN77</td>
<td><img loading="lazy" src="https://www.cdnplanet.com/static/img/check-circle.svg" width="24" height="24" class="icon-in-table-cell" alt="Yes"> Yes</td>
</tr>
<tr>
<td data-hostname="www.cdnetworks.com">CDNetworks</td>
<td><img loading="lazy" src="https://www.cdnplanet.com/static/img/check-circle.svg" width="24" height="24" class="icon-in-table-cell" alt="Yes"> Yes</td>
</tr>
<tr>
<td data-hostname="www.cloudflare.com">Cloudflare</td>
<td><img loading="lazy" src="https://www.cdnplanet.com/static/img/check-circle.svg" width="24" height="24" class="icon-in-table-cell" alt="Anycast"> Anycast</td>
</tr>
<tr>
<td data-hostname="a0.awsstatic.com">CloudFront</td>
<td><img loading="lazy" src="https://www.cdnplanet.com/static/img/check-circle.svg" width="24" height="24" class="icon-in-table-cell" alt="Yes"> Yes</td>
</tr>
<tr>
<td data-hostname="www.fastly.com">Fastly</td>
<td><img loading="lazy" src="https://www.cdnplanet.com/static/img/check-circle.svg" width="24" height="24" class="icon-in-table-cell" alt="Anycast"> Anycast for public resolvers, only for Google and OpenDNS</td>
</tr>
<tr>
<td data-hostname="gcore.com">Gcore</td>
<td><img loading="lazy" src="https://www.cdnplanet.com/static/img/check-circle.svg" width="24" height="24" class="icon-in-table-cell" alt="Yes"> Yes</td>
</tr>
<tr>
<td data-hostname="www.imperva.com">Imperva</td>
<td><img loading="lazy" src="https://www.cdnplanet.com/static/img/check-circle.svg" width="24" height="24" class="icon-in-table-cell" alt="Anycast"> Regional Anycast with EDNS support</td>
</tr>
<tr>
<td data-hostname="">Lumen</td>
<td><img loading="lazy" src="https://www.cdnplanet.com/static/img/help-circle.svg" width="24" height="24" class="icon-in-table-cell" alt="Unknown"> Unknown</td>
</tr>
<tr>
<td data-hostname="media.limelight.com">Limelight</td>
<td><img loading="lazy" src="https://www.cdnplanet.com/static/img/check-circle-partially.svg" width="24" height="24" class="icon-in-table-cell" alt="Anycast for public resolvers, but not all POPs included"> Anycast for public resolvers, but not all POPs included</td>
</tr>
<tr>
<td data-hostname="www.medianova.com">Medianova</td>
<td><img loading="lazy" src="https://www.cdnplanet.com/static/img/check-circle.svg" width="24" height="24" class="icon-in-table-cell" alt="Yes"> Yes</td>
</tr>
<tr>
<td data-hostname="www.stackpath.com">StackPath</td>
<td><img loading="lazy" src="https://www.cdnplanet.com/static/img/check-circle.svg" width="24" height="24" class="icon-in-table-cell" alt="Anycast"> Anycast</td>
</tr>
<tr>
<td data-hostname="www.tatacommunications.com">TATA</td>
<td><img loading="lazy" src="https://www.cdnplanet.com/static/img/check-circle.svg" width="24" height="24" class="icon-in-table-cell" alt="Anycast"> Anycast</td>
</tr>
<tr>
<td data-hostname="cloudcache.tencent-cloud.com">Tencent Cloud</td>
<td><img loading="lazy" src="https://www.cdnplanet.com/static/img/check-circle.svg" width="24" height="24" class="icon-in-table-cell" alt="Yes"> Yes</td>
</tr>
<tr>
<td data-hostname="edgecast.production.backyard.verizonmedia.com">Verizon Media</td>
<td><img loading="lazy" src="https://www.cdnplanet.com/static/img/check-circle.svg" width="24" height="24" class="icon-in-table-cell" alt="Anycast"> Regional Anycast with EDNS support</td>
</tr>
</tbody>
</table>
</p><h3>CDNs that need to take action</h3>
<p>
Quite a few CDN providers don't do anycast for HTTP and don't support edns-client-subnet: Akamai, CDNetworks, CloudFront, Fastly, Internap, Level3 and Limelight.
These are the CDNs that provide worse performance to some Google DNS and OpenDNS users than possible. But the size of the problem is not equal for all these CDNs.
Fastly is the newcomer in the CDN market and currently has a limited number of POPs (4 in the US and 3 in Europe). Supporting edns-client-subnet will not improve performance all that much for them.
Performance of the bigger CDNs however, like Akamai and Limelight, could improve significantly for many end-users if they would support edns-client-subnet. They have thousands of servers in many, many locations and quite a few Google DNS and OpenDNS users currently don't connect to an edge server inside the network of their ISP, but rather to a server in some other country or even continent.
</p>
<h3>A few more things to say</h3>
<p>
CDNetworks is listed as a <a href="https://www.afasterinternet.com/participants.htm" class="external-link">participant</a> in the Global Internet Speedup initiative, but they actually don't support edns-client-subnet. That is odd. The same goes for Bitgravity and CloudFlare, but since these CDNs do anycast, it is a none-issue.<br>
Edgecast is the only CDN with anycast for HTTP that supports edns-client-subnet, but it shouldn't really matter much because they do anycast (per region actually: North-America, Europe, APAC, ...). Perhaps Edgecast can improve the effectiveness of their regional anycast architecture just a little bit with edns-client-subnet.
</p>
<h2 id="testing">Test methodology</h2>
<p>This is what I did to test support for edns-client-subnet by CDNs:
</p>
<p>
I downloaded the patch to dig from <a href="https://wilmer.gaa.st/edns-client-subnet/" class="external-link">https://wilmer.gaa.st/edns-client-subnet/</a> and installed it. Actually, I copied the patched dig as dig-client into my path for easy access.
</p>
The new version of Dig is version 9.10 and it includes an option called the “edns-client-subnet” which allows us to insert the client subnet in the query.
<pre>
sajal@sajal-laptop:~$ dig-client gp1.wac.v2cdn.net @ns1.edgecastcdn.net +client=58.8.96.26
; <<>> DiG 9.9.1-P3 <<>> gp1.wac.v2cdn.net @ns1.edgecastcdn.net +client=58.8.96.26
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 8715
;; flags: qr aa rd; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
;; WARNING: recursion requested but not available
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
; CLIENT-SUBNET: 58.8.96.26/32/14
;; QUESTION SECTION:
;gp1.wac.v2cdn.net. IN A
;; ANSWER SECTION:
gp1.wac.v2cdn.net. 3600 IN A 117.18.237.1
;; Query time: 56 msec
;; SERVER: 72.21.80.5#53(72.21.80.5)
;; WHEN: Tue Oct 16 17:08:39 2012
;; MSG SIZE rcvd: 91
sajal@sajal-laptop:~$
</pre>
<p>
The response from Edgecast shown above means that Edgecast is saying that the response is only valid for 58.8.96.26/14 (i.e. 58.8.0.1 - 58.11.255.254). It may be the case that the CDN has only whitelisted Google/OpenDNS to receive client-subnet. In that case we can query Google. Google passes along subnet information to everyone.
</p>
<pre>
sajal@sajal-laptop:~$ dig-client gp1.wac.v2cdn.net +client=58.8.96.26 @8.8.8.8
; <<>> DiG 9.9.1-P3 <<>> gp1.wac.v2cdn.net +client=58.8.96.26 @8.8.8.8
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 5615
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 512
; CLIENT-SUBNET: 58.8.96.26/32/14
;; QUESTION SECTION:
;gp1.wac.v2cdn.net. IN A
;; ANSWER SECTION:
gp1.wac.v2cdn.net. 2192 IN A 117.18.237.1
;; Query time: 60 msec
;; SERVER: 8.8.8.8#53(8.8.8.8)
;; WHEN: Tue Oct 16 17:13:56 2012
;; MSG SIZE rcvd: 74
sajal@sajal-laptop:~$
</pre>
<!-- <p>
My bash script is <a href="https://pastie.org/private/voudrszepsldypeepbilta" class="external-link">here</a>
</p> -->A better CDN Finder2012-05-29T02:00:00+02:00https://www.cdnplanet.com/blog/better-cdn-finder/Our easy to use and popular CDN Finder tool has been rebuilt from the ground up, resulting in much better results. Read about the changes we made, how it is built and how the user benefits.
<div class="notice-msg info">
This article is outdated. Read about the <a href="https://www.cdnplanet.com/blog/all-new-cdn-finder/">all new CDN Finder<a>!
</a></a></div>
<p>
The <a href="https://www.cdnplanet.com/tools/cdnfinder/">CDN Finder tool</a> has been very popular since the day we launched it 6 months ago. It enables you to easily find out what CDN(s) a site is using or what CDN is behind a hostname. CDN Finder is easy to use, pretty fast and often gives correct results, but definitely not always. We have always been aware of the tool's imperfections and Sajal recently decided to spend a Saturday on building a new, better CDN Finder from the ground up, with the primary goal to return better results (less false negatives: our tool says 'no CDN' while the hostname <i>does</i> point to a CDN).
</p>
<p>
Mission accomplished: the <a href="https://www.cdnplanet.com/tools/cdnfinder/">new CDN Finder</a> is done, deployed and really is a lot better.
</p>
<h2>What we did to improve CDN Finder</h2>
<p>
In the first version of the tool, CDN Finder fetched the HTML and parsed it with very simple logic to get to a list of hostnames. For each found hostname CDN Finder would then do a DNS lookup, look at the last CNAME only and regex match that hostname to our list of CDN hostnames.
</p>
<p>There are three problems with this method:
<ol>
<li>Hostnames serving content on the page linked to from within CSS or injected via JavaScript are missed
</li><li>Using just the last CNAME in the chain is error prone as some CDNs have many different hostnames
</li><li>Our list of CDN hostnames was not complete
</li></ol>
</p><p>We tackled all three problems and the results are now much better.
</p>
<h3>1. Full page rendering</h3>
<p>
CDN Finder (full site lookup) now loads and renders the full page in Webkit using <a href="https://phantomjs.org/" class="external-link">PhantomJS</a>.
The 'browser' will parse the HTML, CSS and JavaScript, execute the JavaScript, etc.
As a result, CDN Finder has much better list of hostnames that served at least one object on the page.
And there is another reason to render the page with PhantomJS: we get the response headers for each resource.
This allows catching CDNs even if no CNAMEs are given. For example, if the <code>Server</code> response header contains <code>cloudflare-nginx</code>, the resource is served by CloudFlare. We use this only as a fallback, if the CNAME chain processing has 'no CDN' as a result.
</p>
<h3>2. Full CNAME chain processing</h3>
<p>
Taking that better list of hostnames as input, for each hostname the CDN Finder analyzes the full CNAME chain and by that can better decide which CDN is behind it.
Example:
<pre>
cdnfinder@cdnplanet:~$ host ec.cdnplanet.com
ec.cdnplanet.com is an alias for wac.6e8d.edgecastcdn.net.
wac.6e8d.edgecastcdn.net is an alias for gp1.wac.edgecastcdn.net.
gp1.wac.edgecastcdn.net has address 117.18.237.250
</pre>
</p><p>Previously the CDN Finder would only use <code>gp1.wac.edgecastcdn.net</code> to detect the CDN. Now it uses <code>ec.cdnplanet.com</code>, <code>wac.6e8d.edgecastcdn.net</code> and <code>gp1.wac.edgecastcdn.net</code>. In this example, the <code>wac.6e8d.edgecastcdn.net</code> part will be stable, as that is what the customer CNAMEs to. <code>gp1.wac.edgecastcdn.net</code> can be changed at any time by the CDN, into for example <code>gp1.wac.eccdn.net</code> and that would then fail as <code>eccdn.net</code> is not in our whitelist.
</p>
<h3>3. More CDNs on our whitelist</h3>
<p>
CDN Finder now also catches the following CDNs: Bitgravity, ChinaCache, CDN77, OnApp and Turbobytes.
</p>
<h2>Benefits to the user</h2>
<p>Simply put: the results are more correct/reliable. If the result for a hostname is 'no CDN', then this really should be the case. We feel confident about the methodology and the tool runs smoothly. We hope you enjoy using the <a href="https://www.cdnplanet.com/tools/cdnfinder/">CDN Finder</a> and please share your thoughts in the comments!
</p>
<h2>Open-source</h2>
<p>
CDN Finder is open-source. You can find it on Github <a href="https://github.com/turbobytes/cdnfinder" class="external-link">here</a>.
</p>How to test your CDN before go live2012-05-01T02:00:00+02:00https://www.cdnplanet.com/blog/how-test-your-cdn-before-go-live/When turning on a CDN for your site, make sure to test your config/setup actually works. If you don't, you can be in big trouble. We show you 4 ways to do this testing and number 1 is clearly not our favourite...
<p>
In this article I show a few ways to test CDN setups, changes in hosting, etc before sending real users to the new setup.
</p>
<p>
Example scenario: example.com does not currently use a CDN. They serve their CSS file using the link https://static.example.com/style.css . Now they go to a CDN provider, and setup such that <code>static.example.com</code> is served by the CDN.
</p>
<p>
To allow the CDN to serve the files, the site owner must delete the existing DNS record for <code>static.example.com</code> and make a CNAME to the one given by the CDN provider, say <code>example.com.clients.somecdnprovider.net</code>
</p>
<p>
There are few ways to proceed from here.
</p>
<h2>Method 1: The dumb way</h2>
<p>Make <code>static.example.com</code> CNAME to <code>example.com.clients.somecdnprovider.net</code>. Flush the browser cache. Make sure DNS changes have propagated to your ISP/computer (use ping to check the IP address). Refresh a few times. See everything is in order... and then pray that the site is not broken for your customers. Monitor your mailbox and Twitter for complaints. Nobody saying anything bad? Then you are probably fine. Congratulations!</p>
<h2>Method 2: Adjust hosts file</h2>
<p>Rather than point your live traffic to something untested, its nice to run through some "what if" scenarios.</p>
<ol>
<li>Find the IP of <code>example.com.clients.somecdnprovider.net</code></li>
<li>Map that IP to <code>static.example.com</code> in your <a href="https://en.wikipedia.org/wiki/Hosts_(file)" class="external-link">hosts file</a></li>
<li>Clear browser cache and OS DNS cache</li>
<li>Browse around your site keeping your browser's Dev Tools open to see if anything is broken</li>
</ol>
<h3>Method 3: The 1337 way!</h3>
<p>This is the method I use very often.</p>
<p>First some primer on how HTTP clients work.</p>
<p>When you issue a GET request for https://static.example.com/style.css, many things happen behind the scenes.</p>
<ol>
<li>The client gets the IP for <code>static.example.com</code> from the OS</li>
<li>The client establishes a HTTP Connection with the returned IP</li>
<li>The client asks the server for /style.css , and amongst the other request headers, it includes "Host: static.example.com"</li>
<li>The server uses the information given by the Host header to know which virtual zone it may be mapped to. This is the most important request header.</li>
</ol>
<p>So, the server the client connects to, and the value of the Host header, is completely under the clients control. We will manipulate this information to our advantage to test the new setup.</p>
<p>My favourite user agent for ad hoc testing is the GET and HEAD commands provided by <a href="https://linux.die.net/man/1/lwp-request" class="external-link">lwp-request</a>. I also use <a href="https://httpd.apache.org/docs/2.0/programs/ab.html" class="external-link">ApacheBench</a> and curl.</p>
<p>To fetch the example file from the CDN we would issue the following command.</p>
<pre>GET -H 'Host: static.example.com' https://example.com.clients.somecdnprovider.net/style.css</pre>
<p>Or if you like curl:</p>
<pre>curl -H 'Host: static.example.com' https://example.com.clients.somecdnprovider.net/style.css</pre>
<p>ApacheBench:</p>
<pre>ab -H 'Host: static.example.com' https://example.com.clients.somecdnprovider.net/style.css</pre>
<p>What these commands do is a little different from before:</p>
<ol>
<li>The client gets the IP for <code>example.com.clients.somecdnprovider.net</code> from the OS</li>
<li>The client establishes a HTTP Connection with the returned IP</li>
<li>The client asks the server for /style.css , and amongst the other request headers, it includes <code>Host: static.example.com</code></li>
<li>The server uses the information given by the Host header to know which virtual zone it may be mapped to. This is the most important request header.</li>
</ol>
<p>So, now you are connecting to <code>example.com.clients.somecdnprovider.net</code> but overriding the Host header to <code>static.example.com</code>. This is exactly the same behaviour if you had made the DNS change and a real user was requesting the file.</p>
<p>Some other checks I do before going live:</p>
<h3>Cacheability</h3>
<p>Some CDNs include the cache information in HTTP response headers. For them examine the headers:</p>
<pre>HEAD -H 'Host: static.example.com' https://example.com.clients.somecdnprovider.net/style.css</pre>
<p>Run this command many times to prime the cache. On cache HITs, CDNs usually include the following headers:</p>
<p>CloudFront:</p>
<pre>X-Cache: Hit from cloudfront</pre>
<p>NetDNA/MaxCDN:</p>
<pre>X-Cache: HIT</pre>
<p>Internap - Any one of them needs to have a HIT</p>
<pre>X-Cache: HIT from cdce-sin002-002.sin002.internap.com</pre>
<p>Level3 - Does not indicate; try making requests and monitoring your access log</p>
<p>BitGravity - Does not indicate; try making requests and monitoring your access log</p>
<p>Fastly</p>
<pre>X-Cache: HIT</pre>
<p>Test this over and over. Test for query string behaviour. If you set the CDN to ignore query strings, it should be mostly HITs, even if you try random query strings. On the other hand, if you've set the CDN to treat query strings as unique, each time you try a different query string, it should be a MISS.</p><p>
</p><h3>Compression</h3>
<p>Test compression by manipulating the Accept-Encoding header. Look for Content-Length and Content-Encoding response headers. Make sure you are testing a compressable object like CSS/JS/HTML:</p>
<pre>HEAD -H 'Host: static.example.com' -H 'Accept-Encoding: gzip' https://example.com.clients.somecdnprovider.net/style.css</pre>
and
<pre>HEAD -H 'Host: static.example.com' https://example.com.clients.somecdnprovider.net/style.css</pre>
<p>Should provide different results for encoding and length.</p>
<h3>Bonus tip</h3>
<p>Test your CDN from all over the world.</p>
<p>Head on over to <a href="https://whatsmydns.me/" class="external-link">https://whatsmydns.me/</a>, enter <code>example.com.clients.somecdnprovider.net</code> make a note of all the IPs. For each IP, do the following:</p>
<pre>GET -H 'Host: static.example.com' https://the-returned-ip/style.css</pre>
<p>You can run the all the above tests on all the IPs returned for a more thorough testing.</p>
<p>Note: This does not work for Anycast based CDNs like StackPath or Cloudflare.</p>
<p>Only when you are completely confident make the real switch in your DNS.</p>
<p>As far as I remember, this method was first introduced to me by <a href="https://www.kirit.com/" class="external-link">Kirit Sælensminde</a> years ago for a different purpose.</p>
<p>Note: If you are behind <a href="https://www.sajalkayan.com/4-reasons-why-i-love-my-isp.html" class="external-link">an ISP that runs a transparent proxy</a>, methods #2 and #3 are not accurate unless you know how to circumvent the proxy.</p>
<h2>Method 4: webpagetest.org FTW</h2>
<p>While the command line methods are very powerful, and my method of choice for these tests, it may be a little complex for some people unfamiliar with the command line. <a href="https://www.webpagetest.org/" class="external-link">Webpagetest.org</a> to the rescue!</p>
<p>By using the <a href="https://sites.google.com/a/webpagetest.org/docs/using-webpagetest/scripting#TOC-setDNSName" class="external-link">setDNSName</a> command, you can fool the test agent/browser into thinking that the CNAME switch has already been done. Based on the above scenarios, the script needed would be:</p>
<pre>
setDnsName static.example.com example.com.clients.somecdnprovider.net
navigate https://example.com/
</pre>
<p>Make sure to run multiple tests from each location, and check caching and compression headers.</p>Turbobytes: multi-CDN made easy2012-04-25T02:00:00+02:00https://www.cdnplanet.com/blog/turbobytes-multi-cdn-made-easy/Turbobytes is a new and innovative multi-provider CDN platform that load balances amongst CDNs based on performance, enabling publishers, webshops and others to get optimal performance worldwide. Great pricing, a friendly Cancel-Anytime policy and no need for your own contracts with several CDNs.
<div class="notice-msg info">
TurboBytes is out of business since June 2020
</div>
<p>
After months of hard work, we are proud to announce Turbobytes, the new and <strong>easy way of using multiple Content Delivery Networks</strong> for static object delivery. Turbobytes offers a multi-provider CDN platform that load balances amongst providers based on performance. The platform monitors Content Delivery Network performance from an end-user point of view (2 lines of non-blocking JavaScript code on webpages), analyzes the data in real-time and makes intelligent decisions on whether or not to switch to another, better performing CDN, in some part of the world. The actual switching is done by making changes to DNS records and we can do this on a regional or country level, and even at the state level in the US
</p>
<p>We target medium-sized businesses (publishers, webshops, analytics/widget providers and ad networks) that need to have their static content delivered in different parts of the world at high speed.</p>
<h2>Benefits of using Turbobytes</h2>
<ul>
<li>Faster content delivery
</li><li>Higher reliability: no more damage to your business because your CDN or your own servers are down or slow
</li><li>Great pricing
</li><li>Easy come, easy go: free trial, low monthly fees (starts at $0), no commitment for bandwidth and a Cancel-Anytime policy
</li><li>Better insight: unique, personalized CDN data, presented simple and easily accessible (desktop, tablet, phone)
</li><li>Excellent service
</li></ul>
<p>We work with Level3, Edgecast, Internap and NetDNA and expect to add several CDNs in the next months. Our partner for DNS is Dyn.</p>
<p>What makes the Turbobytes service unique is that it's <strong>easy</strong> (one contract, performance optimization is automated) to use multiple CDN providers in order to get optimal performance.
</p>
<h2>Is multi-CDN really of added value?</h2>
<p>
Below you see two charts, showing CDN performance in US and Australia (total time, incl DNS lookup, to load a 16 KB file). As you see, CDN performance varies and the US is much different from Australia.
<img class="img-no-max-width" src="https://www.cdnplanet.com/static/img/cdn-performance-april2012-us-median.png" width="593" height="305" alt="US April CDN Performance">
<img loading="lazy" class="img-no-max-width" src="https://www.cdnplanet.com/static/img/cdn-performance-april2012-au-median.png" width="606" height="303" alt="AU April CDN Performance">
<br><small>The number of measurements per CDN per country per day is 25000+</small>
</p>
Google DNS, OpenDNS and CDN performance2012-02-16T01:00:00+01:00https://www.cdnplanet.com/blog/google-dns-opendns-and-cdn-performance/Find out how popular the Google Public DNS and OpenDNS services are in countries across the globe based on millions of real-user tests.
<p><strong>This is the first in a series of three blog posts.</strong></p>
<p>
<a href="https://developers.google.com/speed/public-dns/" class="external-link">Google Public DNS</a> and <a href="https://www.opendns.com/" class="external-link">OpenDNS</a> are free DNS resolvers that anybody can use on their computer to resolve a hostname (www.example.com) to an IP address. Google and OpenDNS promise faster and safer DNS lookups and each serves billions of DNS queries every month.
</p>
<p>
It has been <a href="https://apcmag.com/why-using-google-dns-opendns-is-a-bad-idea.htm" class="external-link">blogged</a> <a href="https://www.sajalkayan.com/in-a-cdnd-world-opendns-is-the-enemy.html" class="external-link">about</a> <a href="https://00f.net/2012/02/22/akamai-vs-public-dns-servers/" class="external-link">before</a> that using these DNS resolvers can cause (big) problems when requesting content from CDNs, because some CDNs detect the user's location wrong and serve content from far-away servers.
</p>
<p>
We decided to investigate if this problem still exists, and if so how big the problem is. We will answer the following 3 questions:
<ol>
<li><a href="#ms">How many people use Google DNS and OpenDNS?</a>
</li><li><a href="https://www.cdnplanet.com/blog/which-cdns-support-edns-client-subnet/">Which CDNs support edns client subnet?</a>
</li><li><a href="https://www.cdnplanet.com/blog/real-world-cdn-performance-googledns-opendns-users/">Is real-world CDN performance different for Google DNS and OpenDNS users?</a>
</li></ol>
</p><p>We answer the first question in this article and the others in separate blog posts, which we hope to publish tomorrow and the day after tomorrow. Please be patient. The good stuff on CDN performance is coming ...
</p>
<h2 id="ms">How many people use Google DNS and OpenDNS?</h2>
<div class="notice-msg info">
Update (Oct 17, 2012): we computed all the numbers again, after discovering we did not mark AS30607 as OpenDNS. We apologize for this mistake.
</div>
<p>
At Turbobytes, we continuously monitor the performance of CDNs to fuel our multi-CDN platform. We built a custom RUM solution for this and many websites have added our JavaScript snippet to their pages. When thinking about testing usage of Google DNS and OpenDNS and the impact on CDN performance, Sajal came up with a way to leverage our RUM user base, with the big benefit of getting a lot of measurements. Let's look at the test results first and then you can read about our <a href="#tm">test methodology</a>.
</p>
<h3>Results</h3>
<p>
Between Oct 3 and Oct 15 2012 we ran over 3.5 million tests from around the world.<br>
The key findings are:
<ul>
<li>8.04% of users worldwide use either Google DNS or OpenDNS
</li><li>Google DNS has 2.5 times more users than OpenDNS
</li><li>Popularity of these DNS services varies greatly by country
</li><li>Google DNS is very popular in Vietnam, Indonesia, Turkey and Egypt
</li></ul>
</p><p>The chart below shows the 'market share' of the two DNS services in 10 large countries. China is not shown because we had a relatively small number of measurements from China.
</p>
<p>
<img src="https://www.cdnplanet.com/static/img/google-dns-opendns-market-share-october-2012-fs8.png" width="575" height="378" alt="Google DNS and OpenDNS market share Oct 2012">
</p>
<p>
Just by looking at these 10 countries you can see the big differences. Canada is the only country where OpenDNS is used by more people than Google DNS. United Kingdom and Australia are pretty similar, usage of bot public DNS services is relatively low in France, but high in Russia and Brazil. In India and especially Indonesia, both DNS services are used by a lot of people. View the table below to gain insight in Google Public DNS and OpenDNS usage/popularity for the 80 countries and states (US/CA) that we received most test results from.
</p>
<div data-ng-controller="BlogPostCtrl">
<div data-angulartable="" data-ng-model="tbldata" class="datatable"><table style="border:solid;"><thead><tr><th colspan="1">Country/state</th><th colspan="1">Count</th><th colspan="2">Using OpenDNS</th><th colspan="2">Using Google DNS</th><th colspan="2">Using OpenDNS or Google</th></tr><tr><th colspan="1"></th><th colspan="1"></th><th colspan="1">Count</th><th colspan="1">Pct</th><th colspan="1">Count</th><th colspan="1">Pct</th><th colspan="1">Count</th><th colspan="1">Pct</th></tr></thead><tbody><tr class="odd"><td>All data</td><td>3,541,208</td><td>81,292</td><td>2.30%</td><td>203,319</td><td>5.74%</td><td>284,611</td><td>8.04%</td></tr><tr class="even"><td>United States</td><td>920,307</td><td>24,145</td><td>2.62%</td><td>30,593</td><td>3.32%</td><td>54,738</td><td>5.95%</td></tr><tr class="odd"><td>United States (California)</td><td>262,210</td><td>5,688</td><td>2.17%</td><td>9,527</td><td>3.63%</td><td>15,215</td><td>5.80%</td></tr><tr class="even"><td>Canada</td><td>190,940</td><td>7,944</td><td>4.16%</td><td>5,675</td><td>2.97%</td><td>13,619</td><td>7.13%</td></tr><tr class="odd"><td>France</td><td>154,761</td><td>1,161</td><td>0.75%</td><td>3,139</td><td>2.03%</td><td>4,300</td><td>2.78%</td></tr><tr class="even"><td>United Kingdom</td><td>147,626</td><td>3,110</td><td>2.11%</td><td>5,789</td><td>3.92%</td><td>8,899</td><td>6.03%</td></tr><tr class="odd"><td>Mexico</td><td>129,807</td><td>1,059</td><td>0.82%</td><td>7,522</td><td>5.79%</td><td>8,581</td><td>6.61%</td></tr><tr class="even"><td>Indonesia</td><td>125,920</td><td>7,269</td><td>5.77%</td><td>21,676</td><td>17.21%</td><td>28,945</td><td>22.99%</td></tr><tr class="odd"><td>Philippines</td><td>118,762</td><td>3,787</td><td>3.19%</td><td>5,242</td><td>4.41%</td><td>9,029</td><td>7.60%</td></tr><tr class="even"><td>Brazil</td><td>104,819</td><td>2,118</td><td>2.02%</td><td>9,432</td><td>9.00%</td><td>11,550</td><td>11.02%</td></tr><tr class="odd"><td>Germany</td><td>104,009</td><td>822</td><td>0.79%</td><td>4,499</td><td>4.33%</td><td>5,321</td><td>5.12%</td></tr><tr class="even"><td>Malaysia</td><td>94,843</td><td>2,177</td><td>2.30%</td><td>10,470</td><td>11.04%</td><td>12,647</td><td>13.33%</td></tr><tr class="odd"><td>India</td><td>92,005</td><td>2,625</td><td>2.85%</td><td>5,641</td><td>6.13%</td><td>8,266</td><td>8.98%</td></tr><tr class="even"><td>Canada (Ontario)</td><td>86,169</td><td>3,342</td><td>3.88%</td><td>2,937</td><td>3.41%</td><td>6,279</td><td>7.29%</td></tr><tr class="odd"><td>Chile</td><td>83,174</td><td>506</td><td>0.61%</td><td>1,199</td><td>1.44%</td><td>1,705</td><td>2.05%</td></tr><tr class="even"><td>Poland</td><td>76,789</td><td>982</td><td>1.28%</td><td>6,745</td><td>8.78%</td><td>7,727</td><td>10.06%</td></tr><tr class="odd"><td>United States (Texas)</td><td>69,494</td><td>1,650</td><td>2.37%</td><td>1,895</td><td>2.73%</td><td>3,545</td><td>5.10%</td></tr><tr class="even"><td>Italy</td><td>68,187</td><td>2,200</td><td>3.23%</td><td>4,935</td><td>7.24%</td><td>7,135</td><td>10.46%</td></tr><tr class="odd"><td>Romania</td><td>62,046</td><td>302</td><td>0.49%</td><td>3,029</td><td>4.88%</td><td>3,331</td><td>5.37%</td></tr><tr class="even"><td>United States (New York)</td><td>61,482</td><td>1,647</td><td>2.68%</td><td>1,861</td><td>3.03%</td><td>3,508</td><td>5.71%</td></tr></tbody></table>
</div>
<br>
</div>
<p>
We will continue our DNS tests and likely publish an update to this blog post in the future.
</p>
<h3 id="tm">Test methodology</h3>
<p>
Sajal used the following setup:
<ul>
<li>RUM.js. Added a few lines of code to this JS file, to send request to a random hostname for which our DNS server is the authoritative name server
</li><li>HTTP server (Nginx). Returns a 204 response to all requests. Uploads logfiles to S3 every hour
</li><li>Node.js based DNS server. Uses the excellent <a href="https://github.com/tjfontaine/node-dns" class="external-link">native-dns library</a>. Gives same response with very low TTL to all queries. Echoes back the client-subnet (if any) with scope netmask of 32. Uploads logfiles to S3 every hour
</li><li>Desktop. Offline analysis after downloading logfiles from S3. Using MongoDB for storage, <a href="https://discoproject.org/">Disco</a> for parallel processing
</li></ul>
</p><p>The HTTP server and DNS server ran on an EC2 instance in US-East.
</p>
<p>
<img loading="lazy" src="https://www.cdnplanet.com/static/img/dnstest-process-.png" width="641" height="341" alt="DNS test procedure">
</p>
<p>
The first step is to get the user's IP and their resolver's IP
<ol>
<li>Generate a uuid in JavaScript using random numbers and current timestamp
</li><li>User tries to access https://<uuid>.example.com/foo.jpg
</li><li>User asks for the IP of <uuid>.example.com from the resolver configured in the user's machine
</li><li>The configured resolver asks our authoritative DNS server. We log the IP of resolver and the uuid here. Also log EDNS parameters if available
</li><li>User finally establishes HTTP connection to <uuid>.example.com. We log the IP of the user and the uuid
</li><li>Match users IP to resolver IP where uuid matches
</li></ol>
</p><p>Each user's IP was considered only once a day !
</p>
<p>
Step two is to match ASNs, country, etc. <br>
We detected the ASN of the DNS resolver's IP and user's IP and then detected geo location of the user, geo location of the dns server, whether edns was used or not, the edns paramaters that came with the query and user agent.
If the ASN matched Google Public DNS or OpenDNS, we used information on these webpages (<a href="https://developers.google.com/speed/public-dns/faq#locations" class="external-link">Google DNS server locations</a>, <a href="https://www.opendns.com/data-center-locations/" class="external-link">OpenDNS network map</a>) to figure out its location.
</p>
<script type="text/javascript">
(function() {
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
ga.src = '/scripts/public-dns-mkt-share.js?sajal=cool';
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
})();
</script>Announcing the CloudFront Purge Tool2012-01-23T01:00:00+01:00https://www.cdnplanet.com/blog/announcing-cloudfront-purge-tool/Currently, you cannot purge files from CloudFront via the AWS console, which means you always have to do it via the API. The CloudFront Purge Tool, a Chrome extension, takes away the hassle of using the AWS API, and enables you to quickly purge one or more files. Yes, it is secure.
<div class="notice-msg info">
You should not use this tool. It probably does not work anymore.<br>
Nowadays, CloudFront enables you to <a href="https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/Invalidation.html#Invalidation_Requests">purge files via the AWS console</a>.
<br>Follow us on <a href="https://twitter.com/cdnplanet" class="external-link">Twitter</a> to hear about the launch of new tools (coming soon!)
</div>
<p>
The CDN Planet website runs in the Amazon cloud and we like it a lot. But there is one thing we bumped into some time ago that bothered us: you can't purge files from <a href="https://www.cdnplanet.com/cdns/cloudfront/">Amazon CloudFront</a> using the AWS console. And so, we searched the Web for a good, free and secure tool for easy CloudFront purging. We found nothing we liked, so we built it, as a Chrome extension. And we came up with a brilliant name for it: the <strong>CloudFront Purge Tool</strong> ;-)
</p>
<p>The CloudFront Purge Tool is a simple, intuitive and <strong>secure</strong> app that installs on your local computer, in the Chrome browser. You can read the full description and view screenshots on our <a href="https://www.cdnplanet.com/tools/cloudfront-purge-tool/">CloudFront Purge Tool</a> page.
</p>
<p>But, you can also skip that and go directly to the <a href="https://chrome.google.com/webstore/detail/gadbenpggeagjgnameagcpjnjdlckkpb" class="external-link">Chrome web store</a> to install the app. Enjoy!
</p>How to do an effective trial with a CDN2012-01-11T01:00:00+01:00https://www.cdnplanet.com/blog/how-do-effective-trial-cdn/You are in the process of selecting a Content Delivery Network and have a shortlist of candidates. Next step: do a trial with them. How can you best do a trial with a CDN? Read our tips.
<div class="notice-msg info">
<strong>Tip</strong>: Dummy Origin, our free and open source tool, makes it easier to evaluate the (caching) behavior of a CDN. Read
<a href="https://www.cdnplanet.com/blog/cdn-behavior-testing-dummy-origin/">CDN behavior testing with Dummy Origin</a>
</div>
<p>
When you are in the process of deciding which CDN to use, at some point in the selection process you will have a shortlist of 1, 2 or 3 CDNs that are serious candidates. These CDNs meet your key requirements and you had positive conversations with them. As mentioned before in my article <a href="https://www.cdnplanet.com/blog/how-select-cdn/">How to Select a CDN</a>, the next step is to do a (free) trial with those Content Delivery Networks. This is a key step in the decision making process and one you must never skip.
</p>
<h2>Goals of the trial</h2>
<p>In my opinion, the goal of the trial is simple: be in a better position to decide which Content Delivery Network to sign up with. Doing a trial is all about gathering real experience in using the CDN and working with that particular CDN company. We strongly recommend to free up enough time for the trial, so you can truly answer the following questions afterwards:
<ul>
<li>Does the CDN do what I want/expect?
</li><li>Is it easy to use?
</li><li>Is it fast?
</li></ul>
</p><p>Let's start with a few tips on how to do the trial, the approach to take.
</p>
<h2>Approach to the trial</h2>
<p>
Let me repeat: free up enough time to do the trial. It is very important, so don't do it in your spare time. Focus on the important stuff.
</p>
<p>Create the evaluation document, in which you will log your findings.
Put all your requirements in a 3-column table: Requirement, Score, Remarks.
For Score, put in either a -1, 1, 3 or 5, with -1 meaning "not available", 1 meaning "bad", 3 meaning "ok" and 5 meaning "excellent".
Remarks is a free text field to write down any relevant notes, probably to help later remind you why you gave that score.
After the trial, you can calculate the total score for each CDN.
</p><p>
Create a real-world test page (e.g. a copy of your homepage) and switch on the CDN for that.
Even better: use your real site. This may be a bit risky, and if you decide to do this, make sure you are closely monitoring website performance.
</p>
<p>
Set up the CDN. Configure it to do what you want, e.g. turn on Gzip compression, create the custom CNAME, etc. The CDN provider should play a (pro-)active role in this, advicing you on what is best, of course taking your requirements as a starting point. Involve them!
</p>
<p>
Use the CDN! Make changes to the config, view and analyze reports, etc.
</p>
<h2>Evaluating Functionality</h2>
<p>You should carefully evaluate all the functionalities/features that you have listed in your requirements document, using the above mentioned evaluation document. </p>
In my opinion, the following should definitely be in scope:
<ul>
<li>Online control panel: ease of use, responsiveness, look & feel
</li><li>Change propagation time: how fast is a change implemented on the CDN platform/edge servers?
</li><li>Security: is the online control panel secure? do you get a different password for FTP?
</li><li>Reports: relevance of data, delay, ability to export data (as CSV)
</li></ul>
<h2>Evaluating Customer Service</h2>
<p>If you need help, you want to get it in a swift, pleasant way, with a satisfying result.
Be sure to evaulate these aspects of customer service:</p>
<ul>
<li>Choice of contact method/medium (phone, email, online ticketing)
</li><li>Responsiveness
</li><li>Quality of the response
</li><li>Friendliness
</li><li>Costs: can you call a free or local number?
</li></ul>
<p>Tip: contact the customer service department often, not just once. And don't talk to the same representative every time, but talk to several.
</p>
<h2>Evaluating Performance</h2>
<p>
As improving performance is the #1 reason to start using a CDN, evaluating performance during a trial is very important.
And it is also difficult to get a grip on.
Some Content Delivery Networks will play a role in this. Cotendo for example provides Keynote performance reports, free of charge, which is nice.
</p>
<p>One thing you can do, easily and for free, is run web page performance tests using <a href="https://www.webpagetest.org/" class="external-link">Webpagetest.org</a> on that test page.
Do it often and from from various, relevant geographical locations.
</p>Initcwnd settings of major CDN providers2011-11-30T01:00:00+01:002017-02-13T01:00:00+01:00https://www.cdnplanet.com/blog/initcwnd-settings-major-cdn-providers/Initcwnd is one thing to tweak on the server to improve TCP performance. What is the initcwnd value of CDN providers? Find out what our tests in Feb 2017 show and learn how we tested.
<p>
In November 2011, we first published the initcwnd values of CDNs, following our blog post <a href="https://www.cdnplanet.com/blog/tune-tcp-initcwnd-for-optimum-performance/">Tuning initcwnd for optimum performance</a> that showed how tuning the initial congestion window parameter (initcwnd) on the server can have a significant improvement in TCP performance.
</p>
<p>
CDNs constantly tune their platform to improve performance. When we updated this article on August 27 2014, we measured higher initcwnd values for many CDNs (compared to the 2011 measurements). The most recent measurements (February 13 2017) again show some CDNs have increased the initcwnd size but interestingly for other CDNs we measured a lower value than in 2014. Also, quite a few CDNs have still not changed to be higher than the default 10 in Linux kernel.<br>
First we show you the data, then some <a href="#con">conclusions</a> and finally we describe our <a href="#method">test methodology</a>.
</p>
<h2>CDN initcwnd values</h2>
<table class="table-condensed table-striped valign-text-top">
<thead>
<tr>
<th>CDN</th>
<th>Initcwnd value (Feb 2017)</th>
<th>Initcwnd value (Aug 2014)</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="https://www.cdnplanet.com/cdns/cachefly/">Cachefly</a></td>
<td><img loading="lazy" alt="down" src="https://www.cdnplanet.com/static/img/arrow-down-bold-circle.svg" width="24" height="24" class="icon-in-table-cell">46</td>
<td>66</td>
</tr>
<tr>
<td><a href="https://www.cdnplanet.com/cdns/lumen/">Level3 (now Lumen)</a></td>
<td><img loading="lazy" alt="up" src="https://www.cdnplanet.com/static/img/arrow-up-bold-circle.svg" width="24" height="24" class="icon-in-table-cell">46</td>
<td>12</td>
</tr>
<tr>
<td><a href="https://www.cdnplanet.com/cdns/stackpath/">Highwinds</a></td>
<td><img loading="lazy" alt="up" src="https://www.cdnplanet.com/static/img/arrow-up-bold-circle.svg" width="24" height="24" class="icon-in-table-cell">44</td>
<td>36</td>
</tr>
<tr>
<td><a href="https://www.cdnplanet.com/cdns/akamai/">Akamai</a></td>
<td><img loading="lazy" alt="up" src="https://www.cdnplanet.com/static/img/arrow-up-bold-circle.svg" width="24" height="24" class="icon-in-table-cell">32</td>
<td>16</td>
</tr>
<tr>
<td><a href="https://www.cdnplanet.com/cdns/edgio/">Edgecast (now Edgio)</a></td>
<td><img loading="lazy" alt="up" src="https://www.cdnplanet.com/static/img/arrow-up-bold-circle.svg" width="24" height="24" class="icon-in-table-cell">30</td>
<td>10</td>
</tr>
<tr>
<td><a href="https://www.cdnplanet.com/cdns/cloudfront/">CloudFront</a></td>
<td><img loading="lazy" alt="up" src="https://www.cdnplanet.com/static/img/arrow-up-bold-circle.svg" width="24" height="24" class="icon-in-table-cell">25</td>
<td>10</td>
</tr>
<tr>
<td><a href="https://www.cdnplanet.com/cdns/cdnetworks/">CDNetworks</a></td>
<td><img loading="lazy" alt="up" src="https://www.cdnplanet.com/static/img/arrow-up-bold-circle.svg" width="24" height="24" class="icon-in-table-cell">22</td>
<td>10</td>
</tr>
<tr>
<td><a href="https://www.cdnplanet.com/cdns/tata-communications/">Tata Communinications</a></td>
<td><img loading="lazy" alt="same" src="https://www.cdnplanet.com/static/img/minus-circle.svg" width="24" height="24" class="icon-in-table-cell">10</td>
<td>10</td>
</tr>
<tr>
<td><a href="https://www.cdnplanet.com/cdns/stackpath/">MaxCDN (now Stackpath)</a></td>
<td><img loading="lazy" alt="down" src="https://www.cdnplanet.com/static/img/arrow-down-bold-circle.svg" width="24" height="24" class="icon-in-table-cell">10</td>
<td>32</td>
</tr>
<tr>
<td><a href="https://www.cdnplanet.com/cdns/fastly/">Fastly</a></td>
<td><img loading="lazy" alt="same" src="https://www.cdnplanet.com/static/img/minus-circle.svg" width="24" height="24" class="icon-in-table-cell">10</td>
<td>10</td>
</tr>
<tr>
<td><a href="https://www.cdnplanet.com/cdns/cloudflare/">Cloudflare</a></td>
<td><img loading="lazy" alt="same" src="https://www.cdnplanet.com/static/img/minus-circle.svg" width="24" height="24" class="icon-in-table-cell">10</td>
<td>10</td>
</tr>
<tr>
<td><a href="https://www.cdnplanet.com/cdns/cdn77/">CDN77</a></td>
<td><img loading="lazy" alt="same" src="https://www.cdnplanet.com/static/img/minus-circle.svg" width="24" height="24" class="icon-in-table-cell">10</td>
<td>10</td>
</tr>
<tr>
<td><a href="https://www.cdnplanet.com/cdns/leaseweb/">Leaseweb</a></td>
<td><img loading="lazy" alt="same" src="https://www.cdnplanet.com/static/img/minus-circle.svg" width="24" height="24" class="icon-in-table-cell">10</td>
<td>10</td>
</tr>
</tbody>
</table>
<div class="notice-msg info">
The initcwnd value is not the only parameter that determines CDN performance. Don't think CacheFly is the fastest CDN globally because their initcwnd is highest. The initcwnd value is just *one* of the performance parameters.
</div>
<h2 id="con">Conclusions</h2>
<p>Six out of the measured 15 CDNs have a initcwnd of 10. This is the default value in Linux kernel and apparently many these CDNs have found this to work well. Most the others set the initcwnd to be higher than 20 except for Limelight which is at 14. CacheFly still is at top with a value of 46. This is 20 lower than the 2014 measured value but very likely this is a direct result of how we measured initcwnd this time which is different from last time. Read on ...
</p>
<h2 id="method">Test Methodology</h2>
<p>During the 2014 measurements, the tests were conducted on a Macbook Air in The Netherlands with an initial receive window (initrwnd) set to a high 262144.
For each CDN, we made requests to some far-away POP (US-West or APAC) to ensure a high RTT to make it easier reading tcpdumps. For each test, we made few hits to prime the cache at the edge servers. We then studied the tcpdumps, ran the entire process several times for sanity check. For the global IP Anycast CDNs like Cachefly and Cloudflare We added an extra 500ms latency using <a href="https://cs.baylor.edu/~donahoo/tools/dummy/" class="external-link">Dummynet</a>. No dummy packet loss was added.<br>
We used <a href="https://www.cdnplanet.com/uploads/probe.py.txt">this python script</a> to run tests and capture results using tcpdump. The dumps were manually analyzed in wireshark as <a href="https://www.cdnplanet.com/blog/tune-tcp-initcwnd-for-optimum-performance/#howtotest" title="How to test initcwnd">described here</a>
</p>
<p>That was all in 2014. In 2017, we got lazy and simply used the <a href="https://www.cdnplanet.com/tools/initcwndcheck/">Initcwnd Checker tool</a> on this CDN Planet website. We measured the initcwnd of every CDN a few times. The tool works just fine. The only differences with the 2014 testing methodology are:</p>
<ol>
<li>the initial receive windown was 65000</li>
<li>the tests ran from a AWS EC2 instance in US-East</li>
<li>we did not take the trouble to figure out what the IPs are of far-away POPs and/or add latency to have higher RTTs</li>
</ol>
<p>
Differences 2 and 3 are not so relevant but the lower initial receive window is important and explains why we measured 'just' a initcwnd of 46 for Cachefly.
46 packets at ~1400 bytes each is ~65000 bytes and that is the initrwnd on the test machine. In other words, the test machine could not receive more than 65000 bytes in the first roundtrip and so Cachefly served no more than 46 packets. Had we advertised a higher initrwnd, maybe Cachefly would have sent out more packets.
</p>
Tuning initcwnd for optimum performance2011-10-25T02:00:00+02:00https://www.cdnplanet.com/blog/tune-tcp-initcwnd-for-optimum-performance/Tuning the initial congestion window parameter (initcwnd) on the server can have a significant improvement in TCP performance, resulting in faster downloads and faster webpages. Get an in-depth understanding of TCP slow start and how to tune for performance.
<p>Tuning the initial congestion window parameter (initcwnd) on the server can have a <strong>significant improvement in TCP performance</strong>, resulting in faster downloads and faster webpages.</p>
<p>In this article, I will start with an introduction to how TCP/IP connections work with regards to HTTP. Then I will go into <a href="#slow-start">TCP slow start</a> and show how <a href="#change-initcwnd">tuning the initcwnd setting</a> on the (Linux) server can greatly improve page performance.</p>
<p>In our follow-up article we show data on the value of the initcwnd setting for the various CDNs: <a href="https://www.cdnplanet.com/blog/initcwnd-settings-major-cdn-providers/">Initcwnd settings of major CDN providers</a>.</p>
<h2>Three-way handshake</h2>
<p>Imagine a <em>client</em> wants to request the webpage https://www.example.com/ from a <em>server</em>. Here is an over simplified version of the transaction between client and server. The requested page is 6 KB and we assume there is no overhead on the server to generate the page (e.g. it's static content cached in memory) or any other overhead: we live in an ideal world ;-)</p>
<img src="https://www.cdnplanet.com/static/img/TCP1.png" height="485" width="514" alt="TCP Slow Start" title="Illustration showing TCP Slow Start">
<ul>
<li>Step 1: Client sends SYN to server - "How are you? My receive window is 65,535 bytes."
</li><li>Step 2: Server sends SYN, ACK - "Great! How are you? My receive window is 4,236 bytes"
</li><li>Step 3: Client sends ACK, SEQ - "Great as well... Please send me the webpage https://www.example.com/"
</li><li>Step 4: Server sends 3 data packets. Roughly 4 - 4.3 kb (3*MSS<sup>1</sup>) of data
</li><li>Step 5: Client acknowledges the segment (sends ACK)
</li><li>Step 6: Server sends the remaining bytes to the client
</li></ul>
<small>1. MSS = <a href="https://en.wikipedia.org/wiki/Maximum_segment_size" class="external-link">Maximum Segment Size</a></small>
<p>After step 6 the connection can be ended (FIN) or kept alive, but that is irrelevant here, since at this point the browser has already received the data.</p>
<p>The above transaction took 3*RTT (Round Trip Time) to finish. If your RTT to a server is 200ms this transaction <strong>will</strong> take you <strong>at least</strong> 600ms to complete, no matter how big your bandwidth is. The bigger the file, the more round trips and the longer it takes to download.</p>
<h2 id="slow-start">Congestion control/TCP Slow Start</h2>
<p>TCP/IP has a built-in mechanism to avoid congestion on the network called slow start.</p>
<p>Here is a good explanation on Youtube:</p>
<iframe style="border:0;" width="560" height="315" src="https://www.youtube.com/embed/ObOVClMZXGM" allowfullscreen=""></iframe>
<p class="m-t-10">As illustrated in the video and as you have seen in our example transaction in the section above, a server does not necessarily adhere to the client's RWIN (receivers advertised window size). The client told the server it can receive a maximum of 65,535 bytes of un-acknowledged data (before ACK), but the server only sent about 4 KB and then waited for ACK. This is because the initial congestion window (initcwnd) on the server is set to 3. The server is being cautious. Rather than throw a burst of packets into a fresh connection, the server chooses to ease into it gradually, making sure that the entire network route is not congested. The more congested a network is, the higher is the chances for packet loss. Packet loss results in retransmissions which means more round trips, resulting in higher download times.</p>
<p>Basically, there are 2 main parameters that affect the amount of data the server can send at the start of a connection: the receivers advertised window size (<em>RWIN</em>) and the value of the initcwnd setting on the server. The initial transfer size will be the lower of the 2, so if the initcwnd value on the server is a lot lower than the RWIN on the computer of the user, the initial transfer size is less then optimal (assuming no network congestion).
It is easy to <a href="#change-initcwnd">change the initcwnd setting on the server</a>, but not the RWIN. Different OSes have different RWIN settings, as shown in the table below.</p>
<table class="table-striped table-responsive margin-bottom-small">
<thead>
<tr>
<th class="bg-blue-dark white">OS</th>
<th class="bg-blue-dark white">RWIN</th>
</tr>
</thead>
<!-- research and put table -->
<tbody>
<tr class="odd">
<td>Linux 2.6.32</td>
<td>3*MSS (usually 5,840)</td>
</tr>
<tr class="even">
<td>Linux 3.0.0</td>
<td>10*MSS (usually 14,600)</td>
</tr>
<tr class="odd">
<td>Windows NT 5.1 (XP)</td>
<td>65,535 <sup>2</sup></td>
</tr>
<tr class="even">
<td>Windows NT 6.1 (Windows 7 or Server 2008 R2)</td>
<td>8,192 <sup>2</sup></td>
</tr>
<tr class="odd">
<td>Mac OS X 10.5.8 (Leopard)</td>
<td>65,535 <sup>2</sup></td>
</tr>
<tr class="even">
<td>Mac OS X 10.6.8 (Snow Leopard)</td>
<td>65,535 <sup>2</sup></td>
</tr>
<tr class="odd">
<td>Apple IOS 4.1</td>
<td>65,535 <sup>2</sup></td>
</tr>
<tr class="even">
<td>Apple IOS 5.1</td>
<td>65,535 <sup>2</sup></td>
</tr>
</tbody>
</table>
<small>2. Some Operating Systems dynamically calculate RWIN based on external factors. The value here is based on SYN packets sent to CDN Planet. The Win flag can also be increased by the client before the transfer actually starts.</small>
<p>A you can see from the table, Windows and Mac users would benefit most from servers sending more bytes in the initial transfer (which is almost everybody!)
</p>
<h2 id="change-initcwnd">Changing initcwnd</h2>
<p>Adjusting the value of the initcwnd setting on Linux is simple. Assuming we want to set it to 10:</p>
<p>Step 1: check route settings.</p>
<pre>
sajal@sajal-desktop:~$ ip route show
192.168.1.0/24 dev eth0 proto kernel scope link src 192.168.1.100 metric 1
169.254.0.0/16 dev eth0 scope link metric 1000
default via 192.168.1.1 dev eth0 proto static
sajal@sajal-desktop:~$
</pre>
<p>Make a note of the line starting with default.</p>
<p>Step 2: Change the default settings.<br>
Paste the current settings for default and add <em>initcwnd 10</em> to it.</p>
<pre>
sajal@sajal-desktop:~$ sudo ip route change default via 192.168.1.1 dev eth0 proto static initcwnd 10
</pre>
<p>Step 3: Verify</p>
<pre>
sajal@sajal-desktop:~$ ip route show
192.168.1.0/24 dev eth0 proto kernel scope link src 192.168.1.100 metric 1
169.254.0.0/16 dev eth0 scope link metric 1000
default via 192.168.1.1 dev eth0 proto static initcwnd 10
sajal@sajal-desktop:~$
</pre>
<h2>Changing initrwnd</h2>
<p>The advertised receive window on Linux is called initrwnd. It can only be adjusted on linux kernel 2.6.33 and newer <a href="https://blog.habets.pp.se/2011/10/Optimizing-TCP-slow-start" title="Optimizing TCP slow start" class="external-link">(H/T: Thomas Habets)</a>.<br>
This is relevant for CDN servers because they interact with the origin server and it is relevant to other servers that interact with (3rd party) servers, e.g. most Web 2.0 sites that get/send data via API calls. If the interaction between your server and the other server is faster, then what you are sending to real end users will be faster too!
</p>
<p>Step 1: check route settings.</p>
<pre>
sajal@sajal-desktop:~$ ip route show
192.168.1.0/24 dev eth0 proto kernel scope link src 192.168.1.100 metric 1
169.254.0.0/16 dev eth0 scope link metric 1000
default via 192.168.1.1 dev eth0 proto static
sajal@sajal-desktop:~$
</pre>
<p>Make a note of the line starting with default.</p>
<p>Step 2: Change the default settings.<br>
Paste the current settings for default and add <em>initrwnd 10</em> to it.</p>
<pre>
sajal@sajal-desktop:~$ sudo ip route change default via 192.168.1.1 dev eth0 proto static initrwnd 10
</pre>
<p>Step 3: Verify</p>
<pre>
sajal@sajal-desktop:~$ ip route show
192.168.1.0/24 dev eth0 proto kernel scope link src 192.168.1.100 metric 1
169.254.0.0/16 dev eth0 scope link metric 1000
default via 192.168.1.1 dev eth0 proto static initrwnd 10
sajal@sajal-desktop:~$
</pre>
<p>This changes the <em>initcwnd</em> and <em>initrwnd</em> until the next reboot. I don't know of any better way to make it persist. if you know of a way please mention it in the comments.</p>
<p><strong>Note: Don't attempt this if you don't know what you are doing. Maybe it's better to try this at console, so you can recover if you manage to ruin your networking settings. A reboot should fix it.</strong></p>
<p><span class="clearfix" style="text-decoration:line-through;">I have absolutely no idea how to change this for windows servers. If anyone does please post it in comments. We will update this article.</span>
After <a href="https://twitter.com/#!/dritans" class="external-link">@dritans</a> gave him some pointers, <a href="https://twitter.com/#!/andydavies" class="external-link">@andydavies</a> did the hard work and made it happen on Windows 2008 server. Read how to do it in his excellent blog post <a href="https://andydavies.me/blog/2011/11/21/increasing-the-tcp-initial-congestion-window-on-windows-2008-server-r2/" class="external-link">Increasing the TCP Initial Congestion Window on Windows 2008 Server R2</a>.
</p>
<h2>Results</h2>
<p>Take another look at the illustration in the <em>Three-way handshake</em> section. With the new initcwnd setting, the browser-server interaction will look like this:</p>
<img loading="lazy" src="https://www.cdnplanet.com/static/img/TCP2.png" height="384" width="518" alt="TCP Slow Start - after tuning" title="Illustration showing TCP Slow Start, after tuning">
<p>The entire transaction now happened in 400ms, rather than 600ms. That is a big win and this is just for one HTTP request. Modern browsers open 6 connections to a host which means you will see a faster load time for 6 objects
</p>
<p>Here is a before and after comparison of accessing a 14 KB file (13,516 bytes transfered ~10*MSS) from across the globe with different settings.</p>
<img loading="lazy" src="https://www.cdnplanet.com/static/img/initcwnd_chart.png" height="371" width="600" alt="Accessing 14 KB file with various initcwnd settings" title="Chart showing download times of a 14 KB file with various initcwnd settings">
<p><small><a href="https://www.cdnplanet.com/uploads/initcwnd-effects.txt">Download the raw data</a></small></p>
<p class="m-t-10">It is clear that when initcwnd is larger than the payload size, the entire transaction happens in just 2*RTT. The graph shows that the total load time of this object was reduced by ~50% by increasing initcwnd to 10. A great performance gain!</p>
<h2 id="howtotest">How to test initcwnd?</h2>
<p>To test this behavior, it is very important to have <a href="https://www.sajalkayan.com/check-if-you-are-behind-a-transparent-proxy.html" class="external-link">clean internet</a>. If there is any proxy between you and the destination host, your test efforts are useless.</p>
<p>Open your favorite packet capture tool (e.g. <a href="https://www.wireshark.org/">Wireshark</a>) and access a file. If possible, access it from a server with high RTT from you. Analyze the dump. It is very very important to make sure you use a fresh connection -- if testing from browser, restart the browser between tests.</p>
<img loading="lazy" src="https://www.cdnplanet.com/static/img/dump-maxcdn.png" height="438" width="587" alt="Packet capture of a transaction with MaxCDN" title="Packet capture of a transaction with MaxCDN"><br>
<p>The image above shows a transaction with MaxCDN. Based on this capture, the RTT is 168ms. The first data packet (shown here as <em>[TCP segment of a reassembled PDU]</em>) arrives at the 409ms mark. So, between 409ms and 577ms there were only 2 data packets received. This shows that MaxCDN will initially allow only 2 un-acknowledged data packets on the wire, hence initcwnd = 2.</p>
<h2>Content Delivery Networks and big sites</h2>
<p>The initcwnd setting is especially more important for Content Delivery Networks. CDN servers act as a proxy between your content and your users, in two roles:</p>
<ul>
<li><em>Server</em> - Responding to requests from users.
</li><li><em>Client</em> - Making requests to the origin server (on cache MISS)
</li></ul>
<img loading="lazy" src="https://www.cdnplanet.com/static/img/tcp-performance-tuning_2.png" width="600" height="154" alt="Browser, CDN and origin server interaction and TCP performance settings" title="Illustration showing interaction between browser, CDN server and origin server; and TCP performance settings">
<p>Now, when the CDN acts as the <em>server</em>, the initcwnd setting on the CDN is an important factor in determining the first segment size of a new connection. But when the CDN acts in the <em>Client</em> role, the CDN's advertised receive window will be the bottleneck if the origin server's initcwnd is higher than the CDN server's initrwnd.
Since most CDNs do not keep the connection to the origin alive, a cache MISS suffers slow start twice!</p>
<p>In our opinion, CDNs should tune their servers and have high values for both initcwnd and initrwnd. We are in the process of probing TCP settings of CDNs and popular sites. We will publish our findings in the coming days.</p>
<p>Interested in more info and insight on tuning initcwnd? Read Google's paper <a href="https://static.googleusercontent.com/media/research.google.com/en//pubs/archive/36640.pdf" class="external-link">An Argument for Increasing TCP's Initial Congestion Window</a>. It's a great resource.</p>
<h2>Wish list</h2>
<p>This is what I ask for my next birthday:</p>
<ul>
<li>Increase <a href="https://en.wikipedia.org/wiki/Maximum_transmission_unit" class="external-link">MTU</a>
</li><li>Increase default initcwnd in OSes
</li><li>Have a better congestion avoiding system. One that does not involve slow start.
</li><li>CDNs keep persistent connections to origin, at least for few minutes.
</li><li>CDNs have a large RWIN size (initrwnd), not stick to the default value in Linux
</li></ul>
<h2>Big disclaimer</h2>
<p>Your mileage may vary! Apply any of the suggestions above at your own risk. The author is no expert on networking or TCP/IP. Increasing initcwnd will make the traffic more bursty, some networking equipment may not be able to keep up with that.</p>Browser cachability issues with CloudFront2011-10-11T02:00:00+02:00https://www.cdnplanet.com/blog/cloudfront-cachability-date-header/In this post, I intend to illustrate some potential issues with browser cachability when using Amazon CloudFront and some possible work-arounds. These are some issues all CloudFront users should be made aware of. Sending stale responses to users may have a big impact on user experience.
<div class="notice-msg info">
This article is outdated. Read about <a href="https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/ConfiguringCaching.html">Optimizing Content Caching and Availability</a> in the official AWS CloudFront docs.
</div>
<p>
In this post, I intend to illustrate some potential issues with browser cachability when using <a href="https://www.cdnplanet.com/cdns/cloudfront/" title="Amazon CloudFront CDN">Amazon CloudFront</a> and some possible work-arounds. CloudFront users should be aware of these issues, because sending stale responses to users may have a big impact on the user experience.
</p>
<p>One of the Holy Grails of web performance is browser caching. Google lists it as the <a href="https://code.google.com/speed/page-speed/docs/caching.html#LeverageBrowserCaching" class="external-link">first item</a> in their <a href="https://code.google.com/speed/page-speed/docs/rules_intro.html" class="external-link">Web Performance Best Practices</a>.</p>
<p>First, some primer into web-caching. If you are well versed in the topic, <a href="#proxybehavior">skip this section</a>.</p>
<h2>How does browser caching work?</h2>
<p>The <code>Expires</code> and <code>Cache-Control</code> response headers are responsible for indicating the cachability of an object to browsers (and proxies). <code>Expires</code> is a part of HTTP/1.0 whereas <code>Cache-Control</code> was introduced in HTTP/1.1 . On HTTP/1.1 compliant agents, <code>Cache-Control</code> supersedes the <code>Expires</code> header. All browsers released in the last ~10 years implement HTTP/1.1.</p>
<p>Lets examine the response headers for the URL : https://ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.min.js</p>
<pre>
sajal@sajal-desktop:~$ HEAD https://ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.min.js
200 OK
Cache-Control: private, x-gzip-ok="", max-age=31536000
Connection: close
Date: Sat, 08 Oct 2011 15:11:26 GMT
Server: sffe
Vary: Accept-Encoding
Content-Type: text/javascript; charset=UTF-8
Expires: Sat, 08 Oct 2011 15:11:26 GMT
Last-Modified: Thu, 22 Sep 2011 14:12:07 GMT
Client-Date: Sat, 08 Oct 2011 15:11:26 GMT
Client-Peer: 209.85.175.95:80
Client-Response-Num: 1
X-Content-Type-Options: nosniff
X-Xss-Protection: 1; mode=block
sajal@sajal-desktop:~$
</pre>
<p>HTTP/1.0 agents see the <code>Expires: Sat, 08 Oct 2011 15:11:26 GMT</code> header and takes it as a permission to keep the object into cache untill Sat, 08 Oct 2011 15:11:26 GMT which is equivalent to saying this asset shouldn't be cached. HTTP/1.1 agents look at the <code>Cache-Control: private, x-gzip-ok="", max-age=31536000</code> header, and specifically the <code>max-age=31536000</code> portion, allowing the agent to keep it in cache for 31536000 seconds or roughly one year since the value of the <code>Date</code> header which is <code>Sat, 08 Oct 2011 15:11:26 GMT</code>.</p>
<p>There are other directives in <code>Cache-Control</code>, but for the sake of this discussion we ignore them. Basically the directive <code>private</code> says that the object should only be cached on the browser and not at upstream proxies. If the directive was <code>public</code> then any upstream proxy could cache it.</p>
<p>Note: Many modern HTTP/1.0 agents implement some or all of HTTP/1.1 features</p>
<h2>Expiration Calculations</h2>
<h3>HTTP/1.0</h3>
<p>On HTTP/1.0 agents, the <code>Expires</code> header is solely responsible for determining cachability. The behavior of <code>Expires</code> is defined in <a href="https://tools.ietf.org/html/rfc1945#section-10.7" class="external-link">section 10.7</a> of <a href="https://tools.ietf.org/html/rfc1945" class="external-link">RFC 1945</a>.</p>
<ul>
<li>If <code>Expires</code> is equal or earlier than the <code>Date</code> header, the object should not be cached.
</li><li>If <code>Expires</code> is later than <code>Date</code>, then the object can be cached until the date as specified in the <code>Expires</code> header.
</li></ul>
<h3>HTTP/1.1</h3>
<p><a href="https://www.ietf.org/rfc/rfc2616.txt" class="external-link">HTTP/1.1</a> has a lot more complex algorithm and options for calculating cachability of the object. Calculate the following in seconds (simplified version).</p>
<ul>
<li>Age: Age of the object is the current time on the client minus the <code>Date</code> value.
</li><li>freshness_lifetime: This is the max_age_value if present, else <code>Date</code> minus <code>Expires</code> is used.
</li><li>cache_ttl: freshness_lifetime minus Age
</li></ul>
<p>Now, if cache_ttl is zero or a negative value, the object is stale and can't be cached. If cache_ttl is a positive, it can be kept in cache for that duration.</p>
<p>Take this example:-</p>
<pre>
Cache-Control: max-age=31536000
Connection: close
Date: Sat, 08 Oct 2011 15:11:26 GMT
Server: sffe
Vary: Accept-Encoding
Content-Type: text/javascript; charset=UTF-8
Expires: Sat, 08 Oct 2011 15:11:26 GMT
Last-Modified: Thu, 22 Sep 2011 14:12:07 GMT
Client-Date: Sat, 08 Oct 2011 15:11:26 GMT
Client-Peer: 209.85.175.95:80
Client-Response-Num: 1
X-Content-Type-Options: nosniff
X-Xss-Protection: 1; mode=block
</pre>
<ul>
<li>Age = Sat, 08 Oct 2011 15:11:26 GMT - Sat, 08 Oct 2011 15:11:26 GMT = 0 seconds
</li><li>freshness_lifetime = 31536000 seconds
</li><li>cache_ttl = 31536000 seconds - 0 seconds = 31536000 seconds = <strong>365 days</strong>
</li></ul>
<p>Now, if the <code>Date</code> was <code>Thu, 08 <strong>Sep</strong> 2011 15:11:26 GMT</code></p>
<ul>
<li>Age = Sat, 08 Oct 2011 15:11:26 GMT - Thu, 08 Sep 2011 15:11:26 GMT = 2592000 seconds
</li><li>freshness_lifetime = 31536000 seconds
</li><li>cache_ttl = 31536000 seconds - 2592000 seconds = 28944000 seconds = <strong>335 days</strong>
</li></ul>
<p>But, if the <code>Date</code> was <code>Wed, 08 <strong>Sep <span style="color:#c00;">2010</span></strong> 15:11:26 GMT</code></p>
<ul>
<li>Age = Sat, 08 Oct 2011 15:11:26 GMT - Wed, 08 Sep 2010 15:11:26 GMT = 395 days = 34128000 seconds
</li><li>freshness_lifetime = 31536000 seconds
</li><li>cache_ttl = 31536000 seconds - 34128000 seconds = <strong>-2592000 seconds</strong>
</li></ul>
<p>In the last example, the <code>cache_ttl</code> value is a negative value. The object is already stale when it reached the browser, hence it should not be cached.</p>
<p>In the above example, if the <code>Age</code> response header was present, we should use that value for the <code>cache_ttl</code> calculations.</p>
<h2 id="proxybehavior">Proxy behavior of CloudFront vs other CDNs</h2>
<p>As per RFC 2616, proxies are supposed to cache the <code>Date</code> and <code>Expires</code> headers from the origin. These are useful indications to the end client about the file properties. CloudFront follows the RFCs and sends cached <code>Date</code> and <code>Expires</code> values, however all other CDNs I tested act as if they were the origin and generate a fresh <code>Date</code> header.</p>
<p>Lets assume you have just implemented CloudFront for your newly launched website, and decided to use <code>Cache-Control: max-age=2592000</code>, thus allowing CloudFront (and browsers) to cache your object for 30 days. The <code>Date</code> header is cached at CloudFront, the <code>max-age</code> value also remains the same, what changes is the current time at your visitors' browser. 5 days after launch, your object would only be browser cacheble for 25 days. After 29 days, it would be cachable for 1 day. If the a user visited your site on day 29, then again on day 31 then both these visits would result in a request being made to CloudFront since the first visit had a <code>cache_ttl</code> of one day only.</p>
<h3>Browsers may be receiving expired content from CloudFront</h3>
<p>Now, after day 30, when the object gets stale in CloudFront, it would make a request to the origin to check for changes in the file using a conditional GET. If it gets a <code>304 Not modified</code> response, it refresh the cachability of the object, but continues to send the original <code>Date</code> header. All requests made to CloudFront for that object are already stale the moment it reaches the browser. All efforts you put into having the correct caching headers are now rendered useless.</p>
<table class="table-striped">
<thead>
<tr>
<th>Time since launch</th>
<th>Age(now - Date)</th>
<th>Age Header</th>
<th>cache_ttl in Firefox and Chrome</th>
</tr>
</thead>
<tbody>
<tr>
<td>0 days - Just Launched</td>
<td>0</td>
<td>0</td>
<td>2592000 (30 days)</td>
</tr>
<tr>
<td>1 day</td>
<td>86400</td>
<td>86400</td>
<td>2505600 (29 days)</td>
</tr>
<tr>
<td>29 days</td>
<td>2505600</td>
<td>2505600</td>
<td>86400 (1 day)</td>
</tr>
<tr>
<td>30 days</td>
<td>2592000</td>
<td>0</td>
<td><strong>0 (not cachable)</strong></td>
</tr>
<tr>
<td>31 days</td>
<td>2678400</td>
<td>86400</td>
<td><strong>-86400 (stale -not cachable)</strong></td>
</tr>
</tbody>
</table>
<p><small>One thing to note here is that CloudFront sends an appropriate <code>Age</code> header, which is the time since CloudFront last re-validated the object from the origin. Unfortunately, not all browsers follow this. Firefox and <a href="https://www.webpagetest.org/result/111008_2P_677e3e108b34d3ebbd0e1235019bb4d1/" class="external-link">Chrome</a> (link goes to a page with headers from i.ticdn.com) surprisingly seem to ignore the <code>Age</code> header, which is too bad. IE8 which does seem to take it into consideration is not suspectable to the stale object problem, but it would still face the reduced cache_ttl problem.</small></p>
<h2>Possible work-arounds</h2>
<ul>
<li>Have Expires/Cache-Control so far into the future that you are sure to have modified objects in the meantime. <code>CDN Planet</code> uses <code>max-age</code> of 10 years. To do this effectively, you also need to have versioned URLs, so that if you change something, the url should change too. (Tip: CloudFront ignores the querystring, so the cache buster should be in the path)
</li><li>Purge/invalidate objects at CloudFront at least once during the duration of <code>max-age</code>. Be aware: CloudFront charges for invalidation requests so this can add up if you have many files.
</li></ul>Compare CDNs, side-by-side2011-10-10T02:00:00+02:00https://www.cdnplanet.com/blog/compare-cdns-side-by-side/By popular demand, we now provide an easy way to compare two CDNs, side-by-side on a single page. No bells, no whistles, just all the useful info about the CDNs.<p>
In the days following our launch last week, <a href="https://twitter.com/#!/TedGoas/status/120900252445323264" class="external-link">several</a> <a href="https://twitter.com/#!/3rdEden/status/122774808378359809" class="external-link">people</a> told us they want an easy way to <strong>compare CDNs</strong> on CDN Planet. Sajal and I agreed this would improve the user experience and decided to start working on it immediately. And today we launch our new feature: <strong>easy, side-by-side CDN comparison</strong>.
</p>
<p>
From every CDN page (e.g. the <a href="https://www.cdnplanet.com/cdns/cachefly/">Cachefly</a> page), you simply select the CDN you want to compare it with in the 'Compare CDNs' box in the sidebar. That will take you to the Compare page, which is simple, straightforward and easy to use. It gives you a side-by-side presentation of POP locations, pricing and features. No bells, no whistles, just useful info presented in a nice way.
Take a look for example at the <a href="https://www.cdnplanet.com/compare/akamai/cachefly/">Akamai versus CacheFly</a> page or <a href="https://www.cdnplanet.com/compare/stackpath/level3/">Highwinds versus Level3</a> page.
</p>
<p>
We hope you find this new feature useful.
Don't hesitate to send us tips for further improving the user experience on CDN Planet!
</p>How to Select a CDN2011-10-03T02:00:00+02:00https://www.cdnplanet.com/blog/how-select-cdn/Your goal is to select the Content Delivery Network that is a perfect fit with your needs and requirements. Learn about the 4 categories of selection criteria (functionality, performance, costs and service) and read our tips on how to effectively and efficiently go through the selection process.
<p>
As WPO consultants, we help medium and large sized companies make their site faster. One of the questions we get a lot is "Do I need a Content Delivery Network?". If we answer "Yes, using a CDN is probably wise", the next question always is "which CDN should I use?".
</p>
<p>
In this article we want to give you some tips on <strong>how to select the right CDN</strong>.
There are many companies that offer Content Delivery Network services and talking to all of them can be time consuming.
And unless you are at least somewhat familiar with the way a Content Delivery Network works, the differences in technology and features/services, making the right choice can be a challenge.
</p>
<p>
Your goal is to select the CDN that is a perfect fit with your needs and requirements.
We hope this article (and the rest of the site!) helps you reach this goal.
</p>
<h2>Do I need a Content Delivery Network?</h2>
<p>
If your website visitors reside in multiple countries or even multiple continents, there is a good chance using a CDN makes sense.
And you don't need millions of visitors a day for a CDN to pay off.
Even at a low traffic level (~1,000 UVs a day) you should at least consider it (working with the Pay As You Go CDNs can be very easy).
</p>
<h3>Latency</h3>
<p>
The geographical distance between you servers and your visitors has a big impact on the user experience, because this distance determines in large how fast each object on your pages loads. The closer your visitors are to the server, the less time it takes between the moment a browser requests a webpage object (e.g. logo.gif) and the moment the browser receives the first byte from the server. That time is called latency.<br>
Latency is a big site speed killer. Decreasing latency is a key reason for using a Content Delivery Network because the CDN has servers in many locations in the world, placed in strategic locations (e.g. close to the backbone).
</p>
<h3>Server speed & throughput</h3>
<p>Besides latency, the 'power' of the servers is also very important. Your servers may be close to the visitors but they can still be slow, for a number of reasons, including:</p>
<ul>
<li>the servers cannot handle the number of concurrent connections from visitors' browsers (resulting in a queue)
</li><li>the hard drives are old and slow
</li><li>the servers are behind a undercapacity load balancer
</li><li>the servers have a small pipe to the internet
</li></ul>
<p>You can expect the CDN servers to be tuned for performance (e.g SSD hard drives) and are able to respond to requests quickly and send those bytes out fast.
</p>
<h2>CDN selection criteria</h2>
<p>What are looking for in a Content Delivery Network? Static files serving? Dynamic website acceleration? Streaming HD video? Is there a budget limit? Do you need 24/7 support in your native language?<br>
You have to think about your needs and requirements and write these down. We recommend grouping these in 4 categories: functionality, performance, costs and service.
</p>
<h3>Functionality/features</h3>
<p>Two questions apply here:</p>
<p><strong>1. What should the CDN be able to do?</strong><br>
Some things can be considered basic functionality, like Origin-Pull and Gzip compression. Other things are not so common, like a custom CNAME for secure webpages.
</p>
<p><strong>2. What should you, as a user of the CDN service, be able to do?</strong><br>
These may be on your requirements list, amongst others:
</p>
<ul>
<li>view (real-time) statistics on CDN usage
</li><li>upload files via FTP
</li><li>do a Purge-All via an API
</li><li>override the browser caching headers your origin server sends
</li></ul>
<p>Take your time to define your needs and requirements. This is very important.
</p>
<h3>Performance</h3>
<p>Speed is key. You will want to know how fast the CDN servers will be delivering the files to your end users.
The KPIs to look at are: latency (in ms) and throughput (in kbit/s).
For each KPI you should get the min, max, median and average for a long-enough period and preferably during normal and/or peak traffic hours.</p>
Some tips:
<ul>
<li>Do a trial or proof of concept with one or more Content Delivery Networks in the geographical regions that are most important to you
</li><li>Have solid performance monitoring in place during this test phase so you have real data about the CDN performance
</li><li>Interpret the data in the right way and compare the results of the different CDNs (this can be harder than you think)
</li></ul>
<h3>Costs</h3>
<p>Money is always important. Prices differ between CDNs and it makes sense to gather pricing information so you get some feel for the differences (tip: we present pricing information on our pages about <a href="https://www.cdnplanet.com/cdns/">Content Delivery Networks</a>).<br>
Remember: custom price plans are very common in the CDN market so don't let initial pricing info guide you too much in the decision making process.
</p>
<h3>Service/support</h3>
<p>
Many CDN providers offer 24/7/365 support via email and phone and that is great. Besides customer support availabilty, there are other important things to consider:
<ul>
<li>Friendliness and knowledge level of customer support reps
</li><li>How fast do problems get solved?
</li><li>Account management quality and availability
</li><li>Do they really work <strong>with</strong> you as a partner, or are you 'just another customer'?
</li></ul>
</p><h2>Approach to selecting a Content Delivery Network</h2>
<p>Here are a few tips for going through the selection process smoothly and effectively:
<ul>
<li>Think about your needs and requirements and write these down, grouped into the 4 categories
</li><li>Give each need/requirement a weight (using the <a href="https://en.wikipedia.org/wiki/MoSCoW_Method" class="external-link">MoSCoW method</a>)
</li><li>Gather information relevant to your situation, needs and requirements and put this in a spreadsheet
</li><li>Make the spreadsheet tell you which 3 CDNs seem to be a good match
</li><li>Get in touch with these CDNs; prepare for the conference call and ask your questions
</li><li>Do a free trial or proof of concept with 1 or 2 CDNs and make sure you are able to get real insight from that
</li><li>Negotiate a good deal ;-)
</li></ul>
</p><h2>How CDN Planet can help</h2>
<p>
We want to help you select the right CDN in the best way we can.
This site launched on October 3, 2011 and provides information on some of the Content Delivery Networks, with more CDNs coming soon.
These 'CDN pages' provide a nice, basic way to compare one CDN to the other, but will likely leave you undecided.
</p>
<p>
If you want someone to guide you through the CDN selection process, you may hire us as consultants via info [at] cdnplanet [dot] com.
Of course you can also contact the CDN(s) you find interesting directly. They will happily provide more info.
</p>CloudFront, nginx and gzip: not that simple2011-10-03T02:00:00+02:00https://www.cdnplanet.com/blog/gzip-nginx-cloudfront/CloudFront's request to origin servers are marked as HTTP/1.0. By default nginx does not send gzipped response to a HTTP/1.0 request. gzip_http_version setting should be set to 1.0 for it to work correctly.
<div class="notice-msg info">
This article is outdated. Read about <a href="https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/ServingCompressedFiles.html">Serving Compressed Files</a> in the official AWS CloudFront docs.
</div>
<p>
<a href="https://www.cdnplanet.com/cdns/cloudfront/">Amazon CloudFront</a>'s request to origin servers are marked as HTTP/1.0. By default nginx does not send a gzipped response to a HTTP/1.0 request. To tackle this, give <a href="https://wiki.nginx.org/HttpGzipModule#gzip_http_version" class="external-link">gzip_http_version</a> a value of 1.0 and it will work correctly.
</p>
<p>Enabling gzip compression is key for having a high performance website and is part of <a href="https://code.google.com/speed/page-speed/docs/payload.html#GzipCompression" class="external-link">Google's Web Performance Best Practices</a>. It can reduce the size of HTML, CSS, JavaScript and other textual files by up to 80%, resulting in lower round trip times and improved overall page load times.</p>
<p><a href="https://wiki.nginx.org/" class="external-link">nginx</a> is a highly popular and efficient event driven webserver for serving static files and/or serving as a <code>frontend</code> or <code>spoonfeeder</code> to its relatively bloated counterparts like Apache.</p>
<h2>How does gzip work?</h2>
<p>When browsers make a request to a server, they send a <code>Accept-Encoding</code> header. For IE8 this is <code>Accept-Encoding: gzip, deflate</code>. The server then knows that this browser accepts data compressed using gzip or deflate.</p>
<p>A HTTP request from IE8 may look like this:</p>
<pre>
GET / HTTP/1.1
Accept: image/gif, image/jpeg, image/pjpeg, image/pjpeg, application/x-ms-application, application/x-ms-xbap, application/vnd.ms-xpsdocument, application/xaml+xml, */*
Accept-Language: en-us
User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.2; Trident/4.0; .NET CLR 1.1.4322; .NET CLR 2.0.50727; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729)
Accept-Encoding: gzip, deflate
Host: www.example.com
Connection: Keep-Alive
</pre>
<p>Now, the server sees <code>Accept-Encoding: gzip, deflate</code>, sends the response compressed as gzip and marks it with the response header <code>Content-Encoding: gzip</code>. The server can also optionally send another header <code>Vary: Accept-Encoding</code>. This tells proxies to <code>vary</code> the object in the proxy cache based on the <code>Accept-Encoding</code> header. The result is that the proxy will have a compressed and uncompressed version f the file in cache (and maybe even three: uncompressed, gzip compressed, deflate compressed). Failing to provide the <code>Vary</code> header may result in the wrong encoding going to an incompatible browser. The <code>Vary</code> header was <a href="https://www8.org/w8-papers/5c-protocols/key/key.html#SECTION00034000000000000000" class="external-link">introduced in HTTP/1.1</a>.</p>
<blockquote>A cache finds a cache entry by using a key value in a lookup algorithm. The simplistic caching model in HTTP/1.0 uses just the requested URL as the cache key. However, the content negotiation mechanism (described in Section 10) breaks this model, because the response may vary not only based on the URL, but also based on one or more request-headers (such as Accept-Language and Accept-Charset).</blockquote>
<h2>Requests from CloudFront</h2>
<p>Requests made from <a href="https://www.cdnplanet.com/cdns/cloudfront/">CloudFront</a> to nginx are tagged as HTTP/1.0. To verify this, check access logs for requests made by the user-agent <code>Amazon CloudFront</code>. These will be flagged as HTTP/1.0.</p>
<p>Trying to be helpful, nginx assumes that the requesting user-agent will not honor the <code>Vary</code> header and does not send it a gzipped response.</p>
<p>I have met people who tell me "CloudFront does not support gzip". This is not true. It just takes more tinkering compared to other <a href="https://www.cdnplanet.com/cdns/">Content Delivery Networks</a>, but it works just the same.</p>
<h2>Getting gzip to work on nginx with CloudFront</h2>
<p>The fix is simple: set the <a href="https://wiki.nginx.org/HttpGzipModule#gzip_http_version" class="external-link"><code>gzip_http_version</code></a> setting to 1.0 (the default is 1.1). This would enable nginx to serve gzipped content to HTTP/1.0 clients as well, ensuring everyone who asks for gzipped content gets gzipped content. If you just applied this fix, you need to <a href="https://docs.amazonwebservices.com/AmazonCloudFront/latest/DeveloperGuide/Invalidation.html" class="external-link">invalidate</a> these files in your CloudFront distribution so that it fetches a fresh gzipped file from your server.</p>
<p>If you are using nginx as origin server for CloudFront, it is highly recommended that you test your site with tools like <a href="https://www.webpagetest.org/" class="external-link">Webpagetest.org</a>, <a href="https://getfirebug.com/" class="external-link">Firebug</a> or developer tools in Chrome/Safari, to get the confirmation that the files are now being sent compressed and everthing works fine.</p>