FireBrand FireBrand - 1 year ago 116
PHP Question

Prerendering angular pages to serve crawlers with Codeigniter

I've made an angular app with Codeigniter on the server side using this angular-codeigniter-seed.
I'm trying to add SEO to my app, and i'm using ui-router-metatags to have dynamic meta-tags on every page but the problem is that only Chrome's crawler runs javascript and waits for the DOM to load when it indexes pages, all the others(facebook, tweeter etc) don't, and they take the html as is.

There are a lot of paid services that pre-render the angular pages and cache them for when the crawler comes (seo4ajax, brombone, etc), but i can't pay and i don't need the volumes that they offer.

So i'm thinking in the direction of rendering some pages(don't have to be all of them) on the server and serving them as ready HTMLs, how should i approach this?.

I'm running Angular 1.5.3, Codeigniter 3.0.4, ui-router 0.2.18 and the server runs Nginx.

here,s a sample from my routing:

.state('song', {
url: "/song/:songTitle",
templateUrl: root + 'templ/audio',
controller: 'audioController as audio',
resolve: {
songTitle: function ($stateParams) {
return $stateParams.songTitle;
songDescription: function (SongDescService) {
return SongDescService.getDescription();
metaTags: {
title: function (songTitle) {
return songTitle;
description: function(songDescription){
return songDescription;
properties: {
'og:title': function (songTitle) {
return songTitle;
'og:type': 'audio',
'og:description': function(songDescription){
return songDescription;
'og:image': root + 'images/logo.png'


I've endded up injecting the meta tags in my Home.php controller like so:

public function index()
    //get the page name to insert proper meta tags
    $pageName = ($this->uri->segment(1) == null ? 'homepage' : $this->uri->segment(1));
    $metaTags = metatags(); // helper function gets the metatags defined in config.php

    $data = $metaTags[$pageName];

    if ($pageName == 'song') {
        $songName = $this->uri->segment(3);
        $songObj = getsong($songName);
        $data['title'] = $data['properties']['og:title'] = $data['properties']['twitter:title'] = $songObj->title;
        $data['description'] = $data['properties']['og:description'] = $data['properties']['twitter:description'] = $songObj->description;

        $data['properties']['og:image'] = $data['properties']['twitter:image'] = base_url() . 'images/song_image.png';//will better be dynamic

    } else {
        $data['properties']['og:image'] = base_url() . $data['properties']['og:image'];
        $data['properties']['twitter:image'] = base_url() . $data['properties']['twitter:image'];

    //add prefixes
    $data['title'] .= ' | myApp';
    $data['properties']['og:title'] .= ' | myApp';
    $data['properties']['twitter:title'] .= ' | myApp';

    $this->load->view('layout/index', $data);

config.php exmaple:

$config['metatags'] = array(
'homepage' => array(
    'title' => 'Home',
    'properties' => array(
        'description' => 'blah blah blah',
        'og:title' => 'Home',
        'og:description' => 'blah blah blah',
        'og:type' => 'website',
        'og:image' => 'images/social_image.png',
        'twitter:title' => 'Home',
        'twitter:description' => 'blah blah blah',
        'twitter:image' => 'images/social_image.png',

And on the layout page(index.php) i added to the head the following:

<title><?= $title ?></title>
<meta name="description" content="<?= $properties['description'] ?>">

<meta property="og:title" content="<?= $properties['og:title'] ?>">
<meta property="og:description" content="<?= $properties['og:description'] ?>">
<meta property="og:type" content="<?= $properties['og:type'] ?>">
<meta property="og:image" content="<?= $properties['og:image'] ?>">

<meta property="twitter:title" content="<?= $properties['twitter:title'] ?>">
<meta property="twitter:description" content="<?= $properties['twitter:description'] ?>">
<meta property="twitter:image" content="<?= $properties['twitter:image'] ?>">

That's what worked for me, hope it will help someone in the future.