How to optimize your WordPress site - Advanced steps for Developers


There are so many articles online titled “How to Optimize your WordPress site”. This post, however, offers more specific, advanced steps and in-depth solutions that a developer can do to optimize a theme.

There are so many developers out there struggling with their WordPress theme or are just using the default structure of a WordPress theme that is very slow and un-optimized.

There are many ways a developer can optimize this WordPress theme and below i am going to list what i do on my themes. I am going to start by listing a few generic things and then move to more advanced topics.


Your hosting matters. Not everyone can afford a dedicated or cloud hosting, but at least get a hosting package that is optimized for WordPress. You will need to have PHP7 or latest version for better speed and if you can find a hosting that provides LiteSpeed technology, choose that one.

The server response time plays a big role in the speed of a website. So does the data center location. For example, if your main audience is in Europe choose a data center located in Europe. Otherwise your website speed can decrease dramatically.

It is always better to use a hosting package that provides CPanel, so that you can benefit from its possibilities and the settings you can make, eg enabling mod_deflate via Software -> Optimize website.


The more plugins you activate, the slower the site. Simple as that. Use the very minimal list of plugins in order to keep your site optimized, for example Yoast SEO and Wp Smush it.

If you are trying to add some functionality on your site, try to find if it is possible to do it by adding some code in your functions.php file, located in your theme. Do not use plugins for each and every functionality you need for your website. Of course, if you cannot avoid adding a plugin, just use it.


The wp-config.php file is located at the root of your WordPress installation.

At the very top, add the following lines:

define( 'WP_POST_REVISIONS', 2 );  //specify how many post revisions to keep. Old revisions are automatically deleted

define('WP_HOME', '');   //add your website URL
define('WP_SITEURL', '');

In your code, if you want to get the website URL, you can just use the WP_HOME constant. By adding the above 2 lines you will avoid unnecessary database queries.


The functions.php file is located at the root folder of your theme.

Before we start, i would like to recommend to “break” your functions.php file into multiple files for better and easier maintenance by you. It will also be easier for you to locate what you are looking for.
For example you can create a folder called functions to place the mentioned files in it. Here is an example of the files i use on my projects:

  • setup.php : Here you can add code that has to do with the functionality you want, eg registering menus, and the optimizations mentioned below
  • custom_post_types.php : Here you can add code that creates custom post types that you may use in your site
  • php_functions.php : Generic PHP functions that you may use. For me, this file does not use any WordPress code. Just PHP functions that i use, for example a function that prints the zise of a file or a function that formats a given date.
  • … you can include as many files as needed …
  • project_functions.php : Specific functions that are only used in this theme, eg using pre_get_posts filter

Then you can include the above files in your main functions.php file, like so:

$templatedir = get_template_directory();
require $templatedir . '/functions/setup.php';

So, back to our optimizations:

4.1 Remove file versioning

By default, WordPress uses a versioning (?ver=1.12.4) when it includes some JavaScript files, such as jQuery, for example jquery.js?ver=1.12.4
It is advised to remove such versioning. You can do so, by adding the following code in your functions.php file or functions/setup.php file if you followed the “split files” technique as mentioned above:

