Server - Speed Optimization

Server - Speed Optimization

Znači, koraci su sledeći:

  1. Switch to PHP-FPM (FastCGI Process Manager) and nginx

  2. Enable SPDY (recompile nginx to 1.6.0) But it can be enabled over CloudFlare also. CloudFlare also uses nginx as reverse proxy.

  3. Enable Google Pagespeed module

    Test if it is enabled: curl -s -I | grep X-Page-Speed

  4. Secure SSL

5 Tools to Speed Up Your Web Site

Switch to nginx (done)

NOT SURE THIS PERMALLINKS FIX IS THE BEST WAY. READ THESE ALSO: Wordpress permalinks - Nginx Library WordPress NGINX Rewrite Rules

PHP-FPM is recommended over fcgid as it provides every benefit that fcgid has, with the added advantage of a shared opcode cache for all processes.

Fix for permalink errors

There is a very important fix for permalink errors in Wordpress, that is noticable as 404 errors on every page other than home page.

# wordpress permalinks 404 fix
if (!-e $request_filename) { rewrite ^(.*)$ /index.php break; }

The best explanation is found in this post. So, we should use this and not the above, but this one doesn’t work as duplicate locations are set.

# fix permalinks 404
# location ~ /$ { try_files $uri $uri/ /index.php?$args; }

Another fix for Plesk and nginx is here

Finally found a working replacement without an if statement:

rewrite !.(js|ico|gif|jpg|png|css|pdf|mov|mp3|eot|svg|ttf|woff|otf|txt|swf)$ /index.php break;
rewrite /$ /index.php break;

Enable GZip compression in nginx (done)

Every command and it’s meaning is explained in nginx admin-guide. Also, the best configuration was found and explained here.

echo '

# enable gzip compression
gzip on;
gzip_disable "msie6";


# leave default of 1 as gains are not noticable
# gzip_comp_level 9;

# default is to gzip only HTTP 1.1, but we want to gzip http 1.0 requests also
gzip_http_version 1.0;

# leave default
# gzip_buffers 16 8k;

# anything smaller than 50 bytes wont be compressed. default is 20 bytes
gzip_min_length 50;

gzip_proxied any;
gzip_vary on;

' > /etc/nginx/conf.d/gzip.conf

Test with

curl -s -H "Accept-Encoding: gzip,deflate,sdch" -I | grep -i Content-Encoding


Run nginx -t or service nginx configtest to test the nginx configuration and after restart or reload nginx by running service nginx restart

Best online test tools to check if compression is enabled, are on Check GZIP compression and

More nginx tweaks (done)


echo '
# nginx tweaks

# re-encode anything that is not in the defined character set
charset utf-8;

# set a far-future expires-header
# expires max;

# Number of seconds to wait for the next request from the same client on
# the same connection, but this also means that worker connections are
# tied up longer. Default was 65
keepalive_timeout 20;

# Depending on needs, sendfile can be either totally useless or completely
# essential - but it never can degrade performance.
sendfile on;
tcp_nopush on;
tcp_nodelay on;
' > /etc/nginx/conf.d/tweaks.conf

Really good source with explanations is here.


Expiring is much more complicated. Don’t do it for dynamic data - only for static one.

Keep Alive

Keep Alive is enabled by default in nginx, but it’s nice do decrease timeout from default of 65 seconds to 30s.

So set keepalive_timeout to 30 in /etc/nginx/nginx.conf.

HTTP Keepalive Connections and Web Performance Is Keep-Alive Enabled Checker

Worker Processes and Worker Connections

For the number of cores type grep processor /proc/cpuinfo | wc -l. To get practical maximum open file descriptors type ulimit -n.

Change in /etc/nginx/nginx.conf:

worker_processes -> auto
worker_connections -> 10240
worker_rlimit_nofile -> 100000 just below worker_processes line

Very nice boilerplate of nginx settings is here: h5bp/server-configs-nginx

Optimizing Nginx Configuration How To Optimize Nginx Configuration Tuning nginx worker_process to obtain 100k hits per min Nginx Tuning For Best Performance

Depending on needs, sendfile can be either totally useless or completely essential. But it never can degrade performance.

sendfile on;
tcp_nopush on;
tcp_nodelay on;

