browse by category or date


Around three weeks ago, GoDaddy (where I was hosting this blog) suffered a downtime. Although they’re back online within 48 hours, something bad had happened to shared server that hosted my blog. I don’t really know the details, but I only know the outcome. This blog become super slow, intermittently spitted out timeout error, and frequently crashed (I need to terminate the web process from Hosting Control Panel). Being a good customer, I raised ticket and gave them chance to resolve this problem. But after two weeks with no improvement, my patience ran out. So I spent last week moving out this blog to Amazon EC2.

Why Amazon EC2? I can’t really elaborate why. But I can say that it’s a weird logic combination of:

  1. The Amazon brand
  2. I’ve seen many cool projects are hosted in Amazon EC2
  3. Free tier usage!!
  4. Learn something new 😀

Using few guides that I found on the Net, I managed to achieved the setup that I want. It wasn’t a smooth sailing though. In the end, I learned something new and now I write this guide hopefully will get you on a smooth sailing migrating your WordPress blog from GoDaddy to Amazon EC2.

Alright, let’s get dirty!!

After you login to your Amazon Web Service account, you’ll have something like below. Click EC2.


Now on your left screen you’ll have something like this:


You might need to change the location of your data center. For me, I changed it to Asia Pacific (Singapore) because majority of my blog readers come from this region.

Next, click Instance.


Then, click Launch Instance button. You will be prompted with 3 choices:

  1. Classic Wizard: You will be required to configure in details your EC2 instance.
  2. Quick Launch Wizard: You can choose from a number of commonly used OS + Software setup.
  3. AWS Marketplace: You’ll have myriads of setups available to choose. These setups already come with the prepackaged software.

For now, let’s just select Quick Launch Wizard and pick Ubuntu Server 11.10.


Before we can continue, we need to give name to our instance. We also need to create a new Key Pair and download the Key Pair. We are going to need Key Pair to establish SSH connection later on.


When you clicked the download button, you’ll receive a [Name].pem. In my example, it’s wpkey.pem. What to do now is to click the Continue button. After that, we will have the summary screen detailing our setup.


Next, click Launch button to have our instance running.

Assign IP Address

By default, our instance will only have internal IP. To make the instance have an external IP. We need to use Elastic IP. On the EC2 Navigation Panel, click Elastic IPs:


Click Allocate New Address button


Click Yes, Allocate. After a while, we will see the new IP Address in the table. Select the row, then click Associate Address button.


That’s it for now. Let’s continue to the next topic about establishing SSH Connection.

There are many tools that enables you to have SSH session with your EC2 instance. But I won’t review them all here, I’ll just discuss my choice of tools. Which are:

  1. PuttyGen: This tool is used to generate private key from previously downloaded Key-Pair file (e.g. wpkey.pem)
  2. Putty: This tool is used to establish SSH connection.
  3. FileZilla: This tool is an FTP client that capable connecting to SFTP protocol

Putty and PuttyGen can be downloaded HERE. Filezilla’s download page is HERE.

Generate Private Key

Run PuttyGen:

Click Load button, select the .pem file.


Change the Number of bits in a generated key to 2048. Click the Save private key button and name the file wpkey.ppk

Establish SSH Connection

Run Putty:

Go to Connection -> SSH -> Auth. We need to set the Private key for authentication with the .ppk file that we generate before. Click browse, and find the file. After that go to Session

Set the Hostname (or IP address) with the IP address obtained in previous page. Or you could use the Public DNS value shown in your Instance detail. Leave the default value (22) for Port. Under the Saved Session, give name to this connection (e.g. ec2), then click Save. Finally, click Open to initiate connection.

Putty will prompted you whether this connection can be trusted:

Click Yes. To login, use ubuntu as username.

Congratulations! You are now connected to your Ubuntu server through SSH.

Establish SFTP Connection

Run FileZilla, go to Edit -> Settings. In the Settings window, go to Connection -> SFTP. Press Add keyfile button. Select the same .ppk file as the one used by Putty.


Next, go to Sites Manager. Add connection to your instance by following configuration similar to below and save it for future usage.

