Preface: Let’s make a ruckus
Chapter 1: A Guide To This Guide
Chapter 2: Why Page Speed Matters To Digital Marketing
Chapter 3: What Impacts Page Speed?
Chapter 4: Novice – Image Compression And Such
Chapter 5: Intermediate – Server Compression And Geekery
Chapter 6: Advanced Chapter 6: Advanced – Varnish, Apache and nginx ← You are here
Chapter 7: Tools
Chapter 8: Glossary
Hidden Track: WordPress Optimization
In this post: We go down, down into the deepest, darkest heart of servers. And survive, sanity intact. If you want to really get your site screaming fast, this is where you need to go.
This chapter’s different. Instead of individual caching tips and tricks, we talk about entire technology suites and big-time server configuration changes. We’re doing some serious geekenization here. Configuration changes free up the pipe, accelerate server performance and speed up browsers. This is the big time.
The easiest advanced upgrade you can make: Move your database to a separate server optimized for database software.
Database access and web page delivery both require lots of resources. They have to share those resources. A database may suck up CPU cycles that the web server needs during high traffic. The web server may eat up memory the database needs. Both can eat up disk space.
Putting the database on one server and the web server software on another gives each one its own, dedicated set of resources. You can also give the database access to a faster CPU (or, if you’re doing this in the cloud, access to more CPU oomph), which it needs.
Be sure the two servers have a nice, fat pipe between them. They’ll have to talk to each other. But that’s trivial compared to the gains you’ll see.
Our stack is pretty fancy. We built it because we were going after that last .4 seconds to shave off load times. When you’re down to this kind of hair-splitting, things get pretty customized.
But we’ve gotten up to our elbows in servers and tried a lot of stuff. This stack provides us the best results. It runs eigene-homepage-erstellen.net (and many others).
We used to use a pure NGINX stack. Since moving to SSL, we now use a combination of NGINX, Apache, Varnish and a load balancer, like this:
Load balancer >> NGINX >> Varnish >> Apache
The performance win is huge. We originally took eigene-homepage-erstellen.net from 2+ second load times to under 1 second. Since then, we’ve moved to SSL and added other technologies. That’s increased load times back to 2.5 seconds or so. But because of deferred javascript and pre-rendering, all pages fully load for the visitor in under 1 second.
We could write an entire extra book (or seven) about this technology stack and we’d still leave things out. Have a look at this introductory blog post we wrote here. But this isn’t a ‘one size fits all’ solution. You’ll need to tweak and change your toolset to fit your unique site and applications.
If you need specific instructions to do this stuff, please, don’t do it yourself. This isn’t a statement on your skills. It’s just smart: These are open-source technologies. They have their quirks. You’ll need all your problem-solving brainpower to deal with those. The technology needs to already make sense.
Technically, NGINX is the ‘public’ side of our stack. It’s delivering the actual pages to users.
NGINX (“engine x”) is a lightweight HTTP server with modular architecture that serves static and index files, supporting accelerated reverse proxying with caching, simple load balancing, auto indexing, gzipping, FastCGI caching, and much more.
We like NGINX a lot and have used it for eigene-homepage-erstellen.net for over 2 years now. Combined with PHP5-FPM, it’s the foundation for the whole stack. FPM the FastCGI Process Manager. It’s an alternative to some of the PHP-specific accelerators.
It lets you spawn PHP processes at blazing speed and gracefully starts and stops PHP workers without losing queries.
Put NGINX and PHP5-FPM together and you’ve got one heck of a combination.
Apache is the workhorse. It runs WordPress and builds our site’s pages.
If you’re a web geek, you know Apache. It’s robust and super-popular, so our web partners know how to work with it. And, while it’s not as speedy as NGINX out of the box, you can tweak it to match NGINX performance.
Here are a few settings we use to speed things up:
AllowOverride None
AllowOverride is a directive that is usually set to “All” in standard Apache configurations. It tells Apache to scan and process .htaccess files in every directory and sub-directory on every Apache request, meant to be a convenience for .htaccess updates taking effect immediately. As you can imagine, this eats up a LOT of resources from the server.
Our recommendation is to set this directive to “None”, and instead include any .htaccess files required for your site.
<Directory /var/www/eigene-homepage-erstellen.net>
Options -Indexes +FollowSymLinks -MultiViews
AllowOverride None
Include /var/www/eigene-homepage-erstellen.net/.htaccess
</Directory>
Included files get cached by Apache, so any changes made to the .htaccess file would require the reloading of Apache for the updates to take effect.
mod_php
Running Apache with mod_php has been criticized for slow performance, especially when compared to NGINX with FastCGI (PHP5-FPM). However, we (and others) have found that it is the product of poor configuration, directly related to AllowOverride. Once .htaccess discovery is turned off, Apache with mod_php routinely runs as fast or faster than FastCGI.
We certainly aren’t condemning FastCGI, but it is worth benchmarking your site. You may find that FastCGI gets better results, or perhaps mod_php is slightly better. We are confident in both setups as we have implemented each for eigene-homepage-erstellen.net, currently running Apache with mod_php.
OpCache/APC
With any PHP implementation geared towards performance, OpCache or APC should be utilized. PHP 5.5 introduced built-in OpCache. Before 5.5, APC or Xcache had to be implemented alongside PHP for the performance boosts they all provide: caching PHP code.
When a PHP script is processed for the first time, OpCache/APC will store the machine code in local memory, allowing for quick access the next time it is requested. We have seen OpCache/APC provide a tremendous performance boost in all of the sites we have implemented and highly recommend it. There are more complexities than just turning it on, so be sure to do research to find a solution for your implementation.
Varnish is a reverse proxy HTTP accelerator developed for dynamic, content-heavy web sites. Varnish caches pages in virtual memory, leaving the operating system to decide what gets written to disk or stored in RAM.
That’s a triple-win:
Not much to see here. The load balancer handles SSL and passes requests to the best nodes.
We can’t give you a step-by-step, but we can give some configuration examples. Here you go:
Expires headers are a web server’s way of telling a visiting browser “This file won’t be changing for a while.” Used correctly, they reduce the number of HTTP requests required per visit, and that’s a huge performance gain. You reduce use of the pipe.
Here’s a simple example:
If the server does NOT have expires headers defined for PNG files, your browser requests and re-loads the file every time it loads a page. Ugh.
A typical page on our site has 100-200 separate files or resources. Without expires headers, visiting browsers have to load every file, every time they hit the site. Seems like a waste, doesn’t it?
Using expires headers, we reduce those requests 50%.
Warning: Be sure to understand what files you need to keep relatively fresh, and set their expires headers accordingly. Think about which files do change.
NGINX expires configurations
This configuration is super-aggressive, telling NGINX to set the expiration of a huge number of file types to 1 year (31536000 seconds) from first load date.
# Aggressive caching for static files that rarely/never change
location ~* \.(asf|asx|wax|wmv|wmx|avi|bmp|class|divx|doc|docx|eot|exe|
gif|gz|gzip|ico|jpg|jpeg|jpe|mdb|mid|midi|mov|qt|mp3|m4a|mp4|m4v|mpeg|
mpg|mpe|mpp|odb|odc|odf|odg|odp|ods|odt|ogg|ogv|otf|pdf|png|pot|pps|
ppt|pptx|ra|ram|svg|svgz|swf|tar|t?gz|tif|tiff|ttf|wav|webm|wma|woff|
wri|xla|xls|xlsx|xlt|xlw|zip)$ {
expires 31536000s;
add_header Pragma public;
add_header Cache-Control "max-age=31536000, public";
}
This is a lot less aggressive. It tells NGINX to set the expiration data of CSS and javascript files to 24 hours (86400 seconds).
location ~* \.(css|js)$ {
expires 86400s;
add_header Pragma public;
add_header Cache-Control "max-age=86400, public";
}
Apache expires configurations
The same can be done with Apache.
<IfModule mod_mime.c>
AddType text/css .css
AddType application/x-javascript .js
AddType text/x-component .htc
AddType text/html .html .htm
AddType text/richtext .rtf .rtx
AddType image/svg+xml .svg .svgz
AddType text/plain .txt
AddType text/xsd .xsd
AddType text/xsl .xsl
AddType text/xml .xml
AddType video/asf .asf .asx .wax .wmv .wmx
AddType video/avi .avi
AddType image/bmp .bmp
AddType application/java .class
AddType video/divx .divx
AddType application/msword .doc .docx
AddType application/vnd.ms-fontobject .eot
AddType application/x-msdownload .exe
AddType image/gif .gif
AddType application/x-gzip .gz .gzip
AddType image/x-icon .ico
AddType image/jpeg .jpg .jpeg .jpe
AddType application/vnd.ms-access .mdb
AddType audio/midi .mid .midi
AddType video/quicktime .mov .qt
AddType audio/mpeg .mp3 .m4a
AddType video/mp4 .mp4 .m4v
AddType video/mpeg .mpeg .mpg .mpe
AddType application/vnd.ms-project .mpp
AddType application/x-font-otf .otf
AddType application/vnd.oasis.opendocument.database .odb
AddType application/vnd.oasis.opendocument.chart .odc
AddType application/vnd.oasis.opendocument.formula .odf
AddType application/vnd.oasis.opendocument.graphics .odg
AddType application/vnd.oasis.opendocument.presentation .odp
AddType application/vnd.oasis.opendocument.spreadsheet .ods
AddType application/vnd.oasis.opendocument.text .odt
AddType audio/ogg .ogg
AddType application/pdf .pdf
AddType image/png .png
AddType application/vnd.ms-powerpoint .pot .pps .ppt .pptx
AddType audio/x-realaudio .ra .ram
AddType application/x-shockwave-flash .swf
AddType application/x-tar .tar
AddType image/tiff .tif .tiff
AddType application/x-font-ttf .ttf .ttc
AddType audio/wav .wav
AddType audio/wma .wma
AddType application/vnd.ms-write .wri
AddType application/vnd.ms-excel .xla .xls .xlsx .xlt .xlw
AddType application/zip .zip
</IfModule>
<IfModule mod_expires.c>
ExpiresActive On
ExpiresByType text/css A86400
ExpiresByType application/x-javascript A86400
ExpiresByType text/x-component A31536000
ExpiresByType text/html A3600
ExpiresByType text/richtext A3600
ExpiresByType image/svg+xml A3600
ExpiresByType text/plain A3600
ExpiresByType text/xsd A3600
ExpiresByType text/xsl A3600
ExpiresByType text/xml A3600
ExpiresByType video/asf A31536000
ExpiresByType video/avi A31536000
ExpiresByType image/bmp A31536000
ExpiresByType application/java A31536000
ExpiresByType video/divx A31536000
ExpiresByType application/msword A31536000
ExpiresByType application/vnd.ms-fontobject A31536000
ExpiresByType application/x-msdownload A31536000
ExpiresByType image/gif A31536000
ExpiresByType application/x-gzip A31536000
ExpiresByType image/x-icon A31536000
ExpiresByType image/jpeg A31536000
ExpiresByType application/vnd.ms-access A31536000
ExpiresByType audio/midi A31536000
ExpiresByType video/quicktime A31536000
ExpiresByType audio/mpeg A31536000
ExpiresByType video/mp4 A31536000
ExpiresByType video/mpeg A31536000
ExpiresByType application/vnd.ms-project A31536000
ExpiresByType application/x-font-otf A31536000
ExpiresByType application/vnd.oasis.opendocument.database A31536000
ExpiresByType application/vnd.oasis.opendocument.chart A31536000
ExpiresByType application/vnd.oasis.opendocument.formula A31536000
ExpiresByType application/vnd.oasis.opendocument.graphics A31536000
ExpiresByType application/vnd.oasis.opendocument.presentation A31536000
ExpiresByType application/vnd.oasis.opendocument.spreadsheet A31536000
ExpiresByType application/vnd.oasis.opendocument.text A31536000
ExpiresByType audio/ogg A31536000
ExpiresByType application/pdf A31536000
ExpiresByType image/png A31536000
ExpiresByType application/vnd.ms-powerpoint A31536000
ExpiresByType audio/x-realaudio A31536000
ExpiresByType image/svg+xml A31536000
ExpiresByType application/x-shockwave-flash A31536000
ExpiresByType application/x-tar A31536000
ExpiresByType image/tiff A31536000
ExpiresByType application/x-font-ttf A31536000
ExpiresByType audio/wav A31536000
ExpiresByType audio/wma A31536000
ExpiresByType application/vnd.ms-write A31536000
ExpiresByType application/vnd.ms-excel A31536000
ExpiresByType application/zip A31536000
</IfModule>
We tried to find web servers that don’t support expires headers and couldn’t. But never say never, right?
Here’s a list of the ones we know will work. Expires headers can be set in IIS (Windows), Apache Tomcat (Java), etc. Many tools and content management systems will let you set expires headers, too. On WordPress, W3 Total Cache will help you set expires headers.
Keep-Alive is all about reducing server overhead.
In non-geek, the ‘Keep-Alive’ setting tells the server to maintain a connection between your web browser and the site server while you’re browsing. It’s like handing the phone to someone else when they want to talk to the caller, too, instead of hanging up and making them call back. This reduces round trips. The server won’t have to open as many new connections. That means less processor, memory and network overhead.
On HTTPS-only sites Keep-Alive is very, very important. TLS connections require multiple ‘handshakes.’ Keep-Alive means fewer new connections and many fewer handshakes.
As long as you’re using the core HTTP module (HTTPCoreModule) for NGINX, keep alive should be enabled by default. But if you see this directive, it’s disabled:
keepalive_disable;
For shame.
Remove that directive to re-enable. Control how long connections are kept alive with the keepalive_timeout variable. This value is in seconds:keepalive_timeout 10;
Apache should have Keep-Alive enabled by default, too. If it doesn’t, turn it on in the Apache configuration file (apache2.conf or httpd.conf).
KeepAlive On
MaxKeepAliveRequests 100
KeepAliveTimeout 5s
‘MaxKeepAliveRequests’ and ‘KeepAliveTimeout’ are additional variables that can be configured. ‘MaxKeepAliveRequests’ are the maximum number of requests allowed during a persistent connection. If you can, leave this number high. ‘KeepAliveTimeout’ is the number of seconds to wait for the next request from the persistent connection. Don’t set this too high—it could clobber your server by keeping connections open longer than necessary. Try 5-10 seconds, then tweak as necessary.
If you do not have access to the server’s Apache config file, try adding it to the .htaccess file:
<IfModule mod_headers.c>
Header set Connection keep-alive
</IfModule>
We talked about CDN basics in the Intermediate chapter. Time to dig deeper. These are all about the pipe:
When you’re really, really trying to squeeze every bit of performance out of your web site, it comes down to packets. And cookies. Browser cookies. Not chocolate-chip cookies.
Most cookies increase the size of even the tiniest file beyond a single packet. They also may require that the visiting browser upload more packets to the server, because servers read those cookies. Reduce the number of cookied files and you reduce file size plus the number of uploaded cookies.
But why do you need cookies? For dynamic sites, sure. And for web analytics. You don’t need to serve cookies (see what I did there?) for CSS files, or javascript, or all those little images and icons. Serve those from a cookieless domain and you improve reduce pipe use.
Compared to the other stuff in this chapter, this is an easy one. Set up a cookieless domain a couple of ways:
Many CDNs do this for you.
Or:
One note: Don’t set cookies at your top-level domain (yourdomain.com). If you do, then you’ll end up setting cookies on subdomains, as well. Set cookies on www.yourdomain.com instead. A few quick tips:
You can use the _setDomainName variable in your Google Analytics javascript:
_gaq.push(
['_setAccount', 'UA-xxxxxxx-1'],
['_setDomainName', 'www.example.com'],
['_trackPageview']
);
For WordPress, add this PHP variable in your wp-config file:
define('COOKIE_DOMAIN', 'www.example.com');
This chapter talked about the most complex, inch-by-inch performance improvements you can make. Do the Novice and Intermediate stuff, first.
And, of course, test before you launch.
Rest, weary friend. You’ve stared down the monster. But it’s dangerous to go alone. Onward to some favorite tools for the task at hand.