Secure WordPress – Limit Logins to Specified Countries with CloudFlare

Posted April 9th, 2013 in PHP by Flynsarmy

As I’m sure is the case with just about every WordPress user, my site is constantly hit with failed login attempts from bots – usually originating from other countries. I had the idea last night to implement country-based login restrictions using CloudFlare’s IP Geolocation server variable.

My goal was to redirect redirect all users visiting the login page from countries other than Australia to the home page. This occurs before a login attempt can even be made.

Here’s how it’s done:

  • Go into CloudFlare settings and make sure the IP Geolocation option is turned onMake sure CloudFlare IP Geolocation is turned on
  • Add the following to your active themes functions.php file
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    
    // Limit login countries - requires cloudflare
    add_action( 'wp_authenticate', 'login_failed', 1);
    function login_failed()
    {
    	if ( !isset($_SERVER["HTTP_CF_IPCOUNTRY"]) )
    		return;
     
    	if ( !in_array($_SERVER["HTTP_CF_IPCOUNTRY"], array('AU')) )	{
    		wp_redirect( home_url() );
    		exit;
    	}
    }

To test simply use a country other than your own. Once it’s confirmed working switch to your country and you’re good to go!

Read More »

How to Drastically Speed Up WordPress with Redis

Posted March 16th, 2013 in Database, PHP by Flynsarmy

I recently came across a tutorial on sitting Redis infront of WordPress allowing for insanely fast page generation. I gave it a try and it really works, in fact I’m now using it on this very site! The best part however is the fact that the script requires absolutely no modification to your existing WordPress site save for 1 line of htaccess. Truly amazing.

Below I’ll detail my slightly modified version of Jim’s script along with some metrics.

 

Firstly, What is Redis and what will it do for me?

The Redis website describes Redis as

… an open source, BSD licensed, advanced key-value store. It is often referred to as a data structure server since keys can contain stringshasheslistssets and sorted sets.

What does this mean? Essentially it’s Memcached but more useful. Redis stores key-value pairs in memory and spits them out when requested. Unlike Memcached it has built in persistence but what’s most important to us is that it’s fast – very fast.

We’ll be using Redis to speed up our site by loading cached pages from it directly without even booting up WordPress. This will save a large amount of page generation time and get out site infront of our users’ eyeballs faster.

 

Exactly how much faster are we talking?

In my very unscientific tests, loading www.flynsarmy.com a bunch of times resulted in the following:

Before (Secs) After (Secs)
1.556
0.468
0.494
0.498
0.492
0.514
0.499
0.511
0.499
0.02001
0.00896
0.00883
0.00959
0.01472
0.00916
0.00915
0.00756
0.01989

As you can see from the table above this equates to a 20x to 50x speed increase and that was WITH W3 Total Cache installed! Results of course may vary but I think you get the picture.

Read More »

Edit WordPress Posts from the Front End

Posted March 5th, 2013 in PHP by Flynsarmy

I have a project coming up involving editing WordPress posts from the front end of the site. There are a bunch of plugins that let you do this the coolest of which seems to be Front-end Editor but I wanted to come up with my own solution. Luckily it turned out to be surprisingly quick and painless!

For this tutorial I’ll be editing the cafe custom post type from my last post.

Read More »

Adding Custom Post Types, Fields and Taxonomies in WordPress

Posted March 5th, 2013 in PHP by Flynsarmy

This information is pretty readily available on the internet but I figured I’d make my own post for safe keeping. I’ll be adding a custom post type (Cafes) with 3 custom fields (Website, Address and Phone) and a custom taxonomy (Countries).

The following code all goes in your functions.php file.

 

Custom Post Types

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
/**
 * Add Cafe post type
 */
function create_cafe_post_type() {
	register_post_type( 'cafes',
		array(
			'labels' => array(
				'name' => 'Cafes',
				'singular_name' => 'Cafe',
				'add_new' => 'Add New',
				'add_new_item' => 'Add New Cafe',
				'edit_item' => 'Edit Cafe',
				'new_item' => 'New Cafe',
				'view_item' => 'View Cafe',
				'search_items' => 'Search Cafes',
				'not_found' =>  'Nothing Found',
				'not_found_in_trash' => 'Nothing found in the Trash',
				'parent_item_colon' => ''
			),
			'public' => true,
			'publicly_queryable' => true,
			'show_ui' => true,
			'query_var' => true,
			//'menu_icon' => get_stylesheet_directory_uri() . '/yourimage.png',
			'rewrite' => true,
			'capability_type' => 'post',
			'hierarchical' => false,
			'menu_position' => null,
			'supports' => array('title','editor','thumbnail')
		)
	);
}
add_action( 'init', 'create_cafe_post_type' );

 

