WordPress SEO-friendly optimisation with your own plugin

wordpress seo-friendly optimisation with your own plugin

If you run Google PageSpeed Insights or GTMetrix tests on standard WordPress installation, you will noticed that your website could be more SEO friendly with some optimisation. You can do so by creating your own customise plugin and put all the code for optimisation and customisation in one place.

There are many SEO improvement plugins available, and some of them do a very good job. You might be able to use one of those plugins to achieve the goal. But often you would still need to do some customisation on your own, or find another plugin to do the job.  I'm not a big friend of install too many plugins and prefer to have one plugin to handle everything. In this article, we are focus on create our very own customise plugin, with the emphasis on improving the WordPress SEO scores and performance.

Create your own plugin

It easier to crate your own plugin than you thought. All you need to start is to create a PHP file in the plugin folder. In this example, I am using the WordPress twenty sixteen theme that came by default.

One of the advantages of using a customisation plugin to customise your WordPress site is that all the customisation would be available no matter which theme is using, unless the changes you made are theme-specific.

First, create a PHP file using text editor of your choice, I would use Linux command line and nano editor throughout this article to create the file directly in the plugin directory which is at /var/www/html/wp-content/plugins/, you can name your plugin whatever name you want:

sudo nano my-customise-plugin.php

Create the following comment lines and save the file:

<?php 
/* Plugin Name: Simple Customisation Plugin 
   Plugin URI: https://www.e-tinkers.com 
   Description: This plugin allows adding customisation codes without using child theme 
   Author: Henry Cheung 
   Version: 1.0 
*/ 

The Plugin Name is a string that describe your plugin. The Description provides a brief one-sentence description of what the plugin is about. Author, Version and The Plugin URI could be your personal or website information or just leave it blank since we are not publishing the plugin for third-party to download it.

Save the file and login into the wp-admin dashboard, click on Plugins on the left of the dashboard panel, and you should see the plugin that we just created shown up along with the list of other plugins.

Create your own customisation plugin
Create your own customisation plugin

Click on Activate to activate the plugin. Right now the plugin doesn’t do anything, we will need to add code into it.

Add Google Analytics javascript to WordPress

Google Analytics is a freemium web analytics service offered by Google that tracks and reports website traffic, it is is the most widely used web analytics service on the Internet. Google Analytics is implemented using Google Analytics Tracking Code, which is a snippet of JavaScript code that the website owner adds to every page of the website. We’d want to add Google Analytics Tracking Code snippet into our WordPress pages and posts. Instead of create a customised page or post template, we will create a php function to add the javascript snippets when the page or post is dynamically generated.

Obtaining Google Analytics Tracking Code
Obtaining Google Analytics Tracking Code

How to sign up to Google Analytics and obtaining the Google Analytics Tracking Code is quite straightforward and will not be discussed here. The Tracking Info javascript can be obtained from the Google Analytics dashboard, once signup, click on Admin tab on the Dashboard, and click on Tracking Info, and click on Tracking Code, select and copy the entire Javascript snippet to the clipboard. Open my-customise-plugin.php plugin that we just created either using your editor or you could do it on WordPress Dashboard by click on Edit under the plugin panel, append the following code into the php file.

/* Add google analytics js code to the footer */
function my_google_analytics() { ?>
<script>
  (function(i,s,o,g,r,a,m){
    i['GoogleAnalyticsObject']=r;
    i[r]=i[r]||function(){
     (i[r].q=i[r].q||[]).push(arguments)
    },i[r].l=1*new Date();
    a=s.createElement(o),m=s.getElementsByTagName(o)[0];
    a.async=1;
    a.src=g;
    m.parentNode.insertBefore(a,m)
  })(window,document,'script','https://www.google-analytics.com/analytics.js','ga');
  ga('create', 'UA-xxxxxx-1', 'auto');
  ga('send', 'pageview');
</script>
<?php } 
add_action( 'wp_footer', 'my_google_analytics', 10 ); 

The code between <script> tags is the code that you copy from Google Analytics Dashboard.

Remove unnecessary meta tags from WordPress header

WordPress automatically generates a whole lot of meta tags and links that could slow down your website and your site probably don’t need it.

Append the following snippets of code to the my-customise-plugin.php.

