Optimizing for PageSpeed

  • Cache
  • Defer Javascript
  • Minify and Concatenate
  • Optimize Images
  • Deliver Assets
  • Evaluate Performance

Optimization Strategy

  • Browser Cache
  • Page Cache
  • Object Cache
  • Reverse Proxy Cache

Cache

Client and server side caching enable efficient delivery and use of static resources.

Tells the browser to save static assets locally for re-use over a certain period of time.

 

  • Identify filetypes to cache
  • Determine optimum expiration or "max-age"
  • Use unique filenames for cache invalidation
  • Configure or unset ETags (entity tags)

Browser Cache

These tokenized HTTP headers determine if cached assets differ from the latest.

ETags

# Unset ETags in .htaccess
<IfModule mod_headers.c>   
Header unset ETag
</IfModule>
FileETag None
# Configure ETags in httpd.conf
<Directory /usr/local/httpd/htdocs>
    FileETag MTime Size
</Directory>
HTTP/1.1 200 OK
Last-Modified: Wed, 31 May 2017 03:03:59 GMT
ETag: "10c24bc-4ab-457e1c1f"
Content-Length: 12195

Added as directives to .htaccess, httpd.conf on Apache.

Expires & Cache Control

<IfModule mod_expires.c>
    # HTML
    ExpiresByType text/html                             "access plus 0 seconds"
    
    # CSS
    ExpiresByType text/css                              "access plus 1 year"
    
    # JavaScript
    ExpiresByType application/javascript                "access plus 1 year"
    ExpiresByType application/x-javascript              "access plus 1 year"
    ExpiresByType text/javascript                       "access plus 1 year"
</IfModule>
# Set Cache-Control to 1 month
<filesMatch ".(css|jpg|jpeg|png|gif|js|ico)$">
    Header set Cache-Control "max-age=2628000, public"
</filesMatch>
# Document, html, data, and cache.appcache
location ~* \.(?:manifest|appcache|html?|xml|json)$ {
  add_header Cache-Control "max-age=0";
}

# Media: images, icons, video, audio, HTC
location ~* \.(?:jpg|jpeg|gif|png|ico|cur|gz|svg|mp4|ogg|ogv|webm|htc)$ {
  access_log off;
  add_header Cache-Control "max-age=2592000";
}

# CSS and Javascript
location ~* \.(?:css|js)$ {
  add_header Cache-Control "max-age=31536000";
  access_log off;
}

Enable compression by filetype and tell browsers to deflate before loading. Capable of 70% reduction in size.

Compression

# Enable Gzip Compression
<IfModule mod_deflate.c>
AddOutputFilterByType DEFLATE text/plain
AddOutputFilterByType DEFLATE text/html
AddOutputFilterByType DEFLATE text/xml
AddOutputFilterByType DEFLATE text/css
AddOutputFilterByType DEFLATE application/xml
AddOutputFilterByType DEFLATE application/xhtml+xml
AddOutputFilterByType DEFLATE application/rss+xml
AddOutputFilterByType DEFLATE application/javascript
AddOutputFilterByType DEFLATE application/x-javascript
</IfModule>
# Enable Gzip compression with mod_deflate
LoadModule deflate_module modules/mod_deflate.so

Serves pre-compiled pages as static resources to increase speed and reduce server load.

 

  • Mod Rewrite vs. PHP
  • Cache Preloading
  • Garbage Collection

Page Cache

Serve static page cache files using mod_rewrite rules in .htaccess

Rewrites