Nginx Optimization: Understanding sendfile, tcp_nodelay and tcp_nopush

Enable SPDY in nginx (done)

SPDY protocol 3.1 is implemented in nginx. The problem is that you can’t use SPDY without SSL.

  1. check build options: ngnix -V
  2. download ngnix new version
  3. /.configure with options from pt1 and add –with-http_spdy_module
  4. make
  5. make install

Let’s do it:

# needed packages
apt-get install build-essential libssl-dev libpcre3 libpcre3-dev

# download source
cd /usr/local/src/
tar -xvf nginx-1.6.2.tar.gz
cd nginx-1.6.2/

# get the current configuration directives
nginx -V

# nginx version: nginx/1.6.0
# TLS SNI support enabled
# configure arguments: --prefix=/usr/share --sbin-path=/usr/sbin/nginx --conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --lock-path=/var/lock/nginx.lock --pid-path=/var/run/ --http-client-body-temp-path=/var/lib/nginx/body --http-fastcgi-temp-path=/var/lib/nginx/fastcgi --http-proxy-temp-path=/var/lib/nginx/proxy --http-scgi-temp-path=/var/lib/nginx/scgi --http-uwsgi-temp-path=/var/lib/nginx/uwsgi --user=nginx --group=nginx --with-ipv6 --with-file-aio --with-http_ssl_module --with-http_realip_module --with-http_sub_module --with-http_dav_module --with-http_gzip_static_module --with-http_stub_status_module

# append an `--with-http_spdy_module` option
./configure --prefix=/usr/share --sbin-path=/usr/sbin/nginx --conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --lock-path=/var/lock/nginx.lock --pid-path=/var/run/ --http-client-body-temp-path=/var/lib/nginx/body --http-fastcgi-temp-path=/var/lib/nginx/fastcgi --http-proxy-temp-path=/var/lib/nginx/proxy --http-scgi-temp-path=/var/lib/nginx/scgi --http-uwsgi-temp-path=/var/lib/nginx/uwsgi --user=nginx --group=nginx --with-ipv6 --with-file-aio --with-http_ssl_module --with-http_realip_module --with-http_sub_module --with-http_dav_module --with-http_gzip_static_module --with-http_stub_status_module --with-http_spdy_module
make install

# Our nginx now supports SPDY

Now, we need to change the default Plesk template and reconfigure every domain. We do that by making a custom domain template:

mkdir -p /usr/local/psa/admin/conf/templates/custom/domain
cp /usr/local/psa/admin/conf/templates/default/domain/nginxDomainVirtualHost.php /usr/local/psa/admin/conf/templates/custom/domain/

nano /usr/local/psa/admin/conf/templates/custom/domain/nginxDomainVirtualHost.php

# append `spdy` after `ssl` in only one line
# ($OPT['default'] ? ' default_server' : '') . ($OPT['ssl'] ? ' ssl spdy' : '') ?>;

# reconfigure domains
plesk sbin httpdmng --reconfigure-domain

Finally, when definitely sure, do that for all domains:

plesk sbin httpdmng --reconfigure-all

Plesk 12 Nginx Update und SPDY aktivieren unter Debian

Enable SPDY on Nginx Enabling SPDY for Nginx in Plesk SPDY Nginx in Plesk 12 SPDY support in Nginx

Check for SPDY on SPDYCheck

Optimize & secure SSL (done)

I have a separate post on this huge area.

Google Pagespeed module for nginx ( done )

Google has PageSpeed modules that execute optimization on my server. There was also PageSpeed Service that optimized web site automatically and serve it from Google servers, exactly like CDN. Sadly, is PageSpeed Service not currently accepting new signups

We will be using ngx_pagespeed module, and for that, we need to recompile nginx. Later, we will enable pagespeed only on desired domains by using custom nginx directives.

Performance improvements are not mind blowing, but as an advanced tuning technique, it’s ok.

CloudFlare or MaxCDN already do similar optimizations so it’s not that big deal to enable Pagespeed. On the other side - not sure in CloudFlare speed, and I don’t want to pay MaxCDN for that.

How to install?

If you’re using Nginx you need to build from source.