/* remove all unnecessary meta items */
  //No announce of using WordPress
  remove_action('wp_head', 'wp_generator');
  //No support on Window Live Writer	
  remove_action('wp_head', 'wlwmanifest_link');	
  //No Really Simple Discovery (RSD) support
  remove_action('wp_head', 'rsd_link');	
  //No shortlink is necessary
  remove_action('wp_head', 'wp_shortlink_wp_head');
  //remove adjacent post link 
  remove_action('wp_head', 'adjacent_posts_rel_link_wp_head', 10);
  //No announce of RSS generator
  add_filter('the_generator', '__return_false');
  // Remove all emoji supports on all pages
  remove_action( 'wp_head', 'print_emoji_detection_script', 7 );
  remove_action( 'admin_print_scripts', 'print_emoji_detection_script' );
  remove_action( 'wp_print_styles', 'print_emoji_styles' );
  remove_filter( 'wp_mail', 'wp_staticize_emoji_for_email' );
  remove_filter( 'the_content_feed', 'wp_staticize_emoji' );
  remove_filter( 'comment_text_rss', 'wp_staticize_emoji' );
  add_filter( 'emoji_svg_url', '__return_false' );

Move JQuery from page header to footer

If you run GTMetrix test on standard WordPress website, you are likely seeing that some of the WordPress codes are slowing down the page loading, and Javascript is one of those on the list. Javascript and JQuery are known as render blocking codes, it simply means that such files or Javascript prevents other information to be loaded on the users browser until the render blocking file is completely loaded. If there is a delay for the retrieval of the file, the remainder of the site will have to wait until it is completed.

To speed up the page loading, it is generally preferable to load the render blocking codes such as JQuery at near the end of a page than at the header of a page. To do that, we use a function call to de-register the call for the JavaScript to load in the header and re-register it to load in the <footer>. The WordPress also use a copy of JQuery from local server where you run your WordPress, it would be better to load the JQuery from some well-known CDN (Content Distribution Network) service such as Google, as the CDN servers often available globally and is well-cached by both the servers and user browsers. We therefore would want to re-register the JQuery with a URL linked to Google web services instead of linking to the local copy. The function and the call looks like this:

/* Remove jQuery from header and load jQuery via CDN to the footer */
function move_jquery_to_footer() {
   if (!is_admin()) {
      wp_deregister_script('jquery');
      wp_register_script('jquery', 'https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js', false, null, true);
      wp_enqueue_script('jquery');
   }
}
add_action('init', 'move_jquery_to_footer');

Generate and update sitemap.xml

Sitemaps are an easy way for webmasters to inform search engines about pages on their sites that are available for crawling. In its simplest form, a Sitemap is an XML file that lists URLs for a site along with additional metadata that allows web crawlers to find the pages on a website.

There are many plugins available for generating and maintaining sitemap, it however not difficult to create your own function to generate and update sitemap automatically each time your page or post is updated.

First, we will create an empty sitemap.xml file and make suer it has read/write access with the correct access ownership.

cd /var/www/html
sudo touch sitemap.xml
sudo chown www-data: sitemap.xml
sudo chmod 666 sitemap.xml

Add the following code to the my-customise-plugin.php.

/* Create sitemap.xml whenever a post/page is saved */
function create_sitemap() {
   if (str_replace('-','', get_option('gmt_offset')) < 10 ) {
      $tempo = '-0' . str_replace('-','', get_option('gmt_offset'));
   }
   else {
      $tempo = get_option('gmt_offset');
   } 
   if (strlen( $tempo ) == 3 ) {
      $tempo = $tempo . ':00';
   }
   $postsForSitemap = get_posts(array(
        'numberposts' => -1,
        'orderby'     => 'modified',
        'post_type'   => array( 'post', 'page' ),
        'order'       => 'DESC'
      ));
    $sitemap .= '' 
             .'';
    $sitemap .= "\n" . '' . "\n";
    $sitemap .= "\t" . '' . "\n" .
        "\t\t" . '' . esc_url(home_url( '/' )) . '' .
        "\n\t\t" . '' . date("Y-m-d\TH:i:s", current_time('timestamp', 0)) . $tempo . '' .
        "\n\t\t" . 'daily' .
        "\n\t\t" . '1.0' .
        "\n\t" . '' . "\n";
    foreach ($postsForSitemap as $post) {
        setup_postdata($post);
        $postdate = explode (" ", $post->post_modified);
        $sitemap .= "\t" . '' . "\n" .
            "\t\t" . '' . get_permalink ($post->ID) . '' .
            "\n\t\t" . '' . $postdate[0] . 'T' . $postdate[1] . $tempo . '' .
            "\n\t\t" . 'weekly' .
            "\n\t\t" . '0.5' .
            "\n\t" . '' . "\n";
    }
    $sitemap .= '';
    $fp = fopen (ABSPATH . "sitemap.xml", 'w');
    fwrite ($fp, $sitemap);
    fclose ($fp);
}
add_action("save_post", "create_sitemap");