<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
AddDefaultCharset UTF-8
RewriteCond %{REQUEST_URI} !^.*[^/]$
RewriteCond %{REQUEST_URI} !^.*//.*$
RewriteCond %{REQUEST_METHOD} !POST
RewriteCond %{QUERY_STRING} !.*=.*
RewriteCond %{HTTP:Cookie} !^.*(comment_author_|wordpress_logged_in|wp-postpass_).*$
RewriteCond %{HTTP:X-Wap-Profile} !^[a-z0-9\"]+ [NC]
RewriteCond %{HTTP:Profile} !^[a-z0-9\"]+ [NC]
RewriteCond %{HTTP:Accept-Encoding} gzip
RewriteCond %{HTTPS} on
RewriteCond %{DOCUMENT_ROOT}/wp-content/cache/supercache/%{SERVER_NAME}/$1/index-https.html.gz
RewriteRule ^(.*) "/wp-content/cache/supercache/%{SERVER_NAME}/$1/index-https.html.gz" [L]
</IfModule>

Stores database query results for fast access from memory. WP_Object_Cache class used internally for non-persistent cache.

Object Cache

Persistent Caching

  • Memcached
  • APC
  • Redis
  • WordPress Transients

Serves cached files from a server in front of your server. 

 

 

 

 

 

  • Nginx and Varnish
  • Squid and Apache Traffic Server
  • Helps address concurrency issues at scale
  • Load balancing for heavy traffic

Reverse Proxy Cache

What's the best cache

for my site? 

Browsers stop to render Javascript during page load when they encounter scripts in the HTML.

 

  • Enqueue in the Footer
  • Defer jQuery
  • Try-Catch Wrapping
  • Lazy Load 3rd Party Javascript

Defer Javascript

Enqueue in the Footer

// WordPress Codex Reference
wp_enqueue_script( 
    string $handle, 
    string $src = '', 
    array $deps = array(), 
    string|bool|null $ver = false, 
    bool $in_footer = false // We want this to be true
)
// A Typical Javascript Enqueue
wp_enqueue_script(
    'bx-slider',
    get_stylesheet_directory_uri() . '/js/jquery.bxslider.min.js',
    ['jquery'],
    null,
    true // Loads in footer
)

Defer jQuery

// Deferring Inline jQuery
(function() {
  function load_jquery_deferred() {
    if(typeof window.jQuery == "function") {
      clearInterval(jquery_deferred);
      (function($) {

        // Deferred Code Here

      })( jQuery );
    }
  }
  var jquery_deferred = setInterval(load_jquery_deferred, 100);
})();
// Defer jQuery to Footer
add_action('wp_enqueue_scripts', 'defer_jquery');
function defer_jquery() {
  if ( !current_user_can('edit_pages') ) {
    wp_deregister_script('jquery');
    wp_register_script('jquery', includes_url('/js/jquery/jquery.js'), false, NULL, true );
    wp_enqueue_script('jquery');
  }
}

Try-Catch Wrapping

// Try-Catch Wrapper
var tcWrapper = function(f) {
    return function() {
        try {
            f.apply(this, arguments);
        } catch(e) {
            customErrorHandler(e)
        }
    }
}

var NS = { f: function(...) { ... } }
// Add try-catch?
if($this->trycatch) {
    $script = 'try{'.$script.'}catch(e){}';
}
$tmpscript = apply_filters( 'autoptimize_js_individual_script', $script, "" );
if ( has_filter('autoptimize_js_individual_script') && !empty($tmpscript) ) {
    $script=$tmpscript;
    $this->alreadyminified=true;
}
$this->jscode .= "\n" . $script;

Try statements allow you to test Javascript for errors thrown in a catch block.

Third party resources that load javascript, like ads and videos, can seriously delay page loads. 

 

Lazy Load 3d Party Javascript

// Load external script after initial page load 
document.addEventListener("DOMContentLoaded", function() {
  var tag = document.createElement("script");
  tag.src = "https://www.domain.com/path/to/script.js";
  document.getElementsByTagName("head")[0].appendChild(tag);
});

// Facebook javascript SDK embed
(function(d, s, id){
  var js, fjs = d.getElementsByTagName(s)[0];
  if (d.getElementById(id)) {return;}
  js = d.createElement(s); js.id = id;
  js.src = "//connect.facebook.net/en_US/sdk.js";
  fjs.parentNode.insertBefore(js, fjs);
}(document, 'script', 'facebook-jssdk'));

Compile CSS and Javascript to smallest size and combine into fewest files. 

 

  • Autoptimize
  • Better WordPress Minify
  • CloudFlare
  • Webpack

Minify & Concatenate

var path = require("path");
var webpack = require("../../");
module.exports = {
	entry: "./example",
	output: {
		path: path.join(__dirname, "js"),
		filename: "[chunkhash].js",
		chunkFilename: "[chunkhash].js"
	},
	plugins: [
		new webpack.optimize.AggressiveSplittingPlugin({
			minSize: 30000,
			maxSize: 50000
		}),
		new webpack.DefinePlugin({
			"process.env.NODE_ENV": JSON.stringify("production")
		})
	],
	recordsOutputPath: path.join(__dirname, "js", "records.json")
};

Choose image sizes appropriate for usage. Optimize upon upload to WordPress Media Library. 

 

  • WP Smush
  • EWWW
  • TinyPNG
  • Brotli

Optimize Images

Optimize delivery by pushing critical resources as soon as possible or loading from local storage.

 

  • CDNs
  • <link rel="preload">
  • HTTP/2 and HTTP/2 Push
  • Font Optimization
  • Critical CSS
  • Code Splitting
  • Pre-cache with Service Workers

Deliver Assets

Content delivery networks deliver static assets as quickly as possible using a distributed network of proxy servers. 

 

  • CloudFlare
  • Amazon CloudFront
  • MaxCDN
  • Akamai

CDNs

Enables priority loading of critical assets. Begins loading without blocking the document's onload event.

<link rel="preload">

<!-- preload async javascript -->
<link rel="preload" as="script" href="async_script.js"
onload="var script = document.createElement('script');
        script.src = this.href;
        document.body.appendChild(script);">
<!-- preload font -->
<link rel="preload" href="font.woff2" as="font" type="font/woff2" crossorigin>
<!-- preload css -->
<link rel="preload" as="style" href="async_style.css" onload="this.rel='stylesheet'">

Allows multiple request and response messages on the same connection. Basic unit is a "frame".

 

 

 

 

 

  • TCP stream multiplexing
  • HTTP header field compression
  • Prioritize HTTP frames weight
  • Flow control based on frame dependency

HTTP/2

+-----------------------------------------------+
 |                 Length (24)                   |
 +---------------+---------------+---------------+
 |   Type (8)    |   Flags (8)   |
 +-+-------------+---------------+-------------------------------+
 |R|                 Stream Identifier (31)                      |
 +=+=============================================================+
 |                   Frame Payload (0...)                      ...
 +---------------------------------------------------------------+

Send critical resources in first response. Skipping initial roundtrip to request resources.

HTTP/2 Push

Fonts are critical resources. Compress and deliver them as a priority with HTTP/2 or <link rel="preload">.

Font Optimization

Hosting your own font assets allows you to set long expire times and enable compression.

For top notch performance, inline fonts as SVG code.

Deliver inline CSS using <style> tags early on in your HTML for above-the-fold content or "Critical CSS".

 

Webpack Plugin

https://www.npmjs.com/package/webpack-plugin-critical

 

Javascript Bookmarklet

https://gist.github.com/PaulKinlan/6284142

 

Online Generator

https://www.sitelocity.com/critical-path-css-generator

Critical CSS

<!-- Inline Critical CSS with preloaded stylesheet -->
<style>

// Minified Critical CSS goes here.

</style> 
<link rel="preload" href="styles.css" onload="this.rel='stylesheet'">

Fetch locally cached assets first before requesting from remote. 

 

  • Javascript functions that manage browser cache
  • Open Cache, Cache Files, Retrieve Cached files
  • Responds to fetch events for network resources using event listeners
  • Dramatically reduce load time by pre-caching

Pre-cache w/ Service Workers

// Install a service worker

var CACHE_NAME = 'my-site-cache-v1';
var urlsToCache = [
  '/',
  '/styles/main.css',
  '/script/main.js'
];

self.addEventListener('install', function(event) {
  // Perform install steps
  event.waitUntil(
    caches.open(CACHE_NAME)
      .then(function(cache) {
        console.log('Opened cache');
        return cache.addAll(urlsToCache);
      })
  );
});

Look for metrics that matter, testing at various times of  day. Dig deeper with dev tools. 

 

  • Google PageSpeed
  • WebPageTest.org
  • Lighthouse
  • GT Metrix
  • Pingdom
  • Chrome Dev Tools

Evaluating Performance

Chrome Dev tools makes it easy to dig, the key is knowing what to look for...

https://developers.google.com/web/tools/chrome-devtools/

 

Things to look for

  • Time to first byte (TTFB)
  • First Meaningful Paint
  • DOMContentLoaded
  • Long running scripts
  • Lots of one color
  • Bottom Up view

Performance Debugging

Aim to reduce complexity, minimize paints, and use performant practices.

  • Reduce length of CSS selectors
  • Avoid complex style calculations (https://csstriggers.com)
  • Use transform and opacity for animations
  • Be conservative when it comes to layers
  • Flexbox is often faster than floats
  • Avoid style changes on input handlers
  • Load non-essential code with requestIdleCallback

Coding for Performance

// Example using requestIdleCallback
function myNonEssentialWork (deadline) {

  // Use any remaining time, or, if timed out, just run through the tasks.
  while ((deadline.timeRemaining() > 0 || deadline.didTimeout) &&
         tasks.length > 0)
    doWorkIfNeeded();

  if (tasks.length > 0)
    requestIdleCallback(myNonEssentialWork);
}

// https://developers.google.com/web/updates/2015/08/using-requestidlecallback

Jason Johnson

Email: jason@fullsteamlabs.com

Twitter: @jasonmjohnson

 

 

 

 

 

www.fullsteamlabs.com

828-202-5300

Optimizing WordPress for PageSpeed

By Jason Johnson

Optimizing WordPress for PageSpeed

  • 1,597
Loading comments...

More from Jason Johnson