There are multiple types of caching:

  • Opcode Cache - Zend Opcache is final and only sensible solution as it the best performer and is included in core of the current PHP version.

  • Object Cache APCu is almost the only viable solution. And, in Wordpress, it can be controlled with multiple plugins.

  • Database Cache Is not crucial as MySQL Query cache is already doing a quite nice job of optimizing database query results.

  • Full-page Cache

    - *nginx fastcgi_cache*: implemented in nginx itself using disk.
      Can be speedup by using shared memory as disk. Should be very
      fast, but needs to be purged from application somehow -
      Wordpress has plugin for that. Without purging, should be used as
      Microcache for no more than 5 sec.
    - *varnish*: out of question as doesn't support SSL without added
      _pound_ reverse-proxy.
    - *memcached* as data store. Ngnix knows to read directly from
      memcached, with module.
    - *APCu* as full-page data store, using the plugin. Should be the
      fastest compared to Memcached or Redis, but slower than nginx.
  • CDN Cache

Full Page Cache

NGINX Content Caching

nginx w/ fastcgi_cache module

This is the same as proxy_cache directives which are used for reverse proxy caching.


echo '
# microcache settings

fastcgi_cache_path /dev/shm/nginx levels=1:2 keys_zone=cachezone:100m inactive=60m;
fastcgi_temp_path /dev/shm/nginx/tmp;
fastcgi_cache_key "$scheme$request_method$host$request_uri";

' > /etc/nginx/conf.d/microcache.conf

Setting per domain:


cat <<'EOF' > /var/www/vhosts/

# Setup var defaults
set $no_cache "";

