Back to blog

Follow and Subscribe

Using ESI, Part 1: Simple Edge-Side Include

Simon Wistow

VP Strategic Initiatives, Fastly

Note: ESI is now available to all customers, contacting support for enablement is no longer necessary. For documentation and variables, visit the Developer Hub. If you have questions about cost considerations, please contact support.

Edge-Side Include (ESI) is a web standard originally proposed by Akamai and Oracle, among other companies. It allows an Edge Server (like Fastly’s caches) to "mix and match" content from multiple URLs.

Fastly customers can use ESI to cache pages that contain both cacheable and uncacheable content (such as user-specific information). Previously, the presence of any uncacheable content on a page would have rendered the whole page uncacheable, even if that portion was a small percentage of the overall content. The more pages with uncacheable content, the more Fastly has to request the page from the customer’s origin instead of serving that content from the cache.

With ESI, the uncacheable portion can be split off into a separate request, meaning that the vast majority of the page can be cached. This increases the ratio of cached requests to uncached requests (what we call the "cache-hit ratio"). A lower cache-hit ratio means slower pages, increased costs, and less survivability against traffic spikes.

Case Study: Ecommerce Website

On a typical ecommerce site, the home page is completely cacheable. From the logo and thumbnails to the HTML of the page itself, the content is the same for all users who land on the home page.

However, once a user logs in and starts the buying process, the home page can no longer be cached because the number of items in the user’s shopping cart will not be same for all visitors.

At this point, the home page has to be regenerated for every logged-in user, even though the majority of the page’s objects are the same across users. This increases origin load and slows down page load times, both of which negatively impact websites.

How ESI Helps

In lieu of the company’s web application including relevant code for the shopping cart in a page, it can replace that section with an ESI tag that includes the code instead.

The HTML would look something like:

<html>
  <head>
      <title>My Ecommerce Site</title>
  </head>
  <body>
    <div id="content">
      <div id="header">
        <img src="/images/logo.jpg" />
        **<esi:include src="/shopping_cart" />**
      </div>
      <div id="items">
        <span id="item_1">...</span>
        <span id="item_2">...</span>
        ...
        <span id="item_n">...</span>
      </div>
    </div>
  </body>
</html>

Browsers will never see the ESI tag (the <esi:include src="/shopping_cart" /> part) because when it passes through something that understands ESI (such as Fastly, or other web servers with special plugins installed) that tag will be replaced by the contents of the URL /shopping_cart.

Note: for performance reasons, Fastly doesn’t check every request for ESI — contact support@fastly.com if you want to find out how to enable it for a request.

Once the ESI tag has been added, the ecommerce company needs either some logic in VCL or their Web App to check whether or not the user is logged in or not. If they are, the web app returns the HTML for the shopping cart; if not, it returns an empty string or the HTML for an empty shopping cart depending on what they want the experience to be.

Because the ESI tag splits off the personalized content (the part with the shopping cart) into a separate request, the homepage is now the same for everyone, meaning it’s completely cacheable and does not need to be regenerated for every user. In addition the HTML for the shopping cart can potentially be cached on a per-user basis by Fastly meaning even the personalized content doesn’t need to be regenerated every time - only when the shopping cart changes.

AJAX as an Alternative

Observant readers will note that the same thing could be achieved using AJAX. For example, instead of the ESI tag, the customer could use an empty span element and some Javascript that fetches the shopping cart HTML and replaces the contents of the span instead:

<html>
  <head>
    <title>My Ecommerce Site</title>
    **<script src="/jquery.js" />
    <script>
      $(document).ready(function() {
          $.get("/shopping_cart", function(data){
            $("#shopping_cart").html(data);
          });
      });
    </script>**
  </head>
  <body>
    <div id="content">
      <div id="header">
        <img src="/images/logo.jpg" />
        **<span id="shopping cart" />**
      </div>
      <div id="items">
        <span id="item_1">...</span>
        <span id="item_2">...</span>
        ...
        <span id="item_n">...</span>
      </div>
    </div>
  </body>
</html>

However, this only works if you’re trying to insert something into HTML where Javascript is available. But when Javascript is not available, ESI comes to the rescue.

Using ESI with JSON

Say you want a widget to fetch some JSON on your page. That JSON is the same for every user, but you also want to include the user’s geographical location. Everything in the JSON response is cacheable, except for the user’s city information:

{
  "amp_volume": 11,
  "other_variable": "Reticulating Splines",
  "geo_data": {
    "city": "New York"
  }
}

With ESI, that becomes:

{
  "amp_volume": 11,
  "message": "Reticulating Splines",
  "geo_data": {
    **<esi:include src="/geo_information" />**
  }
}

This looks a little strange, because it’s mixing both JSON and XML, but it works absolutely fine.

You may have a URL in your application that returns geo-information. In this case, Fastly gives you the ability to create custom responses at the edge (called "synthetics" in Varnish terminology). Because we expose the GeoIP information of the user in VCL, you can have something like this:

sub vcl_recv {
  if (req.url == "/geo_information") {
    error 900;
  }
}

sub vcl_error {
  if (obj.status == 900) {
    set obj.status = 200;
    set obj.response = "OK";
    synthetic {"{"city": ""} geoip.city {"""} {"}
"};
    return(deliver);
  }
}

Now you have fully cached JSON, which also includes personalized, dynamic information that can be served from the edge instead of your origin, making that widget load much faster.

Looking Ahead

In Part 2 of this series, I’ll discuss a few more complicated tricks you can do with ESI, such as creating JSONP call backs on the fly.