Get Ambassador amrketing delivered right to your inbox!


    The Truth About SEO For Single Page Web Apps

    11 minute read

    This post is a response to a recent discussion on Normally, the information I get from is top-notch, however on this particular topic, I found many myths perpetuated and I wanted to address them head on. If you take only one thing away from this post, let it be this; Google wouldn't produce and support a single page javascript framework (angular.js) if Google couldn't index it. Furthermore, Google builds many of its projects internally using the framework, which it wouldn't do if it couldn't easily be indexed.

    Before we get into some of the nitty gritty, let me start by discussing what I consider to be a true "Single Page Web App" and what a single page web app is not.

    On the discussion, I saw three types of websites referred to as a single page web app.

    • IS: A Single Page Web App - Built on any number of popular javascript frameworks like Backbone.js, ember.js, or our favorite angular.js
    • IS NOT: A single page template - Like a theme you might purchase on Theme Forest, or on
    • IS NOT: An infinite scrolling site - A page that scrolls forever (infinitely, duh), like what you see on Infinite scrolling pages use ajax to load more content when the user gets near the bottom of the page.

    I'll spend a little time on the last two, so that I can help provide some value to the community, but I don't consider them pure examples of a single-page web applications and will focus my time on the first example.


    IS NOT: A Single Page Template

    Single page templates are becoming very popular lately, and for good reason. You have 8 seconds to capture a users attention, and so you need to provide as much information to the user as concisely as possible.

    That being said, just because a website is composed of a single page, does not mean that it is what I would consider a web application. While a single page theme could certainly be layered on top of a CMS, or extended with a tool like X-editable it ships is as a static theme file. If the content is rendered as static html or server-side most of the "single-page web app" optimization concerns are moot.


    IS NOT: An infinitely scrolling site

    If your site paginates any of its content, you can convert it into an infinitely scrolling site. Personally, my favorite tool for the job is Infinite Scroll by Paul Irish. Before becoming familiar with the angular way, I loved David Desandro's suite of plugins, which integrated with Paul's jQuery plugin.

    Infinite scroll works by using a jQuery ajax call to reach out to the next page and append the items we need to the end of the current page.

    This behavior doesn't actually manipulate any of your archive pages, so you can treat your site in exactly the same way you would with any multi-page archived site.

    New Call-to-action 

    A True Single-Page Web App

    Single page web applications, and their corresponding frameworks, have become increasingly popular in the past couple of years. These frameworks (sometimes called javascript MVC frameworks) easily consume RESTful APIs and distribute the processing workload from the server onto the connecting client computers.

    Here are a few of the other benefits of using a javascript framework to produce a single-page web application:

    • As previously mentioned, a front-end framework distributes the processing work across multiple client computers.
    • A front-end framework is extensible. Provided that you build your API in a RESTful way (which most frameworks encourage you to do), you can easily swap out server-side technologies with little front end changes. You could easily prototype your application using a technology like Firebase or Parse, and then migrate to a hosted solution when cost becomes prohibitive.
    • A front-end framework can easily be turned into a mobile application. Technologies like Phonegap and Cordova make it remarkably easy to transform HTML, CSS, and Javascript into a hybrid native application.

    Since single-page web apps use Javascript to display and render content, a major concerns for SEOs is if they're bad for SEO. Like all things SEO, the answer is nuanced. Consider these questions;

    1. Is static HTML bad for SEO? No.
    2. Is a poorly configured static HTML site bad for SEO? Absolutely.
    3. Is WordPress bad for SEO? No, but it's really easy to configure your WordPress install to be bad for SEO.

    Javascript frameworks -- the underlying technology that powers single-page web apps -- are neither good or bad for SEO. Like a wielded hammer, whether it is a tool or a weapon is determined by the hand that holds it.

    If you don't believe me, look no further than the framework the team at Ambassador has chosen, angular.js. The company sponsoring the open-source framework? Google. It seems counter-intuitive to me that Google would produce and release a javascript framework that would be inherently bad for SEO. That doesn't mean that time and effort shouldn't be spent optimizing your single-page web application to make sure it is crawled and indexed appropriately. I just went through this process with our technical team earlier this year and the transition was actually really easy. Let's take a look at the primary "gotchas."


    Optimizing Single Page Apps for SEO: Pretty URLs

    I'd encourage you to take a few minutes to head over to the primary Ambassador site, and navigate around a little bit, paying close attention to the url address bar. I'll wait.

    Looks like a regular site right? No ugly /#/whatever clearly indicating that it's in fact a single page app? I don't want to get too technical, but that's because we're taking advantage of the HTML5 History API, powered by the angular.js $locationProvider module.

    Read next: Pretty URLs in AngularJS: Removing the #

    When you define your application and configure your routes file, just add a simple $locationProvider.html5Mode(true); and you're good to go. Angular will handle everything for you, and fall back gracefully if the browser doesn't support HTML5.

    <span class="kd">var</span> <span class="nx">app</span> <span class="o">=</span> <span class="nx">angular</span><span class="p">.</span><span class="nx">module</span><span class="p">(</span><span class="s1">'myApp'</span><span class="p">);</span>
    <span class="nx">app</span><span class="p">.</span><span class="nx">config</span><span class="p">([</span><span class="s1">'$routeProvider'</span><span class="p">,</span> <span class="s1">'$locationProvider'</span><span class="p">,</span> <span class="kd">function</span><span class="p">(</span><span class="nx">$routeProvider</span><span class="p">,</span> <span class="nx">$locationProvider</span><span class="p">){</span>
        <span class="nx">$routeProvider</span><span class="p">.</span><span class="nx">when</span><span class="p">(</span><span class="s1">'/view1'</span><span class="p">,</span> <span class="p">{</span><span class="nx">templateUrl</span><span class="o">:</span> <span class="s1">'partials/partial1.html'</span><span class="p">,</span> <span class="nx">controller</span><span class="o">:</span> <span class="s1">'MyCtrl1'</span><span class="p">});</span>
        <span class="nx">$routeProvider</span><span class="p">.</span><span class="nx">when</span><span class="p">(</span><span class="s1">'/view2'</span><span class="p">,</span> <span class="p">{</span><span class="nx">templateUrl</span><span class="o">:</span> <span class="s1">'partials/partial2.html'</span><span class="p">,</span> <span class="nx">controller</span><span class="o">:</span> <span class="s1">'MyCtrl2'</span><span class="p">});</span>
        <span class="c1">// use the HTML5 History API</span>
        <span class="nx">$locationProvider</span><span class="p">.</span><span class="nx">html5Mode</span><span class="p">(</span><span class="kc">true</span><span class="p">);</span>
    <span class="p">});</span>

    What was previously:
    is now:

    But let's step back for a second, and think about what difference does this really make to the robots? Functionally the application is behaving the same, only the application is less recognizable as a single page web app to the laymen. The content continues to be rendered via javascript. While the url looks normal to us, the underlying problems with encouraging the robots to read our content remain unchanged. Fundamentally, this problem isn't really all that new.

    The most familiar sibling of the single page web-app's #'s is the common ?, which SEOs have already had the privilege of dealing with. Currently Google (or any other search engine) will consider to be different than Whether that content is being rendered server-side or via javascript makes no difference to the robot consuming the URL, because it is consuming the entire url, especially if each url is defined and encouraged to be indexed in our sitemap.


    Optimizing Single Page Apps for SEO: Cache Rules Everything Around Me

    The problem arises when Google (or any other search engine) attempts to consume the content on the page, and instead of getting <h1>Turning Customers Into Brand Ambassadors</h1> gets something like <h1></h1>. You better believe the latter is bad for SEO, and made much worse if every url in the /resources/ "subdirectory" uses the same html template (which creates duplicate content issues).

    Fortunately the fix is not only easy, but it will help increase performance and speed up page load times for your visitors as well. And faster page load times = better SEO. Just like content that is rendered server-side, you'll want to make sure to cache your content, especially if it isn't content that changes frequently. At Ambassador we'll add a new resource from time-to-time, or occasionally fix a typo, but for the most part the content remains the same. It wouldn't make sense for our users to have to render the page from scratch every time they visit.

    Read next: Google AJAX crawling specification

    For this we use, which is actually optimized for use with single-page frameworks like angular.js, backbone, and ember and strictly follows Google's AJAX crawling specification. Configuration is easy. We're rocking node.js on the backend so installation was a simple:

    <span class="err">$</span> <span class="n">npm</span> <span class="n">install</span> <span class="n">prerender</span><span class="o">-</span><span class="n">node</span> <span class="o">--</span><span class="n">save</span>

    and then in your app.js or server.js file, or wherever you configure express:

    <span class="nx">app</span><span class="p">.</span><span class="nx">use</span><span class="p">(</span><span class="nx">require</span><span class="p">(</span><span class="s1">'prerender-node'</span><span class="p">).</span><span class="nx">set</span><span class="p">(</span><span class="s1">'prerenderToken'</span><span class="p">,</span> <span class="nx">process</span><span class="p">.</span><span class="nx">env</span><span class="p">.</span><span class="nx">PRERENDER_TOKEN</span><span class="p">));</span>

    Don't worry if you aren't using the same setup as us, prerender provides ample documentation regardless of your configuration.

    Once you have completed your initial configuration, it is primarily a matter of choosing a management strategy for your pages and query parameters, and then either including or excluding parameters depending on your strategy. For us, we decided on a subtractive management. So prerender will treat each query parameter as a separate page on our site, unless we explicitly tell it to ignore a specific parameter. Immediately, we know that we'll be passing some utm_ query parameters for Google Analytics that don't affect the page content, so we tell prerender to ignore those.


    Prerender is already configured to handle a majority of the user-agents that will crawl your site, especially the search engines. From time-to-time though, prerender will encounter an unfamiliar user-agent, and won't be able to handle it for you. Don't let this create unneeded anxiety for you, you have enough.

    Before supported Buffer's user agent.

    Prerender is an open source platform, You can even host the prerender service yourself if you feel so obliged. That means that if you are in a rush and have the technical resources to get it done, you can always make a pull request and be the change you wish to see in the world. Otherwise the prerender team is dedicated to making sure your content is crawled correctly, so just create an issue and they should get to it relatively quickly.


    Optimizing Single Page Apps for SEO: sitemap.xml & GMT

    Once you have configured, your single page web app is 90% configured for SEO. What's left is to make sure that you are including all the pages that you want indexed in your sitemap.xml file, (possibly generating sitemap.xml dynamically), and making the appropriate adjustments in Google WMT. Nothing that you wouldn't have to do under any circumstances.

    I would certainly run a crawl in WMT and your favorite SEO analytics platform right away, and check for any crawl errors. Prerender is already friends with Rogerbot, so you should be fine working with Moz. Once that is done, the technical on-site work is handled, so you can return to optimizing your site as you normally would.

     Do you want to learn how to turn more of your customers, partners, affiliates, and fans into revenue generators? Request a custom demo from one of our referral marketing software experts today!


    Peering into the Crystal Ball eBook 

    Posted in Inbound Marketing

    Fadi George

    Written by Fadi George

    Fadi George is the website director at Ambassador, with a special interest in SEO, digital marketing and growth hacking. He's a lover of all things NZ and tech.

    Subscribe To Our Blog

    Stay informed about the latest referral marketing trends, tips, and insights.