We need to add our sitemap file into the robots.txt so that crawlers can get to sitemap.xml. Open the robots.txt using editor:

Sudo nano robots.txt

Add the following line to the end of the file as:

Sitemap: https://www.your-domain.com/sitemap.xml

Remember to replace your-domain.com with your actual url.

Avoid duplicate content

WordPress in general is SEO optimised, however when you are using it as a blog platform, that's where the problem raised due to WordPress's design. It tends to generate a lot of duplicate content. In general around 40% of the WordPress duplicate content are due to Tags, Categories, Author, Archive, Search and so on. I use is_archive()conditional tag to check if a page is an Archive page type. An Archive page is a Category, Tag, Author, Date, Custom Post Type or Custom Taxonomy based page. If it is an Archive page, a html meta will be inserted to the html header to signal the search robot which is trying to index the page to ignore the page.

/* Set noindex meta for archives pages */
function add_noindex_meta() {
    if ( is_archive() ) { ?>
	    <meta name="robots" content="noindex">
<?php
    }
}
add_action('wp_head', 'add_noindex_meta');

If your site is new, it probably has not been indexed by search engines yet, but if your site has been up and running for a while, search for your site name on Google with:

site:www.yourwebsite.com

You will be able to see what all links Google have indexed from your blog, and you can analyse what links Google should not index.

Remove Font Stylesheets from WordPress

CSS stylesheet is a render blocking code just like javascript, the rest of the page won’t get rendering until CSS files are loaded. And unlike Javascript which could be loaded toward the end of a page rendering, stylesheets need to be loaded upfront before the html is rendering to the screen. It is therefore important to limit the number of stylesheets to be loaded and reduce the size of the stylesheets as much as possible.

This section was theme-specific, and was based on WordPress default theme Twenty Sixteen, it may not relevant to you, but the idea of customise the theme using plugin and for optimising the performance is relevant no matter which theme you are using.

Genericons are a special vector font symbol. It is used to display magnify glass icon next to the search box, the menu dropdown indication, etc. PageSpeed suggesting that Genericons took quite some time to load, but I’m a little hesitate to remove Genericons stylesheet unless I could find a better solutions. However, WordPress twenty sixteen theme also loads three open source fonts:

	font-family: Merriweather, Georgia, serif;
	font-family: Inconsolata, monospace;
	font-family: Montserrat, "Helvetica Neue", sans-serif;

The Merriweather font is widely used for the body text. Montserrat font is used for navigation, page and widget title, and all input fields throughout the theme. Inconsolata is a monospace type of font and only used in the user comment area when user need to enter programming code by using <pre>…</pre> html tag. I’m perfectly fine to remove the usage of those fonts and use the fallback fonts such as Georgia, monospace, and Helvetica Neue fonts, this will speedup the page loading slightly as there is no need to load the css stylesheets for the three fonts.

/* Remove Merriweather, Inconsolata and Montserrat fonts */
function remove_special_fonts () {
   wp_dequeue_style ('twentysixteen-fonts');
   wp_deregister_style ('twentysixteen-fonts');
}
add_action ('wp_enqueue_scripts','remove_special_fonts',20);

Remove version query string on scripts and stylesheets

Most of the stylesheets and JavaScript files added by WordPress plugins and themes usually have version query string (?ver=) in their URL. Google PageSpeed Insights suggests to remove all query strings from static resources to enable proxy caching of the file.

