title: What's wrong with this server config for Wordpress (nginx, varnish, apc)?
body: <p>I’m setting up the Wordpress backend for a high traffic website (gets around 15-20 million pageviews a month). It's up ad running but it's not performing as I wished.</p>
  
  <p>The components are:</p>
  
  <ol>
  <li>Php5-fpm </li>
  <li>FastCGI</li>
  <li>Nginx</li>
  <li>Varnish</li>
  <li>APC</li>
  <li>W3TC</li>
  <li>Cloudflare (Business Plan)</li>
  <li>Hosting on Digitalocean (32 GB Memory, 12 Core processor, 320 GB SSD Disk)</li>
  </ol>
  
  <p>I’m not an expert but I’ve set up everything taking help of online blogs and documentation. But I’m sure something is amiss as sometimes I get errors like “<strong>Error connecting to database</strong>”, <strong>slow speed when opening the website for the first time</strong> (long wait time when checked with pingdom tools)</p>
  
  <p>So sharing the config so that the experts here can comment on the server settings. Am I missing anything? </p>
  
  <p>Please note: There are other params too in the below files, I’ve noted down the most relevant</p>
  
  <p><strong>PHP settings:</strong></p>
  
  <p>/etc/php5/fpm/php.ini</p>
  
  <pre><code>max_execution_time = 180
  max_input_time = 60
  max_input_vars = 5000
  memory_limit = 256M
  post_max_size = 10M
  file_uploads = On
  upload_max_filesize = 10M
  max_file_uploads = 20
  default_socket_timeout = 90
  mysql.connect_timeout = 3000
  session.cache_expire = 180
  [apc]
  apc.write_lock = 1
  apc.slam_defense = 0
  apc.shm_size = "1024M"
  </code></pre>
  
  <p>/etc/php5/fpm/pool.d/www.conf</p>
  
  <pre><code>user = nginx
  group = nginx
  listen = /dev/shm/php-fpm-www.sock
  listen.owner = nginx
  listen.group = nginx
  listen.mode = 0660
  pm.max_children = 200
  pm.start_servers = 25
  pm.min_spare_servers = 25
  pm.max_spare_servers = 50
  pm.process_idle_timeout = 60s;
  pm.max_requests = 2000
  request_terminate_timeout = 600
  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] = 1G
  </code></pre>
  
  <p><strong>Nginx:</strong></p>
  
  <p>/etc/nginx/nginx.conf</p>
  
  <pre><code>user  nginx;
  worker_processes  24;
  worker_rlimit_nofile 200000;
  events {
      worker_connections  4000;
      use epoll;
      multi_accept on;
  }
  http {
      server_names_hash_bucket_size  64;
      sendfile        on;
      tcp_nopush     on;
      tcp_nodelay on;
  
      keepalive_timeout  65;
      client_header_timeout 240;
      client_body_timeout 240;
      fastcgi_read_timeout 180;
      client_max_body_size 32m;
      gzip  on;
      gzip_disable "msie6";
  
          gzip_vary on;
          gzip_proxied any;
          gzip_comp_level 6;
          gzip_buffers 16 8k;
          gzip_http_version 1.1;
          gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript;
  
      include /etc/nginx/conf.d/*.conf;
      port_in_redirect off;
  }
  </code></pre>
  
  <p>/etc/nginx/conf.d/default.conf</p>
  
  <pre><code>server {
  client_max_body_size 10M;
  listen 8080;
  set $cache_uri $request_uri;
  
          # POST requests and urls with a query string should always go to PHP
          if ($request_method = POST) {
                  set $cache_uri 'null cache';
          }
          if ($query_string != "") {
                  set $cache_uri 'null cache';
          }
  
          # Don't cache uris containing the following segments
          if ($request_uri ~* "(/wp-admin/|/xmlrpc.php|/wp-(app|cron|login|register|mail).php|wp-.*.php|/feed/|index.php|wp-comments-popup.php|wp-links-opml.php|wp-locations.php|sitemap(_index)?.xml|[a-z0-9_-]+-sitemap([0-9]+)?.xml)") {
                  set $cache_uri 'null cache';
          }
  
          # Don't use the cache for logged in users or recent commenters
          if ($http_cookie ~* "comment_author|wordpress_[a-f0-9]+|wp-postpass|wordpress_logged_in") {
                  set $cache_uri 'null cache';
          }
  
          # Use cached or actual file if they exists, otherwise pass request to WordPress
          location / {
                 try_files $uri $uri/ /index.php?q=$uri&amp;$args;
          }
  location ~ \.php$ {
              fastcgi_buffers 256 16k;
              fastcgi_buffer_size 128k;
              fastcgi_max_temp_file_size 0;
              fastcgi_intercept_errors on;
              include fastcgi_params;
              fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
              fastcgi_pass unix:/dev/shm/php-fpm-www.sock;
  
          }
  
          # BEGIN W3TC Browser Cache
          location ~ \.(css|htc|less|js|js2|js3|js4)$ {
      expires 31536000s;
      add_header Pragma "public";
      add_header Cache-Control "max-age=31536000, public";
      add_header X-Powered-By "W3 Total Cache/0.9.4.1";
  }
  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";
      add_header X-Powered-By "W3 Total Cache/0.9.4.1";
  }
  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 X-Powered-By "W3 Total Cache/0.9.4.1";
  }
  # END W3TC Browser Cache
  }
  </code></pre>
  
  <p><strong>Varnish:</strong></p>
  
  <p>I’ve taken the VCL files from Dreamhost’s VCL collection: <a href="https://github.com/dreamhost/varnish-vcl-collection" rel="nofollow">https://github.com/dreamhost/varnish-vcl-collection</a></p>
  
  <p>/etc/varnish/default.vcl</p>
  
  <pre><code>backend default {
          .host = "127.0.0.1";
          .port = "8080";
  }
  
  import std;
  
  include "lib/xforward.vcl";
  include "lib/cloudflare.vcl";
  include "lib/purge.vcl";
  include "lib/bigfiles.vcl";        # Varnish 3.0.3+
  #include "lib/bigfiles_pipe.vcl";  # Varnish 3.0.2
  include "lib/static.vcl";
  
  acl cloudflare {
          # set this ip to your Railgun IP (if applicable)
          # "1.2.3.4";
  }
  
  acl purge {
          "localhost";
          "127.0.0.1";
  }
  
  # Pick just one of the following:
  # (or don't use either of these if your application is "adaptive")
  # include "lib/mobile_cache.vcl";
  # include "lib/mobile_pass.vcl";
  ### WordPress-specific config ###
  # This config was initially derived from the work of Donncha Ó Caoimh:
  # http://ocaoimh.ie/2011/08/09/speed-up-wordpress-with-apache-and-varnish/
  sub vcl_recv {
          # pipe on weird http methods
          if (req.request !~ "^GET|HEAD|PUT|POST|TRACE|OPTIONS|DELETE$") {
                  return(pipe);
          }
  
          ### Check for reasons to bypass the cache!
          # never cache anything except GET/HEAD
          if (req.request != "GET" &amp;&amp; req.request != "HEAD") {
                  return(pass);
          }
          # don't cache logged-in users or authors
          if (req.http.Cookie ~ "wp-postpass_|wordpress_logged_in_|comment_author|PHPSESSID") {
                  return(pass);
          }
          # don't cache ajax requests
          if (req.http.X-Requested-With == "XMLHttpRequest") {
                  return(pass);
          }
          # don't cache these special pages
          if (req.url ~ "nocache|wp-admin|wp-(comments-post|login|activate|mail)\.php|bb-admin|server-status|control\.php|bb-login\.php|bb-reset-password\.php|register\.php") {
                  return(pass);
          }
  
          ### looks like we might actually cache it!
          # fix up the request
          set req.grace = 2m;
          set req.url = regsub(req.url, "\?replytocom=.*$", "");
  
          # Remove has_js, Google Analytics __*, and wooTracker cookies.
          set req.http.Cookie = regsuball(req.http.Cookie, "(^|;\s*)(__[a-z]+|has_js|wooTracker)=[^;]*", "");
          set req.http.Cookie = regsub(req.http.Cookie, "^;\s*", "");
          if (req.http.Cookie ~ "^\s*$") {
                  unset req.http.Cookie;
          }
  
          return(lookup);
  }
  
  sub vcl_hash {
          # Add the browser cookie only if a WordPress cookie found.
          if (req.http.Cookie ~ "wp-postpass_|wordpress_logged_in_|comment_author|PHPSESSID") {
                  hash_data(req.http.Cookie);
          }
  }
  sub vcl_fetch {
          # make sure grace is at least 2 minutes
          if (beresp.grace &lt; 2m) {
                  set beresp.grace = 2m;
          }
  
          # catch obvious reasons we can't cache
          if (beresp.http.Set-Cookie) {
                  set beresp.ttl = 0s;
          }
  
          # Varnish determined the object was not cacheable
          if (beresp.ttl &lt;= 0s) {
                  set beresp.http.X-Cacheable = "NO:Not Cacheable";
                  return(hit_for_pass);
  
          # You don't wish to cache content for logged in users
          } else if (req.http.Cookie ~ "wp-postpass_|wordpress_logged_in_|comment_author|PHPSESSID") {
                  set beresp.http.X-Cacheable = "NO:Got Session";
                  return(hit_for_pass);
  
          # You are respecting the Cache-Control=private header from the backend
          } else if (beresp.http.Cache-Control ~ "private") {
                  set beresp.http.X-Cacheable = "NO:Cache-Control=private";
                  return(hit_for_pass);
  
          # You are extending the lifetime of the object artificially
          } else if (beresp.ttl &lt; 300s) {
                  set beresp.ttl   = 300s;
                  set beresp.grace = 300s;
                  set beresp.http.X-Cacheable = "YES:Forced";
  
          # Varnish determined the object was cacheable
          } else {
                  set beresp.http.X-Cacheable = "YES";
          }
    # Avoid caching error responses
          if (beresp.status == 404 || beresp.status &gt;= 500) {
                  set beresp.ttl   = 0s;
                  set beresp.grace = 15s;
          }
  
          # Deliver the content
          return(deliver);
  }
  </code></pre>
  
  <p><strong>Wordpress</strong>
  wp-config.php</p>
  
  <pre><code>define( 'WP_MEMORY_LIMIT', '512M' );
  </code></pre>
  
  <p><strong>On W3TC, I’ve the following turned on:</strong></p>
  
  <pre><code>Page Cache (APC)
  Database Cache (APC)
  Object Cache (APC)
  Browser cache
  </code></pre>
  
  <p><strong>Cloudflare settings:</strong></p>
  
  <pre><code>Minify: Off
  Rocket Loader: Off
  Caching level: Standard
  </code></pre>
  
  <p>Kindly guide me. Kindly suggest better settings, if any.</p>