# If non GET/HEAD, don't cache & mark user as uncacheable for 1 second via cookie
if ($request_method !~ ^(GET|HEAD)$) {
    set $no_cache "1";

# Drop no cache cookie if need be
# (for some reason, add_header fails if included in prior if-block)
if ($no_cache = "1") {
    add_header Set-Cookie "_mcnc=1; Max-Age=2; Path=/";
    add_header X-Microcachable "0";

# Bypass cache if no-cache cookie is set
if ($http_cookie ~* "_mcnc") {
    set $no_cache "1";

# Bypass cache if flag is set
fastcgi_no_cache $no_cache;
fastcgi_cache_bypass $no_cache;

# Set cache zone
fastcgi_cache cachezone;

# Set cache key to include identifying components
fastcgi_cache_key $scheme$host$request_method$request_uri;

# Cache 404 responses for 30 minutes
fastcgi_cache_valid 404 30m;

# Only cache valid HTTP 200 responses for 5 seconds
fastcgi_cache_valid 200 5s;

# Serve from cache if currently refreshing
fastcgi_cache_use_stale updating;

# Set files larger than 1M to stream rather than cache
fastcgi_max_temp_file_size 1M;

# Custom logging
access_log /var/log/nginx/microcache.log custom_microcache;


There should already be a header X-Cache-Debug

Test this:

curl -s -I | grep -i X-Nginx-Cache

curl -s -I | grep -i X-Cache-Debug

We can just leave short caching periods. This is why this is often called microcaching.

But we can do a lot more for Wordpress - see:

If we want to be sure that we purged cached data, we can delete files directly in cache path or we can install a third-party module like ngx_cache_purge

There are a lot of Wordpress Plugins for nginx cache control.

Purging with the help of ngx_cache_purge

Nginx + WordPress + fastcgi_cache with conditional purging 40 Million hits a day on WordPress

How to Setup FastCGI Caching with Nginx on your VPS

Add this for purging:

location ~ /purge(/.*) {
    fastcgi_cache_purge WORDPRESS "$scheme$request_method$host$1";

The size of the cache:

du -h -s /dev/shm/nginx/

Q: These two lines are not exactly the same? fastcgi_cache_key "$scheme$request_method$host$request_uri"; vs. fastcgi_cache_purge WORDPRESS "$scheme$request_method$host$1";

A: Yes - In purge block, $request_uri contains part /purge at the beginning for every cached page-url. So $1 variable captures correct cached-url - without /purge in the beginning.

Purge all cache manually from shell by deleting all files altogether:

find /dev/shm/nginx -type f -delete

How to clear the cache of nginx?


Why You Should Always Use Nginx With Microcaching Nice comments: Microcaching for a Faster Site

In http {} block:

fastcgi_cache_path /var/cache/nginx2 levels=1:2 keys_zone=microcache:5m max_size=1000m;

Put in location ~ \.php$ block:

I see you use fastcgi_cache_use_stale updating, that is an important setting. Without it your performance would be much lower in real world scenarios.

log_format custom_microcache ‘$remote_addr - $remote_user [$time_local] ' ‘"$request" $status $body_bytes_sent ' ‘"$http_referer" “$http_user_agent” nocache:$no_cache’;

Purging by deleting cache files

Plugins to support this don’t need nothing on server side as they search for cached files in nginx cache dir and try to delete them. PHP script has to have sufficient rights to delete cache files - doesn’t look very secure to me.

Only for reverse proxy_cache

But this comment is like a bible: Use a fragment cache for WordPress (by Rarst)

Varnish doesn’t support SSL

How to install Varnish on Plesk

Varnish doesn’t support SSL. It can be done with pound in front of it. But nginx does, so this is why we will be using nginx cache.

Varnish: Why no SSL?

Memcached / Redis as store

If your box can handle the entire cache on its own, memcache/redis will only slow you down. APCu is shared memory - used right it will blow away memcached/redis.

All you need is nginx caching and APC at this point.

Memcache/Redis comes into the picture only when you have to scale your cache beyond a single box.


WordPress + Memcached

Blazing fast WordPress with Nginx and Memcached


We can use Redis as a store for full page caching

WordPress Caching with Nginx and Redis WordPress › wp-redis-cache « WordPress Plugins

Wordpress Plugins for Redis

How To Install and Use Memcache

APCu as full-page store

APCu can be also used as storage backend for full-page caching. As it is the best type of object caching for Wordpress, I will use it here, also.

Wordpress Plugins

There is a separate article on that.

Latest PHP > separate file

In case of Apache, this also sets multiple PHP versions. But, you can’t use multiple PHP versions for php-fpm, but you can update to any php-fpm version.

My current version was PHP 5.4.36 - the new one, installed is 5.6.6. This whole section isextracted into separate article.

PHP Opcode Cache

eAccelerator and APC are dormant and probably dead. That leaves us with Xcache and Zend. But, considering that Zend Opcache is included in PHP 5.5, the selection is simple.

And not only that, but XCache is no more the best opcode cache around as this comparison article concludes.

Note that Zend Opcache is not enabled by default.


How To Enable PHP 5.5 Opcache

List of PHP accelerators on Wikipedia

PHP, memcache & w3-total cache for WordPress

PHP’s Zend Opcache Config & Web Viewer

PHP Performance I: Everything You Need to Know About OpCode Caches

PHP Session Store

We can put PHP session data into a shared memory, or even in key-value db like Redis or Memacached.


PHP User Data Cache (Database Cache & Persistent Object Cache)

Persistent Object Caching WP Object Cache

APCu is userland caching: APC stripped of opcode caching in preparation for the deployment of Zend Optimizer+ as the primary solution to opcode caching in future versions of PHP.

APCu uses the same PHP functions that APC used so you don’t have to worry about breaking your application when switching over.

APCu only implements key/value store, and as Memcache or Redis are much better in that. Sometimes, APCu can be a great alternative for small scale applications that don’t need distributed caching systems.

Considering the APCu (formerly APC user cache) user cache, it’s a storage for applicative cache, you can tell Drupal to use it the same way as redis. Main problem with APCu is that it will store cache in the PHP shared memory, which means that you need to have a lot of dedicated RAM to the PHP processes to store Drupal data into it. Plus, if you multiple PHP frontends, each frontend will carry its own shared memory, data in those frontends may differs: this means that you can have a cache desync between PHP frontends; That’s why we oftenly prefer to use a memcache, or redis or mongodb.

The OPCode cache (formerly APC, xcache, etc…) : only serves the purpose of storing compiled version of PHP code into memory, and make it run faster - this has nothing to do with the later The applicative cache : purely business stuff that Drupal will store into a raw storage in order to avoid to do some business computations too often and be faster

How came to APCu: The Future of Caching in PHP

/usr/local/php566/bin/pecl remote-info APCu

/usr/local/php566/bin/pecl install APCu-beta


Enable and configure APCu:

echo '
; configuration for APCu
; extension: /usr/local/php566/lib/php/20131226/
' > /etc/php5/php566/fpm/conf.d/20-apcu.ini

When APC is compiled with mmap support (Memory Mapping), it will use only one memory segment (shm_segment). MMAP support is recommeded.

APC Runtime Configuration

Wordpress plugins

Object cache plugins are always drop-in plugins that upload file object-cache.php.

Object cache is explained simple: WordPress allows you to replace the cache api with a drop-in - a file that gets placed directly in your wp-content folder. If you create your own cache drop-in or use an existing plugin, you can make the object cache persist longer than a single page load. When you do that, transients, change a bit.

Note: Enable object cache only if there are more than 700-800 blog posts on your blog. If you have less posts then no need of enabling it.

Memcached as store

PHP, memcache & w3-total cache for WordPress tollmanz/wordpress-pecl-memcached-object-cache

Install memcached:

apt-get install memcached

apt-get install libmemcached-dev
/usr/local/php566/bin/pecl install memcached --with-libmemcached-dir=/usr/lib


How to flush memcached instance?

All the right answers are here.

apt-get install libmemcached-tools
memcflush --servers=localhost:11211

You can also do that with netcat or telnet, but this is the best way as far I am concerned.

Redis as store

  1. apt-get install libmemcached-dev

  2. /usr/local/php566/bin/pecl download memcached tar xzvf memcached-.tgz rm memcached-.tgz cd memcached-*

    /usr/local/php566/bin/phpize ./configure –disable-memcached-sasl –with-php-config=/usr/local/php566/bin/php-config –with-libmemcached-dir=/usr

    make checkinstall -D –pkgname=“php-memcached-custom” make install

    remove it anytime with dpkg -r php-memcached-custom

    The problem was non-working:

    /usr/local/php566/bin/pecl install memcached

Enable and configure memcached in PHP:

echo '
; configuration for memcached
; /usr/local/php566/lib/php/20131226/
' > /etc/php5/php566/fpm/conf.d/30-memcached.ini

To check functionality:

service php5-fpm restart

W3TC is not working with memcached but only with memcache PHP extension.

That shit worked out of the box:

/usr/local/php566/bin/pecl install memcached

echo '
; configuration for memcache extension
; /usr/local/php566/lib/php/20131226/
' > /etc/php5/php566/fpm/conf.d/30-memcache.ini

service php5-fpm restart

You can have both PHP extensions enabled. So, because of W3TC i disabled memcached-one.

It seems that PHP has two memcached libraries named memcache and memcached

Put sessions in memcached?

Thus, to see a performance boost from minification, having both an OpCode and Userland Cache is extremely important.

APCu | Miscellaneous Knowledge

SSL support

Enable SSL for SPDY support.

Force redirect to SSL

We need something that doesn’t cause redirect loops. We can use nginx variables like $scheme, $ssl_protocol or $https

# force https
if ($ssl_protocol = "") { return 301 https://$server_name$request_uri; }

This solution, with if statement is the only valid solution, albeit slower, as Plesk includes the same nginx.conf file in both server directives for HTTP and HTTPS.

Keep in mind, while testing changes, you should use a 302 temporary redirect instead of 301 permanent, to save hair-pulling when you discover you fixed it an hour earlier but the changes didn’t reflect in your browser.

How to force or redirect to SSL in nginx? Pitfalls - Nginx Community Force SSL in location with nginx http-https redirect / Positive SSL on nginx | DigitalOcean Force SSL and NO-www with Nginx Configuration - Heroku Forums

Enable HTTP/2 on nginx

Performance of HTTP/1.1 vs HTTP/2 Known implementations of HTTP/2


For us, based on hosting locations, using CDN has sense only for foreign, non-german customers. Based on CloudFlare data center locations, the closest is Frankfurt. Hetzner datacenters are in Falkenstein and Nuremberg, both at similar distance as Frankfurt.

Database optimization

The MySQL Query Cache

On our system, it was already enabled.

mysql -hlocalhost -usave-up-ch -p"Xtpx85#3" -e "show variables like 'query_cache_%'"

WOOW Nginx Secure SSL Web Server @

Nginx « WordPress Codex


Copied from W3 Total Cache plugin configuration:

# CORS fonts for CDN
location ~ \.(ttf|ttc|otf|eot|woff|font.css)$ {
   add_header Access-Control-Allow-Origin "*";

# gzip static content
gzip on;
gzip_types text/css text/x-component application/x-javascript application/javascript text/javascript text/x-js text/richtext image/svg+xml text/plain text/xsd text/xsl text/xml image/x-icon;

# Cache static content
location ~ \.(css|htc|less|js|js2|js3|js4)$ {
    expires 31536000s;
    add_header Pragma "public";
    add_header Cache-Control "max-age=31536000, public";

location ~ \.(html|htm|rtf|rtx|svg|svgz|txt|xsd|xsl|xml)$ {
    expires 3600s;
    add_header Pragma "public";
    add_header Cache-Control "max-age=3600, public";

location ~ \.(asf|asx|wax|wmv|wmx|avi|bmp|class|divx|doc|docx|eot|exe|gif|gz|gzip|ico|jpg|jpeg|jpe|json|mdb|mid|midi|mov|qt|mp3|m4a|mp4|m4v|mpeg|mpg|mpe|mpp|otf|odb|odc|odf|odg|odp|ods|odt|ogg|pdf|png|pot|pps|ppt|pptx|ra|ram|svg|svgz|swf|tar|tif|tiff|ttf|ttc|wav|wma|wri|woff|xla|xls|xlsx|xlt|xlw|zip)$ {
    expires 31536000s;
    add_header Pragma "public";
    add_header Cache-Control "max-age=31536000, public";
    add_header Link "<$scheme://$host$uri>; rel=\"canonical\"";

Missing Vary Accept-Encoding header

Specify a Vary: Accept-Encoding header

Uncomment all the gzip_ directives in nginx configuration:

sed -i -re 's/# (gzip_.*)$/\1/gi' /etc/nginx/nginx.conf

Specially important ones are gzip_types and gzip_vary.

Problem is maybe that gzip_vary on adds the Vary header only to non-gzipped responses those can be gzipped, but were not gzipped for some reasons. Source is Igor Sysoev, from here.

When SPDY is enabled the Vary: Accept-Encoding headers disappear. It’s because SPDY require user agents to support compression and that draws header useless. Source is here

Solution is not a really good, but anyway, to please GTmetrix:

location ~* \.(?:css|js|jpg|jpeg|gif|png|ico|cur|gz|svg|svgz|mp4|ogg|ogv|webm|htc)$ {
  expires 1w;
  access_log off;
  add_header Cache-Control "public";

  # add missing vary header in SPDY to please GTmetrix and such
  add_header Vary "Accept-Encoding";

Something similar is mentioned here for fonts.

Configure memcached

Increase memory

The one thing we must do is to configure memcached to use much more memory. Default was 64Mb and I want to increase that to 4G (4096M):

Note that the daemon will grow to this size, but does not start out holding this much memory.

sed -i -re 's/^-m .*$/-m 4096/gi' /etc/memcached.conf
service memcached restart

Enable logging

In short, you just need to add or uncomment two lines to /etc/memcached.conf: -v and lofgile /path/to/log and restart the daemon with service memcached restart.

Or more elegantly, execute

sed -i -re 's/^# -v$/-v/gi' /etc/memcached.conf

to set -v that will print out errors and warnings in log.

Err: Failed to write, and not due to blocking: Connection reset by peer

Probably some file descriptor limit.

# Soft / hard (max value) limit per current user
ulimit -Sn
ulimit -Hn

# Set them both
ulimit -n 50000

Increasing ulimit and file descriptors limit on Linux | Glass Onion Blog php unable to connect to memcached at times when under high load - Stack Overflow

There is also a parameter in memcached: Maximize core file limit

Memcached limit on result size of 1M

Not sure how to check this? The error would look like this: Values may not be more than 1000000 bytes in length; received 1071339 bytes

This is now a user-configurable parameter, so you can increase this limit with -I 15M or anything else. Maximum object size you can specify is 128M but the default remains at 1M.

date 01. Jan 0001 | modified 29. Dec 2023
filename: Server - Speed Optimization (XPS's conflicted copy 2023-11-09)