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
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
828-202-5300

Optimizing WordPress for PageSpeed
By Jason Johnson