Custom Taxonomies

Taxonomies are basically tags for your post types. Add the following below the register_post_type call above:

1
2
3
4
5
6
7
//Cafe countries taxonomy
register_taxonomy("countries", array("cafes"), array(
	"hierarchical" => false,
	"label" => "Countries",
	"singular_label" => "Country",
	"rewrite" => true
));

 

Custom Fields

This is the hardest of the lot. It’s not really hard though, just alot of HTML to make the meta boxes. You can put any HTML you like in meta boxes, I just like to keep mine looking like standard WP fields:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
<?php
/**
 * Add cafe custom fields
 */
function add_cafe_meta_boxes() {
	add_meta_box("cafe_contact_meta", "Contact Details", "add_contact_details_cafe_meta_box", "cafes", "normal", "low");
}
function add_contact_details_cafe_meta_box()
{
	global $post;
	$custom = get_post_custom( $post->ID );
 
	?>
	<style>.width99 {width:99%;}</style>
	<p>
		<label>Address:</label><br />
		<textarea rows="5" name="address" class="width99"><?= @$custom["address"][0] ?></textarea>
	</p>
	<p>
		<label>Website:</label><br />
		<input type="text" name="website" value="<?= @$custom["website"][0] ?>" class="width99" />
	</p>
	<p>
		<label>Phone:</label><br />
		<input type="text" name="phone" value="<?= @$custom["phone"][0] ?>" class="width99" />
	</p>
	<?php
}
/**
 * Save custom field data when creating/updating posts
 */
function save_cafe_custom_fields(){
  global $post;
 
  update_post_meta($post->ID, "address", @$_POST["address"]);
  update_post_meta($post->ID, "website", @$_POST["website"]);
  update_post_meta($post->ID, "phone", @$_POST["phone"]);
}
add_action( 'admin_init', 'add_cafe_meta_boxes' );
add_action( 'save_post', 'save_cafe_custom_fields' );

 

Custom Templates

You may want your new post type to look different from standard posts. This can be done using custom templates. The WordPress codex cover custom post type templates nicely but I’ll quickly go over what I did for completeness. I’m using twentytwelve theme in my demo but this should apply for most themes.

  1. Duplicate single.php into single-<post type>.php
  2. Replace
    <?php get_template_part( 'content', get_post_format() ); ?>

    with

    <?php get_template_part( 'content', '<post type>' ); ?>
  3. Duplicate content.php into content-<post type>.php and modify however you like

 

Conclusion

And we’re done! Always remember to check the WordPress documentation for the various functions involved if you want to customize. Lots of useful information in there.

Read More »

How to Load WordPress Multi-Site in a script

Posted February 12th, 2013 in PHP by Flynsarmy

This morning I was loading WPMU in a custom script and all get_option() lookups were returning the details for the main blog despite being in a switch_to_blog() block. After a bit of debugging it turns out this was due to get_option() caching its values from the main blog and never expiring its cache when switching. There’s a pretty easy action you can use to fix this:

1
2
3
4
5
6
7
8
function switch_to_blog_cache_clear( $blog_id, $prev_blog_id = 0 ) {
    if ( $blog_id === $prev_blog_id )
        return;
 
	wp_cache_delete( 'notoptions', 'options' );
	wp_cache_delete( 'alloptions', 'options' );
}
add_action( 'switch_blog', 'switch_to_blog_cache_clear', 10, 2 );

Below is a script in its entirety for loading WPMU, switching to blog 31 and grabbing its name:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
<?php
 
$_SERVER['HTTP_HOST'] = 'mypolicedev.com';
 
ini_set('display_errors', true);
 
/**
 * BEGIN LOAD WORDPRESS
 */
function find_wordpress_base_path() {
	$dir = dirname(__FILE__);
	do {
		//it is possible to check for other files here
		if( file_exists($dir."/wp-config.php") ) {
			return $dir;
		}
	} while( $dir = realpath("$dir/..") );
	return null;
}
 
define( 'BASE_PATH', find_wordpress_base_path()."/" );
global $wp, $wp_query, $wp_the_query, $wp_rewrite, $wp_did_header;
require(BASE_PATH . 'wp-load.php');
 
function switch_to_blog_cache_clear( $blog_id, $prev_blog_id = 0 ) {
	if ( $blog_id === $prev_blog_id )
		return;
 
	wp_cache_delete( 'notoptions', 'options' );
	wp_cache_delete( 'alloptions', 'options' );
}
add_action( 'switch_blog', 'switch_to_blog_cache_clear', 10, 2 );
 
/**
 * END LOAD WORDPRESS
 */
 
switch_to_blog( 31 );
var_dump(get_bloginfo('name'));
restore_current_blog();

Read More »

How to move WPMU domains

Posted May 31st, 2012 in Database, PHP by Flynsarmy

Here’s a very quick set of SQL snippets for updating a WPMU domain. This might be useful when building a site on a development domain before moving to a production one later on.

Firstly update the wp_blogs table:

1
UPDATE wp_blogs SET domain='newdomain.com';

There will be a bunch of wp_options and wp_posts tables – one per site. Find all the tables:

1
2
SHOW TABLES LIKE "%_options";
SHOW TABLES LIKE "%_posts";

and for each table, perform the following query:

1
2
3
4
#_options tables
UPDATE <tablename> SET option_value=REPLACE(option_value, 'http://olddomain.com', 'http://newdomain.com');
#_posts tables
UPDATE <tablename> SET post_content=REPLACE(post_content, 'http://olddomain.com', 'http://newdomain.com');

This was enough to get the sites working for me. Additional tweaks are probably required afterwards – if you find anything let me know in the comments below.

Read More »

Change default ‘From’ email name from www-data in PHP

Posted April 19th, 2012 in Linux, PHP by Flynsarmy

I’ve been seeing up an Amazon EC-2 server with Debian Squeeze and used tasksel to install Web Server and Mail Server. Like all things debian, this worked pretty well after the installation completed and everything ‘just worked’ however I wasn’t happy with the default from name and email address assigned to emails sent by PHP – www-data <[email protected]>.

I discovered a quick and simple fix to change these defaults for all mail sent with PHP:

Open /etc/php5/apache2/php.ini and set

sendmail_path = '/usr/sbin/sendmail -t -i [email protected] -Fno-reply'

You can see a list of sendmail arguments and what they do here.

Restart apache and you’re good to go:

sudo service apache2 restart

Read More »

Use Apache Alongside Nginx Without Requiring a Subdomain

Posted March 18th, 2012 in Linux, PHP by Flynsarmy

I’ve been looking for ways to speed up my site recently and came across this interesting article on seamlessly integrating nginx with Apache to handle asset files without requiring a CDN subdomain. This works by checking the requests file extension for .js, .jpg, .pdf etc and if not found, proxies the request to Apache and serves the results.

Benefits

You won’t need to modify all your pages/posts updating asset locations to point to a subdomain! Everything will ‘just work’.

Issues/Drawbacks

There are 2 issues I’ve found with this setup:

  • Because Apache is now running on port 8080, your mod_rewrite redirects will now redirect to that port. You won’t be able to use RedirectMatch anymore, however below is the solution I came up with:
    RewriteEngine on
    RewriteRule ^foo.php$ http://%{HTTP_HOST}:80/bar.php [R=301,L]
  • You can no longer use .htaccess redirects for any asset files nginx is serving. Instead, use nginx redirects. Below is an example:
    rewrite ^/foo.jpg$ http://173.255.221.210/bar.jpg permanent;

    For more information on nginx redirects, see the official documentation.

Read More »

PHP WebSocket Chat Application 2.0

Posted February 15th, 2012 in Javascript, PHP by Flynsarmy

Even though it hasn’t worked for quite some time now, my previous PHP WebSocket chat application has garnered quite a bit of attention and is still one of my most heavily trafficked posts. As a result I thought I’d provide you all with a working script as of Feb 15, 2012. Also, because I’m your typical lazy developer, I’ll be building on top of other peoples’ work – most notably PHPWebSocket.

You can download the final script here. According to the Wikipedia article on WebSockets, it should work in IE10+, Firefox 7+ and Chrome 14+. Personally I tested with Chrome 17.0.963.46 and Firefox 10.0.1.

I want to stress that this tutorial is designed to be extremely basic and as such does not give alot of functionality out of the box (but provides all the tools required to add more). It’s simply a working example of a PHP-based WebSocket server.

Read More »

LemonStand: Maximum function nesting level of ’100′ reached

Posted January 12th, 2012 in Lemonstand, PHP by Flynsarmy

Last night I was getting a strange error in LemonStand administration area when attempting to edit a product or category:

Fatal error: Maximum function nesting level of ’100′ reached, aborting! in /path/to/phproad/modules/phpr/helpers/phpr_inflector.php on line 322

If you’re also experiencing this error, it’s caused by XDebug and can be solved by adding

1
ini_set('xdebug.max_nesting_level', 200);

to your config/config.php file.

Thanks to Aleksey Bobkov and EHLOVader for their help in solving this issue. Here is the official LemonStand forum thread on the issue.

Read More »

Page 1 of 512345