/* remove version query string from scripts and stylesheets */
function remove_ver_query_string ($src) {
    return remove_query_arg('ver', $src);
}
add_filter ('script_loader_src', 'remove_ver_query_string');
add_filter ('style_loader_src', 'remove_ver_query_string');

Performance Checks

With the techniques described in this article, plus some server optimisation and various caches implementation described in another article, my WordPress server which is running on a Raspberry Pi 3 achieved amazing scores on both GTMetric test, and Google PageSpeed Insights scores!

GTMetrix test result
GTMetric test scores of WordPress on a Raspberry Pi
PageSpeed Desktop test shows 95/100 score
Google PageSpeed Desktop test shows 95/100 score
PageSpeed Mobile test result
Google PageSpeed Insights mobile test shows 84/100 scores

Summary

In general, you could improve the performance of WordPress site and add SEO tracking and tools such as Google Analytics and sitemap.xml with one single your very own customisation plugin.

You may also noticed that you probably could achieve the same functionalities and features described in this article by finding the plugins from WordPress Plugins site. But you would end up of installing many plugins, which would make your site bloat and potentially slow down your page rendering time.

In general I would like to minimise the number of plugins installed for both site performance and easy to manage reasons. Remember, less is more!

5 comments by readers

  1. I try to follow the guide, and up until now all went great. However this page seems to contain a few errors.
    – I get a “unexpected end of file” with the google analytics code. Not quite sure what’s wrong, maybe the ?>?
    – on the move jquery to footer bit there is a wrong quote char in the code: add_action('init', ‘move_jquery_to_footer'); Right before the word move_

    1. Thanks for pointing out the errors. The closing php tag ?> is not necessary, the error was caused by the wrong open php tag <!php instead of <?php. Good catch on the wrong quote char used, I noticed there are a couple of those since then.

  2. *** This comment has been edited to replace the actual domain name***

    Updating to wordpress 5.4 seems to break the plugin created in this tutorial. When trying to edit a page with this plugin enabled, I am getting "The editor has encountered an unexpected error. Attempt Recovery. Copy Post Text. Copy Error".
    The following is the output when clicking the “copy Error” button:

    TypeError: Cannot read property 'DotTip' of undefined
        at https://www.yourdomain.net/wp-includes/js/dist/edit-post.min.js:6:28507
        at je (https://www.yourdomain.net/wp-includes/js/dist/vendor/react-dom.min.js:78:476)
        at ph (https://www.yourdomain.net/wp-includes/js/dist/vendor/react-dom.min.js:215:173)
        at lh (https://www.yourdomain.net/wp-includes/js/dist/vendor/react-dom.min.js:126:409)
        at O (https://www.yourdomain.net/wp-includes/js/dist/vendor/react-dom.min.js:121:71)
        at ze (https://www.yourdomain.net/wp-includes/js/dist/vendor/react-dom.min.js:118:14)
        at https://www.yourdomain.net/wp-includes/js/dist/vendor/react-dom.min.js:53:49
        at unstable_runWithPriority (https:www.yourdomain.net/wp-includes/js/dist/vendor/react.min.js:26:340)
        at Ma (https://www.yourdomain.net/wp-includes/js/dist/vendor/react-dom.min.js:52:280)
        at mg (https://www.yourdomain.net/wp-includes/js/dist/vendor/react-dom.min.js:52:496)
    

    Please refer to my post in the wordpress forums https://wordpress.org/support/topic/upgrading-to-wp-5-4-the-editor-has-encountered-an-unexpected-error/ to see problem in full.

    Maybe some edits to update this tutorial to work with wordpress v 5.4 could happen? Thank you regardless.

    1. I’m running WordPress v5.4 and do not see any error you encounter. Your error seems to be related to a React component from third party vendors that shipped by WordPress, and I can’t see anything related to this particular plug-in. By the way, I’m using classic editor instead of the Gutenberg (which is basically a editor with React), but I enabled Gutenberg briefly and create and edit a post, and still couldn’t replicate the error.

      1. Interesting. I do not think I installed any react components unless it came in another plugin. But when I deactivate all plugins except for this one, i get the error. If i deactivate the custom plugin, the error goes away. I will try to rebuild the plugin from scratch and see if I still encounter the same errors. Maybe I made a mistake on my custom plugin? I will report back. Any other suggestions in debugging would be great!

Comments are closed.