Configure NginX, PHP-FPM, APC & Varnish For WordPress

This guide will show you how to configure varnish for WordPress along with APC, NginX and PHP-FPM.  Wordpress has gained massive popularity and currently accounts for a decent slice of the internets webpages.  In the recent years, the makers of WordPress have seen a massive surge in its community, bringing new plugins and themes to the table.  It is incredibly extensive and the api vast, but with this expansion also comes a problem.  Wordpress is not known for vetting plugins, so whenever you install a free plugin, or even some paid plugins, you run the risk of introducing poor code into your site.  Perhaps code that will either break functionality, or slow your site’s speed down to a halt. This guide assumes that you have knowledge of WordPress and that you are proficient using command line in a server environment.  Enough talk, Lets dive into a setup that I personally use on all my WordPress blogs to keep content flowing smoothly and most of all, fast!

1. Install NginX, Varnish, PHP-FPM & APC.
Using your preferred method, install NginX.  If you are unaware how to install this there is a post on installation process here.  Essentially it is just adding the RPM and using yum to install NginX.  The same goes for PHP-FPM & APC.  Both of these are available in the CentOS repository, simply yum install them.  Varnish installation can be found on post “Installing Varnish on CentOS“, again fairly straight forward stuff.

2. Optimize NginX for WordPress
We now the have the correct software installed.  Lets open up the NginX configuration file located at /etc/nginx/nginx.conf  and input the following into it.

user www-data;
worker_processes 2;
pid /var/run/nginx.pid;

events {
    worker_connections 768;
    multi_accept on;
    use epoll;
}

http {

    # Let NGINX get the real client IP for its access logs
    set_real_ip_from 127.0.0.1;
    real_ip_header X-Forwarded-For;

    # Basic Settings
    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
    keepalive_timeout 20;
    client_max_body_size 15m;
    client_body_timeout 60;
    client_header_timeout 60;
    client_body_buffer_size  1K;
    client_header_buffer_size 1k;
    large_client_header_buffers 4 8k;
    send_timeout 60;
    reset_timedout_connection on;
    types_hash_max_size 2048;
    server_tokens off;

    # server_names_hash_bucket_size 64;
    # server_name_in_redirect off;

    include /etc/nginx/mime.types;
    default_type application/octet-stream;

    # Logging Settings
    # access_log /var/log/nginx/access.log;
    error_log /var/log/nginx/error.log;

    # Log Format
    log_format main '$remote_addr - $remote_user [$time_local] '
    '"$request" $status $body_bytes_sent "$http_referer" '
    '"$http_user_agent" "$http_x_forwarded_for"';

    # Gzip Settings
    gzip on;
    gzip_static on;
    gzip_disable "msie6";
    gzip_vary on;
    gzip_proxied any;
    gzip_comp_level 6;
    gzip_min_length 512;
    gzip_buffers 16 8k;
    gzip_http_version 1.1;
    gzip_types text/css text/javascript text/xml text/plain text/x-component
    application/javascript application/x-javascript application/json
    application/xml  application/rss+xml font/truetype application/x-font-ttf
    font/opentype application/vnd.ms-fontobject image/svg+xml;

    # Virtual Host Configs
    include /etc/nginx/conf.d/*.conf;
    include /etc/nginx/sites-enabled/*;
}

Now that we have NginX primed and ready to go, we need to make changes to our virtual host.  Create your vhost file at /etc/nginx/conf.d/yourdomain.conf  and input the following text:

server {
    listen       127.0.0.1:8080;
    server_name  syntaxheaven.com;

    port_in_redirect off;
    server_tokens off;
    autoindex off;

    client_max_body_size 15m;
    client_body_buffer_size 128k;

    access_log  /home/syntax/logs/access_log  main;
    error_log   /home/syntax/logs/error_log;

    root   /home/syntax/public_html;
    index index.php  index.html index.htm;
    try_files $uri $uri/ /index.php;

    error_page  404              /404.html;
    location = /404.html {
        root   /usr/share/nginx/html;
    }

    # Define default caching of 24h
    expires 86400s;
    add_header Pragma public;
    add_header Cache-Control "max-age=86400, public, must-revalidate, proxy-revalidate";

    # Redirect server error pages to static 50x.html
    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }

    # Don't log robots.txt requests
    location = /robots.txt {
        allow all;
        log_not_found off;
        access_log off;
    }

    # Rewrite for versioned CSS+JS via filemtime
    location ~* ^.+\.(css|js) {
        rewrite ^(.+)\.(\d+)\.(css|js)$ $1.$3 last;
        expires 31536000s;
        access_log off;
        log_not_found off;
        add_header Pragma public;
        add_header Cache-Control "max-age=31536000, public";
    }

    # Aggressive caching for static files
    # If you alter static files often, please use
    # add_header Cache-Control "max-age=31536000, public, must-revalidate, proxy-revalidate";
    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;
        access_log off;
        log_not_found off;
        add_header Pragma public;
        add_header Cache-Control "max-age=31536000, public";
    }

    location ~* (^(?!(?:(?!(php|inc)).)*/uploads/).*?(php)) {
        try_files $uri = 404;
        fastcgi_split_path_info ^(.+.php)(.*)$;
        fastcgi_pass unix:/var/run/php-fpm.socket;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param   SCRIPT_NAME        $fastcgi_script_name;
        include /etc/nginx/fastcgi_params;
        fastcgi_intercept_errors on;
        fastcgi_ignore_client_abort off;
        fastcgi_connect_timeout 60;
        fastcgi_send_timeout 180;
        fastcgi_read_timeout 180;
        fastcgi_buffer_size 128k;
        fastcgi_buffers 4 256k;
        fastcgi_busy_buffers_size 256k;
        fastcgi_temp_file_write_size 256k;
    }

}