function _remove_script_version( $src ){
    $parts = explode( '?ver', $src );
    return $parts[0];
add_filter( 'script_loader_src', '_remove_script_version', 15, 1 );
add_filter( 'style_loader_src', '_remove_script_version', 15, 1 );

4.2 Remove unneeded stuff from the header

By default, WordPress adds a bunch of tags in your html <header> and in most cases those are really not needed, specially the emojis.
It is best to remove everything that you do not need. So you can add the code below inside functions.php file (or functions/setup.php file if you followed the “split files” technique):

remove_action('wp_head', 'rsd_link');
remove_action('wp_head', 'wp_generator');
remove_action('wp_head', 'feed_links', 2);
remove_action('wp_head', 'index_rel_link');
remove_action('wp_head', 'wlwmanifest_link');
remove_action('wp_head', 'feed_links_extra', 3);
remove_action('wp_head', 'start_post_rel_link', 10);
remove_action('wp_head', 'parent_post_rel_link', 10);
remove_action('wp_head', 'adjacent_posts_rel_link', 10);

remove_action('wp_head', 'print_emoji_detection_script', 7);
remove_action('wp_print_styles', 'print_emoji_styles');
remove_action( 'admin_print_scripts', 'print_emoji_detection_script' );
remove_action( 'admin_print_styles', 'print_emoji_styles' );

4.3 De-register Heartbeat

WordPress heartbeat is an API service. As the name indicates, the heartbeat API sends continuous pulse to perform periodic tasks. This helps the browser to communicate with the server using ajax calls, providing real-time information on the WordPress dashboard. The problem is that the heartbeat API sends continuous ajax requests to the server and thus, it can send a large number of requests to the hosting server, resulting in high CPU consumption.
We can limit this API service, by allowing it to run only when creating or editing a post, so that we keep the auto-save functionality.
So in our functions.php file (or functions/setup.php file if you followed the “split files” technique) we can add the following code:

add_action( 'init', 'my_deregister_heartbeat', 1 );
function my_deregister_heartbeat() {
    global $pagenow;

    if ( 'post.php' != $pagenow && 'post-new.php' != $pagenow )

4.4 Remove JS & CSS files added by plugins

Some plugins register and use JS & CSS files on your site. In most cases these are not needed. Or maybe only the JS file is needed and instead, you can write your own CSS code that can be minified inside your main CSS file. In either case, remove the extra files that are not needed. This usually depends on how the plugin is written, so it is best to Google how to do it for the specific plugin you are using.

For example, the Contact Form 7 plugin gives you the ability to add the following lines in our wp-config.php file:

define('WPCF7_LOAD_CSS', false); //remove CSS file.
define('WPCF7_LOAD_JS', false); //remove JS file.

It is best to add the js file only when it is needed (when you have a form on a page). You can do so, by calling the wpcf7_enqueue_scripts(); function when needed.

Also, if you are using a JavaScript plugin only in a specific page, then you should only load that file in that page, always by calling the wp_enqueue_script.

4.5 Remove any unneeded post formats and widgets

If your theme uses post-formats or widgets that you do not use, you can just remove them. You can find them inside your functions.php file and just comment them out.

Every single thing or piece of code that is not needed should be removed or commented out. Just be careful what to remove when you don’t know or don’t understand a specific code block.

Also, never ever edit the WordPress core files. There is an extended list of hooks that you can use to either alter or add a functionality.

4.6 Load your JavaScript files in the footer

A very common practice in order to avoid the blocking of the HTML parser and optimize your site is to include all JavaScript files in the footer. By default, WordPress loads all JavaScript files in the <head> of the HTML. To avoid this we can add the following code in our functions.php file:

//Move your JS files in the footer:
remove_action('wp_head', 'wp_print_scripts');
remove_action('wp_head', 'wp_print_head_scripts', 9);
remove_action('wp_head', 'wp_enqueue_scripts', 1);
add_action('wp_footer', 'wp_print_scripts', 5);
add_action('wp_footer', 'wp_enqueue_scripts', 5);
add_action('wp_footer', 'wp_print_head_scripts', 5);

//Create a function to register the js files needed for your theme:
if (!is_admin()){
    add_action("wp_enqueue_scripts", "my_scripts", 999);

function my_scripts(){
    //load the js files like so:
    wp_enqueue_script('TweenMax', $template_url . '/js/TweenMax.min.js',array( 'jquery' ), null, true);

4.7 Defer the loading of your JavaScript files

Even if  your scripts are loaded in the footer of your site, you will need to make them load asynchronously.

JavaScript is considered a “parser blocking resource”. This means that the parsing of the HTML document itself is blocked by JavaScript. When the parser reaches a <script> tag it stops to fetch and run it. So the solution to this is to either use DEFER or ASYNC for your JavaScript files. The Async technique cannot guarantee the order in which the JavaScript files will execute. On the other hand, Defer will fetch the files asynchronously but they will run in the order they are being loaded (as you specify in your code).

So, again in your functions.php file you can add the following code:

if (!is_admin()){
	function add_defer_attribute($tag, $handle) {
		return str_replace(' src', ' defer src', $tag);	
	add_filter('script_loader_tag', 'add_defer_attribute', 10, 2);

For more info, there is an excellent post on defer vs async.


Now we are going to mention some obvious things for some developers, yet not so obvious for others.

5.1 Limit Web fonts

Designers love to use many fonts. There are so many fonts out there and they want to use them all!
And you, as a developer, should teach them to limit the number of web fonts to 1 or 2. You can never accept more than 3.
Also if you HAVE to use a webfont, subset it and only use the characters & languages that you will actually need. This way the font file gets smaller in size.

In a few words, web fonts are files that will have to be fetched and then render the text. So having many fonts means a slower site.

Here is an interesting article on web fonts loading for further reading.

5.2 Write proper code

Yes, you DO have to write proper code. Yes, you do have to remove unused code. Yes, you do have to write reusable code.

I am referring to PHP, JavaScript & CSS. All of your code needs to be the best possible.
Find online tutorials and get better at it. See what big companies are doing, like for example the AirBnb JavaScript style guide.

For your CSS, it will be easier for you to write in SASS, but even if you write in plain CSS, start writing using the BEM methodology. For best results use BEM along with OOCSS (Object Oriented CSS) or SMACSS. It will make your life easier, your CSS code smaller and reusable.


It might seem a lot, but it will only take you a day to read the methodologies mentioned above and when you start using them you will feel like you were kept in the dark all this time.

5.3 Stop using Bootstrap

You don’t really need Bootstrap. You can make your own grid so easily, without using that clutter of unneeded CSS & JavaScript. Seriously. I have written more about this topic here.

5.4 General Tips

  • Use a caching plugin. LiteSpeed Cache (if your server supports LiteSpeed technology) and WP Super Cache are great for most projects.
  • Always serve minified versions of your CSS & JS on the production server. Gulp is a great way of doing such tasks. You may find a Gulp guide here.
    If you want to keep a version of them, you should try changing the name and not adding things like ?ver=1.0.1 at the end.
    For example name your CSS: styles/main.v1.0.1.css
  • If you are going to use the same PHP function multiple times in the same page, it is best to call it once and keep it in a variable, eg
    $template_url = get_template_directory_uri();
  • When in development mode, always use:
    error_reporting(E_ALL); ini_set('display_errors', 1);

    This way you can locate all errors and fix them before you go online.
  • Always optimize your images. For the images that you have in your theme you can use Gulp to optimize them.
    For the images uploaded through the WordPress admin you can use the WP Smush plugin.
  • In WordPress admin, go to Settings -> Discussion and turn-off Trackbacks & Pingbacks.


As a developer it is very common to make your own queries using the wp_query class.

For example, you may want to get the 3 latest posts and display them on a page of your site.

In this case the query would be:

$unoptimized_query = new WP_Query(array(
    'post_type' => 'post',
    'post_status' => 'publish',
    'posts_per_page' => 3,
    'orderby' => array('date' => 'DESC'),

This query may be correct, however it actually runs 4 different queries in the database to get your results and it takes about 0.0286 seconds to run on a localhost environment.

You can actually optimize wp_query so that it makes less queries in the database and of course to make it run faster:

$optimized_query = new WP_Query(array(
    'post_type' => 'post',
    'post_status' => 'publish',
    'posts_per_page' => 3,
    'orderby' => array('date' => 'DESC'),
    'update_post_meta_cache' => false, //tells not to run query for post meta
    'update_post_term_cache' => false,  //tells not to run query for terms, remove if terms required
    'ignore_sticky_posts' => true,  //ignores sticky posts  
    'no_found_rows' => true,  //tells not to count posts - remove if pagination required

The optimized query has the same options as above, we just added a few lines of code.

The optimized query runs 2 different queries in the database and it takes about 0.0016 seconds to run on the same environment. That is a big difference.

In case you only need to make a wp_query to get the total results, then you can only query for the IDs, by using the return fields parameter:

$optimized_query_with_ids = new WP_Query(array(
    'fields' => 'ids',
    'post_type' => 'post',
    'post_status' => 'publish',
    'posts_per_page' => 3,
    'orderby' => array('date' => 'DESC'),
    'update_post_meta_cache' => false, //tells not to query post meta
    'update_post_term_cache' => false,  //tells not to query terms, remove if terms required
    'ignore_sticky_posts' => true,
    'no_found_rows' => true,  //tells not to count posts - remove if pagination required

So, as you can see, even the way you buid your queries will make a difference in the speed.


Assuming you have followed all steps mentioned in this article and published your site online, there are a few more steps.

7.1 Harness the power of .htaccess

There are a few tweaks that you can make through .htaccess that will affect your optimize your site greatly.
You can enable GZip, you can cache CSS, JavaScript and images in the user’s browser and many more things.
I find the .htaccess that comes with the HTML boilerplate is a great place to start. Get in from there and check all optimizations proposed.

7.2 Check for updates

Regularly check for new versions of WordPress and activated plugins. Keep them all updated for both security issues and also optimizations that might be added.

7.3 Last step

Check your site with GTMetrix. It is a great free tool that will generate a whole report for you, including hints on how to make it faster.

The higher the score, the better! You may also create a free account that will let you choose the location where the test will be made from. Try the nearest location to your target audience to get a more relevant report.

Check the hints of the report on what to fix. There is an explanation for every hint.

If you can create a Service Worker, then do it! This gives a great boost to your site, specially if you are caching pages.

I really hope you found all these steps for optimizing your WordPress theme helpful. If you liked it, share it!