Optimize your WordPress website

WordPress is already a great piece of software and if you use these great WordPress plugins, is your system is almost ready for production. After your WordPress website is finished, you should do some optimizations on the HTML source code and you have to manipulate the basic behavior of a few features. The following list contains those tweaks I pass to most WordPress websites I build from scratch.

WordPress websiteMost of the functions below belong to your theme’s functions.php file. Just add them below the other modifications you have done before. If your functions.php file is already too crowded, than create a new file for this functions and in include this file using one of the PHP include functions.

Be careful with the default WordPress widgets

In my previous article about dynamic sidebars and widgets I mentioned already that many widgets are less flexible and sometimes a widget is not the function you need. If you use a default widget check the code carefully, for the example the “category widget” shows the complete category description for every list item inside the title attribute. If you can’t find a great alternative use this custom widget for your site (place this code into your functions.php file)

<?php 
/* Category Widget
 * A widget to show your blog's category names and links.
 */
 
add_action( 'widgets_init', 'mycategory_load_widgets' );
 
function mycategory_load_widgets() {
	register_widget( 'MyCategory_Widget' );
}
 
class MyCategory_Widget extends WP_Widget {
 
	function __construct() {
		parent::WP_Widget( 'category-widget', 'MyCategory_Widget', array( 'description' => 'A widget to show your blog\'s category names and links.' ) );
	}
 
	function widget( $args, $instance ) {
		extract( $args );
		$title = apply_filters('widget_title', $instance['title'] );
		echo $before_widget;
		if ( $title ) echo $before_title . $title . $after_title;
		echo '<ul class="links">'.wp_list_categories('echo=0&title_li=&use_desc_for_title=0').'</ul>';
		echo $after_widget;
	}
 
	function update( $new_instance, $old_instance ) {
		$instance = $old_instance;
		$instance['title'] = strip_tags( $new_instance['title'] );
		return $instance;
	}
 
	function form( $instance ) {
		$defaults = array( 'title' => 'Categories' );
		$instance = wp_parse_args( (array)$instance, $defaults );
		echo '
		<label for="'.$this->get_field_id( 'title' ).'">Title:</label> 
		<input id="'.$this->get_field_id( 'title' ).'" style="width: 100%;" type="text" name="'.$this->get_field_name( 'title' ).'" value="'.$instance['title'].'" /> '; 
	} 
}

Harden your WordPress website

There are many articles about “how to secure WordPress” available on the internet. The following modifications might be different from others you have seen before.

The following filter is more a kind of fix for the following situation: If you submit a search with an empty string the result is a list of the latest posts instead of the search result page and/or some error message. If you use a static page for the homepage, the URL from the latest article is used for the canonical URL. The last one is bad for your SEO because of creating duplicated content. Use this filter/function in your functions.php.

add_filter( 'request', 'my_request_filter' );
function my_request_filter( $query_vars ) {
    if( isset( $_GET['s'] ) && trim($_GET['s']) == '' ) {
        $query_vars['s'] = ' ';
    }
    return $query_vars;
}

To make it complete, edit also your search.php and add this “if” statement to report an error if the search string is empty:

if (get_search_query() == ' ') echo 'Error, your search query is empty!';

The following solution is not only WordPress related, but I mention this fix because it’s very easy to change the HTTP headers in WordPress.

  1. Remove the X-Pingback header to keep some spam bots outside
  2. Mask the current installed PHP version and tell the world that your server isn’t running a possible insecure version
function remove_change_myheaders($headers) {
    unset($headers['X-Pingback']);
    $headers['X-Powered-By'] = 'PHP/5';
    return $headers;
}
add_filter('wp_headers', 'remove_change_myheaders');

The next two action removals are used to hide the current WordPress version and to remove the short link which is generated by WordPress. The first one is important because the wp_generator tag will tell the outside world if you forgot to update your WordPress website. The short-link removal is maybe not important, but why should you show this alternative link if this link is not used by any visitors?

remove_action('wp_head', 'wp_generator');
remove_action( 'wp_head', 'wp_shortlink_wp_head', 10, 0 );

Code cleanup

WordPress adds by default a version query string to the CSS and JavaScript files (if you load them with the wp_head action). This might be somehow useful for a JavaScript library to prevent caching, but how often do you update them? The following code is used to remove them dynamically.

function remove_cssjs_ver( $src ) {
    if( strpos( $src, '?ver=' ) )
        $src = remove_query_arg( 'ver', $src );
    return $src;
}
add_filter( 'style_loader_src', 'remove_cssjs_ver', 10, 2 );
add_filter( 'script_loader_src', 'remove_cssjs_ver', 10, 2 );

It depends on what kind of plugins you’re using how many external scripts or CSS style sheets are loaded with every page even if the plugin or function isn’t used for that section or page. To keep your site fast it’s good to remove them where possible. Use the following example to remove unwanted CSS style sheets.

function my_deregister_styles() {
	if (!is_single()) wp_deregister_style( 'wp-syntax-css' );
	wp_deregister_style( 'codeguard-stylesheet' );
}
add_action( 'wp_print_styles', 'my_deregister_styles', 100 );

The same for JavaScript files, think about where you need to load your own scripts and remove 3rd party scripts if possible. In the example below I use different conditions to include or exclude the different scripts.

function load_my_scripts() {
	global $post;
	wp_deregister_script( 'codeguard-script' ); 
 	if (!is_single()) {
		wp_deregister_script( 'wp-postratings' );
	}
	if ($post->comment_status != 'open' && $post->comment_count == 0) {
		wp_deregister_script( 'comment-rating' );
	}
	if (has_term('templates', 'page-type') || is_category('blog') || is_page('domainname')) {
		wp_enqueue_script('thickbox');
		wp_enqueue_style('thickbox');
	}
}
add_action('wp_enqueue_scripts', 'load_my_scripts');

That’s so far for this series of WordPress tutorials. I hope you like the information and if there are details which are too complicated or if you need some explanation, don’t hesitate to ask. Please use the comment form below. Subscribe or follow me on twitter to get updates from new posts about WordPress and web development.

Comments

  1. Hello Olaf, great article! I am building my local Real Estate blog and I’m trying to stay away from the duplicate content issue. I’ve had good success for highly competitive keywords using external (power?) blogging platforms to achieve #1 page and posiiton rankings on google. So writing the articles for placement on external blogging platforms is not my problem – I’m at least comfortable with that. My goal is to mirror the success I’ve had with other platforms on my own blog. Is Yoast SEO something I should get involved with? or do I need a competent WP programmer to skim over my blog to see what potential (unintended) pitfalls may prevent my articles from ranking?

  2. Hello John,
    Where is the difference between the real estate blog and your “own” blog? If you write unique content on a 3rd website which has links to your target site, there is no chance for duplicated content.
    I have my question marks if these links help, because if you link the same site very often it might look as link schema to Google.
    Is there something I’ve missed out?

  3. Is WordPress the best platform for creating a blog for someone not tech savvy?

  4. Hi Kevin,

    it depends on, if you don’t need to change a lot on your site it’s pretty easy.
    Try the interface while using a free blog on wordpress.com.

Because of all the spam attemps I've decided to close the comment form at this time. If you have have any questions or comments please post them by using Google+ or Twitter (the links to my profiles are located at the top of this page).