This configuration is for one WordPress installation located in the home directory.  Take note: you will need to change the root location for your blog, the access & error log locations, and the server name.  To make sure there were no issues you can run nginx -t to check syntax validity on your NginX configuration.

3. Configure PHP-FPM

OK, we are making magic here folks.  Let’s keep the process rolling with the configuration of PHP-FPM ( PHP FastCGI Process Manager ) will act as your PHP handler.  The definition of php-fpm is noted as the following.
PHP is a server-side scripting language designed for web development but also used as a general-purpose programming language. PHP is now installed on more than 244 million websites and 2.1 million web servers worldwide.
OK, you should already have PHP-FPM installed.  If not, just grab it from the repositories as it should be standard in later version of RHEL & CentOS.  The same goes for APC as well.  Open up your php.ini for editing using your favorite editor.  This file is fairly big so I will not be showing you it’s entire contents.  Instead just make sure the following values are set in the configuration file.

ignore_user_abort = Off
post_max_size = 15M
upload_max_filesize = 15M
default_charset = "UTF-8"
allow_url_fopen = Off
default_socket_timeout = 30
mysql.allow_persistent = Off

Now at the very end of your php.ini file drop this immediately following the last line.

[apc]
apc.stat = "0"
apc.max_file_size = "1M"
apc.localcache = "1"
apc.localcache.size = "256"
apc.shm_segments = "1"
apc.ttl = "3600"
apc.user_ttl = "7200"
apc.gc_ttl = "3600"
apc.cache_by_default = "1"
apc.filters = ""
apc.write_lock = "1"
apc.num_files_hint= "512"
apc.user_entries_hint="4096"
apc.shm_size = "256M"
apc.mmap_file_mask=/tmp/apc.XXXXXX
apc.include_once_override = "0"
apc.file_update_protection="2"
apc.canonicalize = "1"
apc.report_autofilter="0"
apc.stat_ctime="0"

;This should be used when you are finished with PHP file changes.
;As you must clear the APC cache to recompile already cached files.
;If you are still developing, set this to 1.
apc.stat="0"

This serve as the configuration settings for APC.  Next we need to locate the php-fpm.conf file.  On my machine this was located just inside /etc at /etc/php-fpm.conf .  Set the following lines to these values.

pid = /var/run/php5-fpm.pid
error_log = /var/log/php5-fpm.log
emergency_restart_threshold = 5
emergency_restart_interval = 2
events.mechanism = epoll

Take note: Double check that the error and access log files actually exist at the location defined.  Finally, in the www.conf  file located at /etc/php-fpm.d/www.conf make the following adjustments.

user = www-data
group = www-data
listen = /var/run/php-fpm.socket
listen.owner = www-data
listen.group = www-data
listen.mode = 0666
listen.allowed_clients = 127.0.0.1
pm = dynamic
pm.max_children = 50
pm.start_servers = 15
pm.min_spare_servers = 5
pm.max_spare_servers = 25
pm.process_idle_timeout = 60s
request_terminate_timeout = 30
security.limit_extensions = .php

Then, amend the following to the end of the file

php_flag[display_errors] = off
php_admin_value[error_reporting] = 0
php_admin_value[error_log] = /var/log/php5-fpm.log
php_admin_flag[log_errors] = on
php_admin_value[memory_limit] = 128M
php_admin_value[session.save_path] = /tmp/

4. Add A Layer of Varnish

Quick recap, so far we have installed NginX, setup PHP-FPM with APC to boot.  But wait, NginX is on port 8080!  That isn’t the port that browsers look to for content, you broke my site! Hold it now, we aren’t finished yet. This is where Varnish comes in.  We will now proceed to set Varnish up on port 80 so when someone navigates to your site, Varnish will handle the request, process its VCL rule set to determine whether to pull from cache, or serve fresh content from NginX. If you followed instructions, you should have Varnish installed. Lets open /etc/sysconfig/varnish , or on some servers /etc/default/varnish for editing.  Once open, amend this somewhere in the file.

