Skip to content

Instantly share code, notes, and snippets.

@sandys
Created September 4, 2012 06:40
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sandys/3617690 to your computer and use it in GitHub Desktop.
Save sandys/3617690 to your computer and use it in GitHub Desktop.
Bulletproof Wordpress installation
proxy_cache_path /var/lib/nginx/cache/staticfiles levels=1:2 keys_zone=staticfilecache:60m inactive=90m max_size=50m;
proxy_temp_path /var/lib/nginx/proxy;
proxy_connect_timeout 30;
proxy_read_timeout 120;
proxy_send_timeout 120;
map $http_cookie $logged_in {
default 0;
~wordpress_logged_in 1; # Wordpress session cookie
}
server {
listen *:80;
#server_name mattkirman.com; # Change this!
set $wordpress_auth "";
if ($uri ~ /wp-admin/) {
set $wordpress_auth wordpress_logged_in_$1;
}
if ($http_cookie ~* "wordpress_logged_in_[^=]*=([^%]+)%7C") {
set $wordpress_auth wordpress_logged_in_$1;
}
add_header Cache-Control public;
access_log /var/log/nginx.vhost.access.log main;
location / {
proxy_pass http://127.0.0.1:81/;
proxy_buffering on;
proxy_buffers 12 12k;
#proxy_redirect off;
proxy_redirect http://127.0.0.1:81/ http://$host/;
server_name_in_redirect off;
#proxy_set_header X-Real-IP $remote_addr;
#proxy_set_header X-Forwarded-For $remote_addr;
###proxy_set_header x-path $uri;
proxy_set_header Referer $http_referer;
proxy_ignore_headers Cache-Control Expires;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Cookie $http_cookie;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Server $http_host;
proxy_set_header X-Forwarded-Host $http_host;
proxy_set_header X-NginX-Proxy true;
proxy_set_header X-M-Secure "true";
proxy_set_header Host $http_host;
proxy_cache_use_stale error
timeout
invalid_header
http_500
http_502
http_504
http_404;
# 2 rules to dedicate the no caching rule for logged in users.
proxy_cache_bypass $wordpress_auth; # Do not cache the response.
proxy_no_cache $wordpress_auth; # Do not serve response from cache.
proxy_cache staticfilecache;
proxy_cache_valid 200 30m;
expires 5m;
}
location ~* \.(?:ico|css|js|gif|jpe?g|png|bmp|html) {
root /var/www;
expires max;
add_header Pragma public;
add_header Cache-Control "public, must-revalidate, proxy-revalidate";
proxy_cache staticfilecache;
}
}

Copy and paste it into a file under /etc/apt/sources.list.d/(we suggest naming the file MariaDB.list or something similar)

# MariaDB repository list - created 2012-09-04 06:29 UTC
# http://downloads.mariadb.org/mariadb/repositories/
deb http://download.nus.edu.sg/mirror/mariadb/repo/5.5/ubuntu precise main
deb-src http://download.nus.edu.sg/mirror/mariadb/repo/5.5/ubuntu precise main

sudo apt-get install nginx apache2 varnish vim-gnome
sudo apt-get install mariadb-common mariadb-server mariadb-client libmariadbclient-dev

Add the following

#/etc/apache2/ports.conf
#NameVirtualHost *:80
#Listen 80
Listen 127.0.0.1:81

and

#/etc/apache2/sites-enabled/000-default

ServerName localhost
  ServerAdmin webmaster@localhost

  DocumentRoot /var/www

  
    Options Indexes FollowSymLinks MultiViews
    AllowOverride None
    Order allow,deny
    allow from all
  

  ErrorLog ${APACHE_LOG_DIR}/error.log

  # Possible values include: debug, info, notice, warn, error, crit,
  # alert, emerg.
  LogLevel warn

  CustomLog ${APACHE_LOG_DIR}/access.log combined



sudo apt-get install libapache2-mod-php5 sudo a2enmod php5