Alright, so far so good. We now can connect to our instance and transfer files. Next, let’s start configuring our server for WordPress.

To install MySQL, run the following command:

sudo -i
apt-get update
apt-get install mysql-server

MySQL will prompt you to enter the root password. Write it down, or save the password somewhere in case you forget.

Once the installation completed, login to MySQL

mysql -u root -p

You’ll be prompted for password. Use the same password as you set in installation step.

At the mysql> prompt, run these commands, replacing NEW_PASSWORD with a password of your own

CREATE DATABASE wordpress;
GRANT ALL PRIVILEGES ON wordpress.* 
TO "wp_user"@"localhost" IDENTIFIED BY "NEW_PASSWORD";
FLUSH PRIVILEGES;
EXIT

Now let’s create a custom configuration file to cater the RAM size limitation in EC2 Micro.

vim /etc/mysql/conf.d/ec2.cnf

Press i to make Vim enter edit mode. Paste below code by mouse right-click

[mysqld]
key_buffer = 12M
query_cache_size = 0
table_cache = 32

ignore_builtin_innodb
default_storage_engine=MyISAM

Press Esc key to return to read mode. Press !wq to save the file.

Alright, that’s all for MySQL portion.

Run the following commands to install PHP FPM:

apt-get install php5-fpm php5-cli php-apc php5-suhosin php5-curl php5-gd php5-intl php5-mcrypt php-gettext php5-mysql php5-sqlite

After the installation completed, we’re going to customize it. But first, let’s backup the config files.

mkdir /root/backup
mkdir /root/backup/etc
mkdir /root/backup/etc/php5
mkdir /root/backup/etc/php5/conf.d
mkdir /root/backup/etc/php5/fpm
mkdir /root/backup/etc/php5/fpm/pool.d
cp /etc/php5/conf.d/apc.ini /root/backup/etc/php5/conf.d
cp /etc/php5/fpm/php.ini /root/backup/etc/php5/fpm
cp /etc/php5/fpm/pool.d/www.conf /root/backup/etc/php5/fpm/pool.d

Now let’s modify /etc/php5/conf.d/apc.ini. Replace its content with:

[APC]
extension=apc.so
apc.enabled=1
apc.shm_segments=1
apc.shm_size=16
apc.ttl=7200
apc.user_ttl=7200
apc.num_files_hint=1024
apc.mmap_file_mask=/tmp/apc.XXXXXX
apc.max_file_size = 1M
apc.post_max_size = 1000M
apc.upload_max_filesize = 1000M
apc.enable_cli=0
apc.rfc1867=0

Next, let’s modify /etc/php5/fpm/php.ini. Append the following text:

[apc]
apc.write_lock = 1
apc.slam_defense = 0

Lastly, let’s modify /etc/php5/fpm/pool.d/www.conf. Match the content with below configuration:

user = nginx
group = nginx
listen = /dev/shm/php-fpm-www.sock
listen.owner = nginx
listen.group = nginx
listen.mode = 0666

Let’s now restart the service

service php5-fpm restart

To install Nginx, we will follow the installation guide found HERE.
First, we will download the Nginx signing key and add it to the apt program keyring

cd /tmp
wget http://nginx.org/keys/nginx_signing.key
apt-key add nginx_signing.key

Now we’re ready to install Nginx

deb http://nginx.org/packages/ubuntu/ oneiric nginx
deb-src http://nginx.org/packages/ubuntu/ oneiric nginx
apt-get update
apt-get install nginx

Next, let’s configure Nginx. But first, as usual, let’s backup the configuration files first.

mkdir /root/backup/etc/nginx
cp /etc/nginx/nginx.conf /root/backup/etc/nginx
cp /etc/nginx/php.conf /root/backup/etc/nginx

Replace the content of /etc/nginx/nginx.conf with:

user nginx;
worker_processes 1;
pid /var/run/nginx.pid;

events {
	worker_connections 1024;
	# multi_accept on;
}

http {

	##
	# Basic Settings
	##

	sendfile on;
	tcp_nopush on;
	tcp_nodelay on;
	keepalive_timeout 65;
	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;

	##
	# Gzip Settings
	##

	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;

	##
	# nginx-naxsi config
	##
	# Uncomment it if you installed nginx-naxsi
	##

	#include /etc/nginx/naxsi_core.rules;

	##
	# nginx-passenger config
	##
	# Uncomment it if you installed nginx-passenger
	##
	
	#passenger_root /usr;
	#passenger_ruby /usr/bin/ruby;

	##
	# Virtual Host Configs
	##

	include /etc/nginx/conf.d/*.conf;
	include /etc/nginx/sites-enabled/*;
	
	port_in_redirect off;
}

Replace the content of /etc/nginx/php.conf with:

# Route all requests for non-existent files to index.php
location / {
	try_files $uri $uri/ /index.php$is_args$args;
}

# Pass PHP scripts to php-fastcgi listening on port 9000
location ~ \.php$ {

	# Zero-day exploit defense.
	# http://forum.nginx.org/read.php?2,88845,page=3
	# Won't work properly (404 error) if the file is not stored on
	# this server,  which is entirely possible with php-fpm/php-fcgi.
	# Comment the 'try_files' line out if you set up php-fpm/php-fcgi
	# on another machine.  And then cross your fingers that you won't get hacked.
	try_files $uri =404;

	include fastcgi_params;

	# Keep these parameters for compatibility with old PHP scripts using them.
	fastcgi_param PATH_INFO $fastcgi_path_info;
	fastcgi_param PATH_TRANSLATED $document_root$fastcgi_path_info;
	fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;

	# Some default config
	fastcgi_connect_timeout        20;
	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;
	fastcgi_intercept_errors    on;
	fastcgi_ignore_client_abort off;
	fastcgi_pass unix:/dev/shm/php-fpm-www.sock;

}
# PHP search for file Exploit:
# The PHP regex location block fires instead of the try_files block. Therefore we need
# to add "try_files $uri =404;" to make sure that "/uploads/virusimage.jpg/hello.php"
# never executes the hidden php code inside virusimage.jpg because it can't find hello.php!
# The exploit also can be stopped by adding "cgi.fix_pathinfo = 0" in your php.ini file.

Now, let’s create a specific configuration file for our site (e.g. /etc/nginx/available-sites/mysite.conf). You need to change every word sodeve.net found below into the name of your blog URL.

server {
	listen 80;
	server_name www.sodeve.net sodeve.net;
	root /var/www/sodeve.net/public;
	index index.html index.htm index.php;

	access_log  /var/www/sodeve.net/access.log;
	error_log  /var/www/sodeve.net/error.log;

	# Directives to send expires headers and turn off 404 error logging.
	location ~* \.(js|css|png|jpg|jpeg|gif|ico)$ {
		expires max;
		log_not_found off;
		access_log off;
	}

	location = /favicon.ico {
		log_not_found off;
		access_log off;
	}

	location = /robots.txt {
		allow all;
		log_not_found off;
		access_log off;
	}

	## Disable viewing .htaccess & .htpassword
	location ~ /\. { deny  all; access_log off; log_not_found off; }

	include /etc/nginx/php.conf;
	#Download
	#rewrite ^/downloadz/(.*)$ /wp-content/plugins/download-monitor/download.php?id=$1 last;
	
	# BEGIN W3TC Minify cache
location ~ /wp-content/w3tc/min.*\.js$ {
    types {}
    default_type application/x-javascript;
    expires modified 31536000s;
    add_header X-Powered-By "W3 Total Cache/0.9.2.4";
    add_header Vary "Accept-Encoding";
    add_header Pragma "public";
    add_header Cache-Control "max-age=31536000, public, must-revalidate, proxy-revalidate";
}
location ~ /wp-content/w3tc/min.*\.css$ {
    types {}
    default_type text/css;
    expires modified 31536000s;
    add_header X-Powered-By "W3 Total Cache/0.9.2.4";
    add_header Vary "Accept-Encoding";
    add_header Pragma "public";
    add_header Cache-Control "max-age=31536000, public, must-revalidate, proxy-revalidate";
}
location ~ /wp-content/w3tc/min.*js\.gzip$ {
    gzip off;
    types {}
    default_type application/x-javascript;
    expires modified 31536000s;
    add_header X-Powered-By "W3 Total Cache/0.9.2.4";
    add_header Vary "Accept-Encoding";
    add_header Pragma "public";
    add_header Cache-Control "max-age=31536000, public, must-revalidate, proxy-revalidate";
    add_header Content-Encoding gzip;
}
location ~ /wp-content/w3tc/min.*css\.gzip$ {
    gzip off;
    types {}
    default_type text/css;
    expires modified 31536000s;
    add_header X-Powered-By "W3 Total Cache/0.9.2.4";
    add_header Vary "Accept-Encoding";
    add_header Pragma "public";
    add_header Cache-Control "max-age=31536000, public, must-revalidate, proxy-revalidate";
    add_header Content-Encoding gzip;
}
# END W3TC Minify cache
	# BEGIN W3TC Page Cache cache
location ~ /wp-content/w3tc/pgcache.*html$ {
    expires modified 3600s;
    add_header X-Powered-By "W3 Total Cache/0.9.2.4";
    add_header Vary "Accept-Encoding, Cookie";
    add_header Pragma "public";
    add_header Cache-Control "max-age=3600, public, must-revalidate, proxy-revalidate";
}
location ~ /wp-content/w3tc/pgcache.*gzip$ {
    gzip off;
    types {}
    default_type text/html;
    expires modified 3600s;
    add_header X-Powered-By "W3 Total Cache/0.9.2.4";
    add_header Vary "Accept-Encoding, Cookie";
    add_header Pragma "public";
    add_header Cache-Control "max-age=3600, public, must-revalidate, proxy-revalidate";
    add_header Content-Encoding gzip;
}
# END W3TC Page Cache cache
	# BEGIN W3TC Browser Cache
gzip on;
gzip_types text/css application/x-javascript text/x-component text/richtext image/svg+xml text/plain text/xsd text/xsl text/xml image/x-icon;
location ~ \.(css|js|htc)$ {
    expires 31536000s;
    add_header Pragma "public";
    add_header Cache-Control "max-age=31536000, public, must-revalidate, proxy-revalidate";
    add_header X-Powered-By "W3 Total Cache/0.9.2.4";
}
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, must-revalidate, proxy-revalidate";
    add_header X-Powered-By "W3 Total Cache/0.9.2.4";
}
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|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|xla|xls|xlsx|xlt|xlw|zip)$ {
    expires 31536000s;
    add_header Pragma "public";
    add_header Cache-Control "max-age=31536000, public, must-revalidate, proxy-revalidate";
    add_header X-Powered-By "W3 Total Cache/0.9.2.4";
}
# END W3TC Browser Cache
	# BEGIN W3TC Minify core
rewrite ^/wp-content/w3tc/min/w3tc_rewrite_test$ /wp-content/w3tc/min/index.php?w3tc_rewrite_test=1 last;
set $w3tc_enc "";
if ($http_accept_encoding ~ gzip) {
    set $w3tc_enc .gzip;
}
if (-f $request_filename$w3tc_enc) {
    rewrite (.*) $1$w3tc_enc break;
}
rewrite ^/wp-content/w3tc/min/(.+\.(css|js))$ /wp-content/w3tc/min/index.php?file=$1 last;
# END W3TC Minify core
	# BEGIN W3TC Page Cache core
rewrite ^(.*\/)?w3tc_rewrite_test$ $1?w3tc_rewrite_test=1 last;
set $w3tc_rewrite 1;
if ($request_method = POST) {
    set $w3tc_rewrite 0;
}
if ($query_string != "") {
    set $w3tc_rewrite 0;
}
if ($http_host != "sodeve.net") {
    set $w3tc_rewrite 0;
}
set $w3tc_rewrite2 1;
if ($request_uri !~ \/$) {
    set $w3tc_rewrite2 0;
}
if ($request_uri ~* "(sitemap(_index)?\.xml(\.gz)?|[a-z0-9_\-]+-sitemap([0-9]+)?\.xml(\.gz)?)") {
    set $w3tc_rewrite2 1;
}
if ($w3tc_rewrite2 != 1) {
    set $w3tc_rewrite 0;
}
set $w3tc_rewrite3 1;
if ($request_uri ~* "(\/wp-admin\/|\/xmlrpc.php|\/wp-(app|cron|login|register|mail)\.php|wp-.*\.php|index\.php|download.php|securimage.php)") {
    set $w3tc_rewrite3 0;
}
if ($request_uri ~* "(wp\-comments\-popup\.php|wp\-links\-opml\.php|wp\-locations\.php)") {
    set $w3tc_rewrite3 1;
}
if ($w3tc_rewrite3 != 1) {
    set $w3tc_rewrite 0;
}
if ($http_cookie ~* "(comment_author|wp\-postpass|wordpress_\[a\-f0\-9\]\+|wordpress_logged_in)") {
    set $w3tc_rewrite 0;
}
if ($http_user_agent ~* "(W3\ Total\ Cache/0\.9\.2\.4)") {
    set $w3tc_rewrite 0;
}
set $w3tc_ua "";
set $w3tc_ref "";
set $w3tc_ssl "";
set $w3tc_enc "";
if ($http_accept_encoding ~ gzip) {
    set $w3tc_enc _gzip;
}
set $w3tc_ext "";
if (-f "$document_root/wp-content/w3tc/pgcache/$request_uri/_index$w3tc_ua$w3tc_ref$w3tc_ssl.html$w3tc_enc") {
    set $w3tc_ext .html;
}
if (-f "$document_root/wp-content/w3tc/pgcache/$request_uri/_index$w3tc_ua$w3tc_ref$w3tc_ssl.xml$w3tc_enc") {
    set $w3tc_ext .xml;
}
if ($w3tc_ext = "") {
  set $w3tc_rewrite 0;
}
if ($w3tc_rewrite = 1) {
    rewrite .* "/wp-content/w3tc/pgcache/$request_uri/_index$w3tc_ua$w3tc_ref$w3tc_ssl$w3tc_ext$w3tc_enc" last;
}
# END W3TC Page Cache core
	# BEGIN W3TC Skip 404 error handling by WordPress for static files
if (-f $request_filename) {
    break;
}
if (-d $request_filename) {
    break;
}
if ($request_uri ~ "(robots\.txt|sitemap(_index)?\.xml(\.gz)?|[a-z0-9_\-]+-sitemap([0-9]+)?\.xml(\.gz)?)") {
    break;
}
if ($request_uri ~* \.(css|js|htc|html|htm|rtf|rtx|svg|svgz|txt|xsd|xsl|xml|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|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|xla|xls|xlsx|xlt|xlw|zip)$) {
    return 404;
}
# END W3TC Skip 404 error handling by WordPress for static files
}

Alright, we’re almost done. What’s left now is just migrating our existing existing WordPress files from GoDaddy to our new EC2 instance.

To migrate WordPress, we basically need to do two things:

  1. Migrate MySQL Database
  2. Migrate WordPress files
  3. Change DNS Record

Migrate MySQL Database

First, let’s backup the DB. The easiest way to do this is using WP-DB Manager. Install this extension first if you don’t have it.

WP-DB Manager backup screen
Once you backup it, use FileZilla to retrive the backup file. Because we are going to import the .sql file to database we created in step 4, it important to add this at the beginning of the file:

use wordpress;

Save the .sql file and then transfer it to our EC2 instance (put it in /tmp/ folder).
Now, in Putty, let’s import the backup file into MySQL.

mysql -u wp_user -p < /tmp/1348812291_-_wordpress.sql

Migrate WordPress files

First let’s create the directory to store the WordPress files

mkdir /var/www
mkdir /var/www/sodeve.net/
mkdir /var/www/sodeve.net/public
chown nginx:nginx /var/www/sodeve.net/public

Now we can copy all files from the old WordPress site at GoDaddy’s into our EC2 instance

Change DNS Record

To host our DNS we need to move our NameServers address to CloudFlare’s NameServers. First, login to your GoDaddy Domain Control Panel.

Click Set Nameservers and change the name servers to:

  1. DAVE.NS.CLOUDFLARE.COM
  2. PAM.NS.CLOUDFLARE.COM

Setting up DNS in CloudFlare is really simple so I’ll give it a pass. 😀
Alright, that’s basically all you need to do to migrate your blog from GoDaddy’s shared host to Amazon EC2.

I hope it helps. If you encountered problem following this example, please drop a comment or two below.

Cheers!

About Hardono

Howdy! I'm Hardono. I am working as a Software Developer. I am working mostly in Windows, dealing with .NET, conversing in C#. But I know a bit of Linux, mainly because I need to keep this blog operational. I've been working in Logistics/Transport industry for more than 11 years.

Possibly relevant:

Today I found out a secret. We were travelling to DHL Warehouse at Alps Avenue when it was happened. As we are driving along Changi Coast Road, my colleague started to accelerate to around 80 KM/h. If I remember correctly, the speed limit on that stretch of road is 70 KM/h. Mind you that Changi Coast Road is a long straight road next to Changi Airport’s runaway. So it’s very easy to get tempted to over speed.

Then we saw it. Almost every cars that driving in the opposite direction signaled us with their high-beam light. It took us 2-3 cars before we notice their signal. Our first thought was we ran over something, and that something is stuck in front of the car. So my friend immediately pressed the hazard-sign light and slowed down. We stopped and get out of the car and checked the front side of the car. Hmm… weird, nothing is stuck there.

So we continue to drive. Not more than 100 meters from where we stopped, we found out the truth. There was Traffic Police officers conducting speed check! We saw one car already kena pulled over.

Instantly we all laughed realizing that the signals were meant “Slow down, there’s traffic Mata ahead”.

Of course I’m not advocating over speeding. The rules in Singapore is clear. If the speed limit is 60 KM/h, you can speed at most 69 KM/h. If your speed is 10-19 KM/h above the speed limit and kena, you’ll receive warning letters (three times at most, then they’ll summon you to court). If your speed is more than 19 KM/h above the speed limit and kena, you’ll need to pay a visit to court.

Drive/ride safely folks!

Changi Coast Road

Changi Coast Road

About Hardono

Howdy! I'm Hardono. I am working as a Software Developer. I am working mostly in Windows, dealing with .NET, conversing in C#. But I know a bit of Linux, mainly because I need to keep this blog operational. I've been working in Logistics/Transport industry for more than 11 years.

Possibly relevant:

I just found out this trick to make your WordPress image upload automatically shown in Lightbox. In order for this trick to works, you need to install NextGen Gallery plugin into your WordPress installation. This doesn’t mean we upload the image into NextGen Gallery, then show the gallery in the post.

Normally, when after we uploaded image to WordPress we are going to see this dialog:


After added the image into Post, we are going to see this added into editor:

<a href="http://sodeve.net/wp-content/uploads/2012/10/ec2.8.png">
  <img src="http://sodeve.net/wp-content/uploads/2012/10/ec2.8-300x178.png"
  alt="" title="ec2.8" width="300" height="178"
  class="aligncenter size-medium wp-image-2980" />
</a>

In above code, whenever a visitor clicked the thumbnail, the page will load the image in full size. Which is bad, because when the visitor wants to continue reading, he/she needs to go back to the page.

To make the image shown in lightbox, we just need to add class shutterset_set_X into the image. Please change the X into a number. This way you can group the images in lightbox accordingly.

<a href="http://sodeve.net/wp-content/uploads/2012/10/ec2.8.png"
class="shutterset_set_1">
  <img src="http://sodeve.net/wp-content/uploads/2012/10/ec2.8-300x178.png"
  alt="" title="ec2.8" width="300" height="178"
  class="aligncenter size-medium wp-image-2980" />
</a>

That’s all folks! If the trick is useful, please share it away. If it doesn’t work on your blog, drop a comment and we’ll see what we can do.

Cheers!

About Hardono

Howdy! I'm Hardono. I am working as a Software Developer. I am working mostly in Windows, dealing with .NET, conversing in C#. But I know a bit of Linux, mainly because I need to keep this blog operational. I've been working in Logistics/Transport industry for more than 11 years.

Possibly relevant: