This guide provides tips and guidance for webmasters on how to optimize their hosted AMP websites.

Isn't AMP fast by default?

The AMP runtime is optimized for speed and if your AMP pages are served by an AMP cache, they are fully optimized and offer the highest loading performance. For example, if your users are coming to your AMP pages from Google Search on mobile, by default the pages are served by an AMP cache.

However, AMP pages are not always served from an AMP cache. A website may decide to show AMP pages from their own servers for other traffic sources. The most frequent use case are sites built completely in AMP, such as tasty.co, where users go straight to the site. Another traffic source is Twitter, which started linking to AMP pages instead of delivering the standard mobile version. This means that if a user clicks a link in one of Twitter's mobile apps, the link goes to the AMP version of your page on your own origin (if one is available).

As a consequence, you can't always be sure that your AMP pages are only served from an AMP cache. For these cases, where you are serving AMP pages from your own servers, it is important to make sure that your AMP pages offer the optimal loading performance.

AMP pages load fast by default, but there are some additional performance optimizations you can implement to help the browser load AMP pages even faster. This guide describes a few optimizations you should consider when publishing AMP pages. However, before you start reading this guide, make sure that you've already covered all the basic web performance best practices. In particular, image optimization has a big impact on loading performance.

For example, by applying the following optimization techniques:

the "The Scenic" AMPStart template loads two seconds faster on a 3G connection.

If you want to skip the details, check out the AMP Boilerplate generator, which you can use to generate custom optimized AMP pages.

Optimize the AMP Runtime loading

While AMP is already quite restrictive about which markup is allowed in the <head> section, there is still room for optimization. The key is to structure the <head> section in a way so that all render-blocking scripts and custom fonts load as fast as possible.

Here is the recommended order for the <head> section in an AMP page:

<!doctype html>
<html  lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,minimum-scale=1">
    <meta name="description" content="This is the AMP Boilerplate.">
    <link rel="preload" as="script" href="https://cdn.ampproject.org/v0.js">
    <link rel="preload" as="script" href="https://cdn.ampproject.org/v0/amp-experiment-0.1.js">
    <link rel="preconnect dns-prefetch" href="https://fonts.gstatic.com/" crossorigin>
    <script async src="https://cdn.ampproject.org/v0.js"></script>
    <script async custom-element="amp-experiment" src="https://cdn.ampproject.org/v0/amp-experiment-0.1.js"></script>
    <!-- Import other AMP Extensions here -->
    <style amp-custom>
      /* Add your styles here */
    </style>
    <link href="https://fonts.googleapis.com/css?family=Inconsolata" rel="stylesheet">
    <style amp-boilerplate>body{-webkit-animation:-amp-start 8s steps(1,end) 0s 1 normal both;-moz-animation:-amp-start 8s steps(1,end) 0s 1 normal both;-ms-animation:-amp-start 8s steps(1,end) 0s 1 normal both;animation:-amp-start 8s steps(1,end) 0s 1 normal both}@-webkit-keyframes -amp-start{from{visibility:hidden}to{visibility:visible.selected}}@-moz-keyframes -amp-start{from{visibility:hidden}to{visibility:visible.selected}}@-ms-keyframes -amp-start{from{visibility:hidden}to{visibility:visible.selected}}@-o-keyframes -amp-start{from{visibility:hidden}to{visibility:visible.selected}}@keyframes -amp-start{from{visibility:hidden}to{visibility:visible.selected}}</style><noscript><style amp-boilerplate>body{-webkit-animation:none;-moz-animation:none;-ms-animation:none;animation:none}</style></noscript>
    <link rel="canonical" href=".">
    <title>My AMP Page</title>
  </head>
  <body>
    <h1>Hello World</h1>
  </body>
</html>

Let's go through it step-by-step:

  1. The first tag should be the meta charset tag, followed by any remaining meta tags.
  2. Next, preload the AMP runtime v0.js <script> tag with <link as=script href=https://cdn.ampproject.org/v0.js rel=preload>. The AMP runtime should start downloading as soon as possible because the AMP boilerplate hides the document via body { visibility:hidden } until the AMP runtime has loaded. Preloading the AMP runtime tells the browser to download the script with a higher priority. Take a look at server-side-rendering to learn how to avoid this.

  3. If your page includes render-delaying extensions (e.g., amp-experiment, amp-dynamic-css-classes, amp-story), preload those extensions as they're required by the AMP runtime for rendering the page.

    <link as="script" rel="preload" href="https://cdn.ampproject.org/v0/amp-custom-css-0.1.js">
    <link as="script" rel="preload" href="https://cdn.ampproject.org/v0/amp-experiment-0.1.js">
    <link as="script" rel="preload" href="https://cdn.ampproject.org/v0/story-1.0.js">
    

  4. Use preconnect to speedup the connection to other origin where the full resource URL is not known ahead of time, for example, when using Google Fonts:

    <link rel="preconnect dns-prefetch" href="https://fonts.gstatic.com/" crossorigin>
    

  5. Load the AMP runtime:

    <script async src="https://cdn.ampproject.org/v0.js"></script>
    

  6. Specify the <script> tags for render-delaying extensions (e.g., amp-experiment, amp-dynamic-css-classes, and amp-story)

  7. Specify the <script> tags for remaining extensions (e.g., amp-bind, ...). These extensions are not render-delaying and therefore should not be preloaded as they might take away important bandwidth for the initial render.
  8. Specify any custom styles by using the <style amp-custom> tag.
  9. Add any other tags allowed in the <head> section. In particular, any external fonts should go last since they block rendering.
  10. Finally, specify the AMP boilerplate code. By putting the boilerplate code last, it prevents custom styles from accidentally overriding the boilerplate css rules.

Preload hero images

AMP HTML uses its own image element: amp-img. While amp-img has many advantages over the traditional HTML img tag, one disadvantage is that the AMP runtime must be loaded before the image download can start. For some images, such as hero images for a product page, it's critical that the images load as quickly as possible. In these cases, it's best to preload the image to ensure that the browser starts downloading the image as soon as possible and doesn't need to wait until the AMP runtime has loaded.

<head>
  <link rel="preload" href="/images/elephants.png" as="image">
</head>
<body>
  ...
  <amp-img width="404" height="720" layout="responsive"
           src="/images/elephants.png" alt="..." >
  </amp-img>
</body>

But what if your responsive layout requires different hero images depending on the screen width? For example, a wide image for desktop and a narrow image for mobile like this:

<amp-img width="404" height="720"
    alt="..." layout="responsive"
    src="/images/elephants_narrow.png"
    media="(max-width: 415px)">
</amp-img>
<amp-img height="720"
    alt="..." layout="fixed-height"
    src="/images/elephants_wide.jpg"
    media="(min-width: 416px)">
 </amp-img>

The good thing is that link rel=preload also supports media queries. So we can use the same media queries in our preload statements, like this:

<link rel="preload" as="image"
    href="/images/elephants_narrow.png"
    media="(max-width: 415px)">
<link rel="preload" as="image"
    href="/images/elephants_wide.jpg"
    media="(min-width: 416px)">

By the way, the same approach works for amp-video poster images:

<link rel="preload" href="/images/poster.jpg" as="image">
...
 <amp-video width="480" height="270" src="elephant.mp4"
             poster="/images/poster.jpg"
             layout="responsive">
     ...
</amp-video>

Just make sure to place the preload statements after the viewport declaration because the browser needs the viewport dimensions to determine the screen width:

<meta name="viewport" content="width=device-width,minimum-scale=1,initial-scale=1">
...
<link rel="preload" media="(max-width: 415px)" ...>

Consider using a service worker

Now that all major browsers support service workers, it's a good idea to evaluate whether it makes sense to add a service worker to your site.

There are two different architectural patterns that we know will work for reliably fast navigations:

  • For single-page applications: the App Shell model (in the AMP context referred to as AMP-in-PWA). This pattern requires a service worker to upgrade an AMP document to the app-shell-based PWA experience.
  • For multi-page-applications: streaming composite resources. A service worker caches the static header and footer and uses streaming to instantly return a cached, partial response while loading the content.

If neither of these patterns is used and it's not possible to cache the whole site (which only is reasonable for very small sites), a service worker might have a negative performance impact. The best thing in this case is to not use a service worker.

However, if you want your website to be installable from the home screen, or want to offer an offline experience, you'll have to use a service worker. In this case, it's important to use navigation preload to mitigate the potential slowdown (Note: Currently, navigation preload is only supported in Chrome).

If your AMP website uses a service worker, here are some best practices:

If you're looking for a way to get started with a service worker in your AMP site, check out this sample that provides a service worker that implements all these best practices.

Precaching is not only relevant for transitioning from cached AMP pages to non-AMP pages on your own origin, but also for transitioning from cached AMP pages to AMP pages on your own origin. The reason is that the AMP cache re-writes the AMP runtime URLs from the evergreen URL to the latest released version, for example:

https://cdn.ampproject.org/v0.js -> https://cdn.ampproject.org/rtv/001515617716922/v0.js.

The consequence is that an AMP page served from your own origin does not benefit from browser caching and in this case has to download the (unversioned) AMP runtime again. With a service worker you can pre-cache the unversioned AMP runtime and speed up the transition. To learn more about why the AMP cache versions AMP runtime URLs, read this document.

Optimize custom fonts

With AMP there are a few things that you can do to optimize your font loading (most of them are actually not specific to AMP):

  • If possible, use amp-font with timeout set to 0 (this will only use the font if it's already in the cache). Fall back to the system font if your custom font has not been loaded yet. This is a similar behavior to font-display: optional.
  • Optimize your web fonts (for example, serve custom fonts using WOFF2).
  • Preload custom fonts:

    <link rel="preload" as="font" href="/bundles/app/fonts/helveticaneue-roman-webfont.woff2" >
    

  • If you are using Google fonts, or any other font provider with unknown font URLs, preconnect the respective font server:

     <link rel="preconnect dns-prefetch" href="https://fonts.gstatic.com/" crossorigin>
    

Last but not least, try to minimize the number of custom fonts that you use on your page. If you can, use the system fonts instead of custom fonts because system fonts make your website match the user's operating system, and it helps to avoid loading more resources.

Server-Side Rendering

Server-side-rendering is a technique that AMP caches use to even further speed up loading time. With server-side-rendering it's possible to remove the AMP boilerplate so that the AMP document can be painted without running the AMP runtime JavaScript. For example, the server-side rendered version of the AMP Boilerplate Generator renders twice as fast as the normal AMP version!

If you're interested in using server-side-rendering, check out the AMP Optimizer tool. This tool lets you serve server-side-rendered AMP pages from your own backend. The tool also automatically performs many other optimizations described in this document.

Basic optimizations

Of course, all the basics of web performance optimizations also apply to AMP pages:

Additional Resources