Check memory usage

  • Top memory usage
    • ps -A --sort -rss -o comm,pmem
  • Resident memory
    • ps -ylC apache2 --sort:rss
  • All below for apache2.conf . MaxClients If your server has 1024MB of RAM and after booting the OS, you have 768MB free. If after starting up MySQL you have 700MB free and after starting up PureFTP you have 650MB free. Your Apache process RSS size is 17MB. That would mean you can run 38 Apache processes before you run out of RAM and begin to swap (your web server should never swap). Set MaxClients to 38 in this case.
  • HostnameLookups Set this to "Off" otherwise Apache will do DNS lookups on client IPs for logging purposes and this is slow.
  • AllowOverride Setting this to "None" will mean higher performance since Apache won't look for .htaccess files in each directory, HOWEVER many WordPress features (like nice URLs) require you to use .htaccess files so we need to compromise here and set this to "All" in most cases.
  • MaxRequestsPerChild After this number of requests, a child process is killed and a new one forked to replace it. This ensures that any accidental memory leakage on the part of the Apache process cannot get out of hand. Setting this to something other than 0 means that Apache processes don't live forever.
  • KeepAlive Off - we will handle keepalive on nginx

Change all dateformat to be Mariadb compliant.

find . -type f -name '*.php' -ls -exec sed -i s/0000-00-00\ 00:00:00/1000-01-01\ 00:00:00/ {} \;

CREATE DATABASE wordpress;
GRANT ALL PRIVILEGES ON wordpress.* to "wordpressuser"@"localhost" identified by "password";
flush privileges;

sudo apt-get install php5-mysql

navigate to http://localhost:81/wp-admin/install.php

#/etc/nginx/nginx.conf
keepalive_timeout 300;
# a small bash script that checks for all tables in a given database for MyMaria tables, alters/repairs them
# 
 
MYSQL="/usr/bin/mysql"
MYSQLR="/usr/bin/mysqlrepair"
MYSQL_HOST="localhost"
MYSQL_USER="root"
MYSQL_PASSWD="your_password"
MYSQL_SOCKET="/var/run/mysqld/mysqld.sock"
MYSQLCONNECT="$MYSQL -u$MYSQL_USER -p$MYSQL_PASSWD -h $MYSQL_HOST -S $MYSQL_SOCKET -A"
MYSQLREPAIR="$MYSQLR -v -F -u$MYSQL_USER -p$MYSQL_PASSWD -B" 
 
echo "Checking DB "
# Use MySQL 'SHOW DATABASES'
DATABASES="`$MYSQLCONNECT --batch -N -e "show databases like 'your_blog_db'"`" 
 
echo "Checking for InnoDB Tables "
# Loop through each instance of MySQL and check all databases in that instance
for DATABASE in $DATABASES
do
   TABLES="`$MYSQLCONNECT --batch -D $DATABASE -N -e "SELECT TABLE_NAME FROM information_schema.TABLES WHERE TABLE_SCHEMA='$DATABASE' AND TABLE_TYPE='BASE TABLE' AND ENGINE IN ('InnoDB')"`"
   for TABLE in $TABLES
   do
      echo "Altering Table : $TABLE"
      ALTERTABLES="`$MYSQLCONNECT --batch -D $DATABASE -N -e "ALTER TABLE $TABLE ENGINE=Maria"`"
      echo "Repairing Table: $TABLE"
      REPAIR_TABLE=`$MYSQLREPAIR $DATABASE --tables $TABLE`
   done
done
#/etc/mysql/my.cnf
skip-innodb
default_storage_engine  = Maria
# My installation of MariaDB on Ubuntu came like this by default.  Make sure you don't switch
# Traditional mode ON/OFF if you don't know the consequences.
sql_mode                = NO_ENGINE_SUBSTITUTION,TRADITIONAL

There are a few modules in WordPress (mostly based around the comment system and plugins such as Akismet) that use the client remote IP address. Unfortunately, with our current configuration $_SERVER['REMOTE_ADDR'] will always return 127.0.0.1.

In order to fix this we just need to set $_SERVER['REMOTE_ADDR'] to the value of the X-Forwarded-For header that we set in our Nginx config. You can do this by adding the following code to the top of your wp-config.php.

if ( ! empty( $_SERVER['X-Forwarded-For'] ) && $_SERVER["REMOTE_ADDR"] == "10.10.0.1")  {     $forwardip = explode(",", $_SERVER['X-Forwarded-For']);     $_SERVER['REMOTE_ADDR'] = $forwardip[0];}

Navigate to http://localhost:81/wp-admin/options-general.php and set Wordpress Address and *Site Address" to http://localhost:80. This is because Wordpress does a lot of redirects for canonical URL and you will see a lot of localhost:81 based requests if you dont.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment