[Story] How I made an angularJS SPA SEO friendly
Introduction
By reading the title, you may have asked yourself:
Sacrebleu! Are you still using angularJS? um.. 🤔
This article is going against the flow by sharing to you outdated content through dying Framework #AngularJS. Some years ago, I used a lot of AngularJs and now I want to share the development of my first web project and solution I discovered when making an angularJS application SEO friendly.
AngularJS was created to make your website better by giving a better user experience. You wanted to make your app faster and you thought SPA was going to be awesome. You were right, but not totally...
Table Of Contents
Some context
Back in time, I was a junior developer discovering web programming and I wanted to help a pom-pom girls organization managed by friends of mine. My goals were to:
- Create a (stunning) website from scratch
- Promote them on the internet
- Learn, learn, learn and learn
I heard about 3 emerging client-side frameworks to create SPA (Single page application): ember, Backbone.js, and AngularJS. As a beginner, I did not pay attention to SPA as I told myself "A framework created by Google should be SEO friendly
" 😑 I chose AngularJS as front end, PHP as back end API with a MySQL database and handmade CSS design 👨🏻🎨. After some months of reading documentations, testing, designing, and programming during my spare time, the website was done: a fancy home page with a presentation, a team page, a blog page with articles and a contact page. An admin panel was available for the Pom Pom manager to edit the public pages (it's working like a CMS). After some months, I published the website. After following an SEO basis tutorial and creating a robots.txt and sitemap.xml, the website has been referenced on Google. 🎉
I was so proud to discover my first website on the google search list... but only one over 4 URL were referenced. At this moment begun a new adventure: How to make an angularJS SPA website optimized for search engine?
The problems
Single-page applications are dynamically rewriting the current page rather than loading entire new pages from a server that makes the app faster and instantaneous for the user. However, when the crawlers are reading the template of your page, it sees:
<h1>{{websiteName}}</h1>
instead of:
<h1>Title of my website</h1>
Also, the head HTML tag was not filled with the right metadata on different pages. It was impossible to index the pages correctly,
resulting in bad organic search.
Before 2009, Google used to index only pure HTML without including dynamic Javascript content. The websites may receive an SEO penalty because Google considered the javascript content render as "Cloaking" and it was against the Webmaster Guidelines. However, since 2015, Google announced the ability to understand dynamic pages:
Today, as long as you’re not blocking Googlebot from crawling your JavaScript or CSS files, we are generally able to render and understand your web pages like modern browsers.
The other players like Bing were not able to render javascript. To be clear, you still needed to make your main content available through the server response to guarantee that search engines index it.
Solutions available
First, enabled html5mode API on your angular app but this requires server-side configuration to rewrite the URL (on the .htaccess in our case). It was not enough to make an SEO friendly website. Below is a list of available solutions at that time:
- Rewrite the codebase to the latest version of Angular (2) because it supports server-side rendering with the Universal module. I already passed to much time on this project.
- One alternative to avoid duplication was to parse the site with a browser executing javascript like PhantomJS to pre-render and save the result into a server and finally serve it to the crawlers. That's mean every change, I had to pre-render the page and push it to the production server? Mhh... it was not really exciting 🤔
- Use a pre-rendering platform or service to crawl the website, execute the javascript, create a cache from it and send the latter to the web crawlers. Sound like a good idea but it's not free.
My SSR solution
First, enabling angularJS html5mode API is useful to remove the hashbang and get a regular URL.
Second, Here's my trick:
Turn the main angularJS HTML template into PHP file.
I was able to:
- Edit the PHP template and fill the metadata tags according to the URL requested (title, description, etc...).
- Add some structured data used by google crawlers to display rich results.
- Being SEO friendly through server-side rendering, here is an example of the index.php
<?php
$path = $_SERVER['REQUEST_URI'];
$chunks = explode('/', $path);
$page = "";
$title = "";
$description = "";
$img = 'logo.jpg';
$alt = "";
?>
Then depending on the path, I was setting the metadata:
if (in_array("contact", $chunks) == true) {
$title = "Contact";
$description = "Contact us to have more informations !";
$img = "/contact.jpg";
$alt = "contact picture";
} else if (in_array("team", $chunks) == true) {
$title = "Team";
$description = "Some description...";
$img = "/team.jpg";
$alt = "Some alt...";
}
// More else if
Next, I inserted the PHP variables into the HTML:
<head>
<meta charset="utf-8" />
<title>
<?php echo $title; ?> - Cheery Dolls
</title>
<base href="/">
<meta name="description" content="<?php echo $description; ?>" />
<meta property="og:url" content="https://cheerydolls.fr<?php echo $path; ?>" />
<meta property="og:title" content="<?php echo $title; ?>" />
<meta property="og:description" content="<?php echo $desc; ?>" />
<!-- etc... -->
<head>
Every time a crawler was requesting a page, the server responded by the corresponding metadata that was making the page unique and indexable on the search page.
Also, I was able to print structured data on the index.php, for example, a blog breadcrumb:
<?php
function display_breadcrumb ($article_title) {
$front = '<!-- breadcrumb -->
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "BreadcrumbList",
"itemListElement": [{
"@type": "ListItem",
"position": 1,
"name": "Blog",
"item": "https://cheerydolls.fr/blog"
}';
if (isset($article_title) && !empty($article_title)) {
$front .= ',{
"@type": "ListItem",
"position": 2,
"name": "Article",
"item": "https://cheerydolls.fr/blog/' . $article_title . '"
}';
}
$front .= ']}</script>';
return $front;
}
echo display_breadcrumb($article_name);
?>
It was not the best solution (also no the prettiest) to appear on the search page but it was doing the job. 🤷♂️ It leads to a hell of maintenance when I had to make important changes to the content, I had to change the rendering content every time...
Conclusion
Build a website from scratch on your own is highly instructive in many aspects: server-side, API, client-side, SEO, security, design and more. I will advise you to do the same 😉 I spent so much energy on this project and I did think to give up sometimes because I was struggling so much in the beginning. It was also a project I was doing during my spare time and I was not paid for. By telling you this story, it makes me step back and revive the experience, choices I made and I learned a lot from it.
Nowadays a lot of wonderful frameworks are making our life easier like Nuxt, Next, Hugo and more! AngularJS entered in long term support period and I advised you to migrate to the latest version of Angular or other frameworks.
I migrated the original Pom Pom girl's website to a modern stack: Nuxt + Vuetify + Express API. Check this out: cheerydolls.fr.
If you want to learn more about Search Engine Optimization, I read recently a complete and interesting cheat sheet on dev.to.
Leave a like/comment to support my article or follow me to be notified of my next articles. 🔥
Thanks for reading!
References in 2016
https://geoffkenyon.com/angularjs-seo-crawling/
https://stackoverflow.com/questions/41957366/angularjs-seo-once-and-for-all
https://www.verticalmeasures.com/blog/search-optimization/overcoming-angular-seo-issues-associated-with-angularjs-framework/
https://www.yearofmoo.com/2012/11/angularjs-and-seo.html