DAEMON_OPTS="-a :80 \
    -T localhost:6082 \
    -f /etc/varnish/default.vcl \
    -u www-data -g www-data \
    -S /etc/varnish/secret \
    -p thread_pools=2 \
    -p thread_pool_min=25 \
    -p thread_pool_max=250 \
    -p thread_pool_add_delay=2 \
    -p session_linger=50 \
    -p sess_workspace=262144 \
    -p cli_timeout=40 \
    -s malloc,768m"

This will server as the default start up options for Varnish on startup, otherwise you would probably need to type out a giant one liner containing all the above on every start.  Finally, we need to open the default VCL file located at /etc/varnish/default.vcl.  Go ahead and replace its content with the following.

backend default {
    .host = "127.0.0.1";
    .port = "8080";
}

acl purge {
"127.0.0.1";
}

sub vcl_recv {
# Allow purge requests
if (req.request == "PURGE") {
        if (!client.ip ~ purge) {
            error 405 "Not allowed.";
        }
        ban("req.url ~ ^" + req.url + " && req.http.host == " + req.http.host);
        return(lookup);
    }

# Add header for sending client ip to backend
set req.http.X-Forwarded-For = client.ip;

# Normalize content-encoding
if (req.http.Accept-Encoding) {
        if (req.url ~ "\.(jpg|png|gif|gz|tgz|bz2|lzma|tbz)(\?.*|)$") {
            remove req.http.Accept-Encoding;
        } elsif (req.http.Accept-Encoding ~ "gzip") {
            set req.http.Accept-Encoding = "gzip";
        } elsif (req.http.Accept-Encoding ~ "deflate") {
            set req.http.Accept-Encoding = "deflate";
        } else {
            remove req.http.Accept-Encoding;
        }
    }

    # Remove cookies and query string for real static files
    if (req.url ~ "^/[^?]+\.(gif|jpg|jpeg|swf|css|js|txt|flv|mp3|mp4|pdf|ico|png|gz|zip|lzma|bz2|tgz|tbz)(\?.*|)$") {
       unset req.http.cookie;
       set req.url = regsub(req.url, "\?.*$", "");
    }

    # Don't cache admin
    if (req.url ~ "((wp-(login|admin|comments-post.php|cron.php))|login|timthumb|wrdp_files)" || req.url ~ "preview=true" || req.url ~ "xmlrpc.php") {
        return (pass);
    } else {
     if ( !(req.http.cookie ~ "wpoven-no-cache") ) {
         unset req.http.cookie;
     }
}
}

sub vcl_hit {
# purge cached objects from memory
if (req.request == "PURGE") {
purge;
error 200 "Purged";
}
}

sub vcl_miss {
# purge cached objects variants from memory
        if (req.request == "PURGE") {
    if (!client.ip ~ purge) {
error 405 "Not allowed.";
    }
    ban("req.url ~ "+req.url);
    error 200 "Purged";
        }

if (req.request == "PURGE") {
purge;
error 404 "Purged varients";
}
}

sub vcl_fetch {
# Dont cache admin
if (req.url ~ "(wp-(login|admin|comments-post.php|cron.php))|login" || req.url ~ "preview=true" || req.url ~ "xmlrpc.php") {
     return (deliver);
} else {
     if ( beresp.ttl > 0s && !(beresp.http.set-cookie ~ "wpoven-no-cache") ) {
         unset beresp.http.set-cookie;
        }
    }
}

sub vcl_deliver {
    # Remove unwanted headers
    unset resp.http.Server;
    unset resp.http.X-Powered-By;
    unset resp.http.x-backend;
    unset resp.http.Via;
    unset resp.http.X-Varnish;
}

This helps to cache certain aspects of WordPress that we need, and tells Varnish to not cache other aspects, like the admin backend.  IT also has fairly aggressive  caching rule  sets and will serve gzipped content. Of course now you will need to install WordPress with your choice of Varnish plugins.  These plugins will purge a particular pages cache once you update it so your users are seeing the fresh content as it is written.  If you have any issues with this make sure to give each service a restart and be sure you have permissions setup correctly.  I had to manually set my docroot to www-data, the user we defined in both NginX configuration file and PHP-FPM.

5. WordPress Plugins

To make sure you take advantage of your new configuration and to avoid problems, I recommend the following WordPress plug-ins:
APC Object Cache Backend
Nginx Helper
WordPress Varnish
There you have it!  You should now have Varnish for WordPress setup along with PHP-FPM, NginX and PHP-FPM.  Drop me a line in the comments if you have any thing to add!

Nhận xét

Bài đăng phổ biến