diff --git a/ajaxblocks.install b/ajaxblocks.install
index eb52e79..bd58ca2 100644
--- a/ajaxblocks.install
+++ b/ajaxblocks.install
@@ -61,6 +61,18 @@ function ajaxblocks_schema() {
         'length' => 1000,
         'default' => '',
       ),
+      'enable_client_cache' => array(
+        'description' => 'Boolean indicating whether the block should be cached by the client\'s browser.',
+        'type' => 'int',
+        'not null' => TRUE,
+        'default' => 0,
+      ),
+      'maximum_client_cache' => array(
+        'description' => 'Time a client\'s cache of a block is valid.',
+        'type' => 'int',
+        'not null' => TRUE,
+        'default' => 0,
+      ),
     ),
     'primary key' => array('block_id'),
   );
@@ -68,11 +80,20 @@ function ajaxblocks_schema() {
   return $schema;
 }
 
+/**
+ * Implements hook_install
+ */
+function ajaxblocks_install(){
+  variable_set('ajaxblocks_enable_cookie_caching', 0);
+  variable_set('ajaxblocks_json2_path', NULL);
+}
 
 /**
  * Implements hook_uninstall().
  */
 function ajaxblocks_uninstall() {
+  variable_del('ajaxblocks_enable_cookie_caching');
+  variable_del('ajaxblocks_json2_path');
   cache_clear_all('ajaxblocks', 'cache');
 }
 
@@ -80,3 +101,27 @@ function ajaxblocks_uninstall() {
 function ajaxblocks_update_last_removed() {
   return 6102;
 }
+
+/**
+ * Adds support for client side caching of ajaxblocks
+ */
+function ajaxblocks_update_7103(){
+  $spec = array(
+      'description' => 'Boolean indicating whether the block should be cached by the client\'s browser.',
+      'type' => 'int',
+      'not null' => TRUE,
+      'default' => 0,
+  );
+  db_add_field( 'ajaxblocks', 'enable_client_cache', $spec);
+
+  $spec = array(
+      'description' => 'Time a client\'s cache of a block is valid.',
+      'type' => 'int',
+      'not null' => TRUE,
+      'default' => 0,
+  );
+  db_add_field( 'ajaxblocks', 'maximum_client_cache', $spec);
+
+  variable_set('ajaxblocks_enable_cookie_caching', 0);
+  variable_set('ajaxblocks_json2_path', NULL);
+}
diff --git a/ajaxblocks.js b/ajaxblocks.js
index c1a6c4f..1750366 100644
--- a/ajaxblocks.js
+++ b/ajaxblocks.js
@@ -10,22 +10,54 @@ Drupal.ajaxblocksSendRequest = function (request, delay) {
     setTimeout(function () {Drupal.ajaxblocksSendRequest(request, 0);}, delay);
     return;
   }
-  $.ajax({
-    url: ((typeof Drupal.settings.ajaxblocks_path !== 'undefined') ? Drupal.settings.ajaxblocks_path : (Drupal.settings.basePath + "ajaxblocks")),
-    type: "GET",
-    dataType: "json",
-    data: request + '&nocache=1',
-    cache: false,
-    success: function (data) {
-      // Replaces the placeholder divs by the actual block contents returned by the AJAX call,
-      // executes the extra JavaScript code and attach behaviours if the apply to the blocks.
-      Drupal.freezeHeight();
-      for (var id in data) {
-        Drupal.ajaxblocksSetBlockContent(id, data[id]);
-      }
-      Drupal.unfreezeHeight();
+  var date = new Date();
+  var now = date.getTime();
+
+  var ajaxRequired = false;
+  // Loop over the blocks for this page
+  // Setup a request to the server if needed
+  var cache;
+  for (var id in Drupal.settings.ajaxblocks.blocks) {
+    cache = Drupal.ajaxblocksGetCache('ajaxblock-' + id);
+    if( cache != null
+        && ( cache.expires == null
+          || cache.expires > now
+        )
+      ){
+      Drupal.ajaxblocksSetBlockContent(id, cache);
+    } else {
+      ajaxRequired = true;
     }
-  });
+  }
+
+  if( ajaxRequired ){
+    // Go back to the server
+    $.ajax({
+      url: ((typeof Drupal.settings.ajaxblocks.path !== 'undefined') ? Drupal.settings.ajaxblocks.path : (Drupal.settings.basePath + "ajaxblocks")),
+      type: "GET",
+      dataType: "json",
+      data: request + '&nocache=1',
+      cache: false,
+      success: function (data) {
+        // Replaces the placeholder divs by the actual block contents returned by the AJAX call,
+        // executes the extra JavaScript code and attach behaviours if the apply to the blocks.
+        Drupal.freezeHeight();
+        for (var id in data) {
+          Drupal.ajaxblocksSetBlockContent(id, data[id]);
+          if( Drupal.settings.ajaxblocks.blocks[id].enable_client_cache == 1 ) {
+            var expiration = parseInt(Drupal.settings.ajaxblocks.blocks[id].maximum_client_cache);
+            if( expiration == 0 ){
+              data[id].expires = null;
+            } else {
+              data[id].expires = now + expiration;
+            }
+            Drupal.ajaxblocksSetCache( 'ajaxblock-' + id, data[id] );
+          }
+        }
+        Drupal.unfreezeHeight();
+      }
+    });
+  }
 }
 
 Drupal.ajaxblocksSetBlockContent = function (id, data) {
@@ -44,16 +76,66 @@ Drupal.ajaxblocksSetBlockContent = function (id, data) {
   Drupal.attachBehaviors(context);
 }
 
+Drupal.ajaxblocksGetCache = function (id) {
+  var data = null;
+  if( Drupal.ajaxblocksLocalStorage ){
+    data = Drupal.ajaxblocksLocalStorage.getItem(id);
+  } else {
+    if( Drupal.settings.ajaxblocks.enable_cookie_cache ){
+      data = jQuery.cookie(id)
+    }
+  }
+  try {
+    data = JSON.parse(data);
+  } catch(err) {
+    return null;
+  }
+  return data;
+}
+
+Drupal.ajaxblocksSetCache = function (id, data) {
+  try {
+    data = JSON.stringify(data);
+  } catch (err){
+    return;
+  }
+  if( Drupal.ajaxblocksLocalStorage ){
+    Drupal.ajaxblocksLocalStorage.setItem(id, data);
+  } else {
+    if( Drupal.settings.ajaxblocks.enable_cookie_cache ){
+      jQuery.cookie(id, data, {path: Drupal.settings.basePath});
+    }
+  }
+}
+
 $(document).ready(function () {
-  if (typeof Drupal.settings.ajaxblocks !== 'undefined') {
-    Drupal.ajaxblocksSendRequest(Drupal.settings.ajaxblocks, Drupal.settings.ajaxblocks_delay);
+  if (
+    typeof Drupal.settings.ajaxblocks !== 'undefined'
+    && typeof Drupal.settings.ajaxblocks.standard !== 'undefined'
+  ) {
+    Drupal.ajaxblocksSendRequest(Drupal.settings.ajaxblocks.standard, Drupal.settings.ajaxblocks.delay);
   }
 });
 
 $(window).load(function () {
-  if (typeof Drupal.settings.ajaxblocks_late !== 'undefined') {
-    Drupal.ajaxblocksSendRequest(Drupal.settings.ajaxblocks_late, Drupal.settings.ajaxblocks_late_delay);
+  if (
+    typeof Drupal.settings.ajaxblocks !== 'undefined'
+      && typeof Drupal.settings.ajaxblocks.late !== 'undefined'
+  ) {
+    Drupal.ajaxblocksSendRequest(Drupal.settings.ajaxblocks.late, Drupal.settings.ajaxblocks.late_delay);
   }
 });
 
 })(jQuery);
+
+Drupal.ajaxblocksLocalStorage = (function() {
+  var uid = new Date,
+      storage,
+      result;
+  try {
+    (storage = window.localStorage).setItem(uid, uid);
+    result = storage.getItem(uid) == uid;
+    storage.removeItem(uid);
+    return result && storage;
+  } catch(e) {}
+}());
diff --git a/ajaxblocks.module b/ajaxblocks.module
index e94666b..decaac9 100644
--- a/ajaxblocks.module
+++ b/ajaxblocks.module
@@ -17,9 +17,46 @@ function ajaxblocks_menu() {
     'type' => MENU_CALLBACK,
   );
 
+  $items['admin/config/development/ajaxblocks'] = array(
+    'title' => 'Ajaxblocks',
+    'description' => 'Control global ajaxblocks settings.',
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('ajaxblocks_admin_settings_form'),
+    'access arguments' => array('administer ajaxblocks settings'),
+    'file' => 'ajaxblocks.admin.inc',
+  );
+
   return $items;
 }
 
+/**
+ * Implements hook_permission
+ */
+function ajaxblocks_permission(){
+  $permissions = array();
+
+  $permissions['administer ajaxblocks settings'] = array(
+    'title' => t('Administer ajaxblocks settings'),
+    'description' => t('Allows access global settings for ajaxblocks.'),
+    'restrict access' => FALSE,
+  );
+
+  return $permissions;
+}
+
+/**
+ * Implements hook_init
+ */
+function ajaxblocks_init(){
+  $json2_path = variable_get('ajaxblocks_json2_path', NULL);
+  if( !empty($json2_path) ){
+    drupal_add_js( $json2_path );
+  }
+  // If cookie based caching is enabled, add the required js files
+  if( variable_get('ajaxblocks_enable_cookie_caching', 0) == 1 ){
+    drupal_add_js('misc/jquery.cookie.js');
+  }
+}
 
 /**
  * Implements hook_form_FORM_ID_alter().
@@ -124,6 +161,22 @@ function ajaxblocks_form_block_admin_configure_alter(&$form, &$form_state, $form
     '#default_value' => isset($settings['uncached_roles']) ? $settings['uncached_roles'] : array(),
   );
 
+  $form['visibility']['ajaxblocks']['ajaxblocks_enable_client_cache'] = array(
+    '#type' => 'checkbox',
+    '#title' => t('Enable Client Cache'),
+    '#description' => t('Enabling client cache will store the result of an ajaxblock call in the client\'s browser after its first load.'),
+    '#default_value' => isset($settings['enable_client_cache']) ? $settings['enable_client_cache'] : 0,
+  );
+
+  $form['visibility']['ajaxblocks']['ajaxblocks_maximum_client_cache'] = array(
+    '#type' => 'textfield',
+    '#required' => TRUE,
+    '#title' => t('Time in milliseconds to cache block content in client\'s browser'),
+    '#field_suffix' => ' ' . t('Milliseconds'),
+    '#description' => t('This setting will be used to invalidate cached data saved between client sessions, allow them to obtain fresh copies. Enter 0 to persist permanently.'),
+    '#default_value' => isset($settings['maximum_client_cache']) ? $settings['maximum_client_cache'] : 0,
+  );
+
   $form['#submit'][] = 'ajaxblocks_save_settings';
 }
 
@@ -142,6 +195,8 @@ function ajaxblocks_save_settings($form, &$form_state) {
       'include_noscript' => (int) $form_state['values']['ajaxblocks_include_noscript'],
       'cached_roles' => implode(' ', array_filter($form_state['values']['ajaxblocks_cached_roles'])),
       'uncached_roles' => implode(' ', array_filter($form_state['values']['ajaxblocks_uncached_roles'])),
+      'enable_client_cache' => (int) $form_state['values']['ajaxblocks_enable_client_cache'],
+      'maximum_client_cache' => (int) $form_state['values']['ajaxblocks_maximum_client_cache'],
     ))
     ->execute();
   ajaxblocks_update_cache();
@@ -381,18 +436,14 @@ function ajaxblocks_preprocess_html(&$variables) {
   if (count($ajax_blocks) == 0) {
     return;
   }
-  drupal_add_js(drupal_get_path('module', 'ajaxblocks') . '/ajaxblocks.js');
-  $path = url('ajaxblocks');
-  if ($path !== base_path() . 'ajaxblocks') {
-    // Provide path for AJAX handler directly in non-trivial cases (for instance, language prefix).
-    drupal_add_js(array('ajaxblocks_path' => $path), 'setting');
-  }
   $block_ids = array();
   $block_ids_late = array();
   $min_delay = 1000000;
   $min_delay_late = 1000000;
   $use_loader_picture = FALSE;
+  $blocks = array();
   foreach ($ajax_blocks as $block_id => $settings) {
+    $blocks[$block_id] = $settings;
     if ($settings['is_late']) {
       $block_ids_late[] = $block_id;
       if ($min_delay_late > $settings['delay']) {
@@ -423,18 +474,34 @@ function ajaxblocks_preprocess_html(&$variables) {
     $get_params = '&' . $get_params;
   }
 
+  // Create a settings array
+  $settings = array( 'ajaxblocks' => array() );
+
   if (count($block_ids) > 0) {
-    drupal_add_js(array('ajaxblocks' => 'blocks=' . implode('/', $block_ids) . '&path=' . $_GET['q'] . $get_params), 'setting');
+    $settings['ajaxblocks']['standard'] = 'blocks=' . implode('/', $block_ids) . '&path=' . $_GET['q'] . $get_params;
     if ($min_delay > 0) {
-      drupal_add_js(array('ajaxblocks_delay' => $min_delay), 'setting');
+      $settings['ajaxblocks']['delay'] = $min_delay;
     }
   }
   if (count($block_ids_late) > 0) {
-    drupal_add_js(array('ajaxblocks_late' => 'blocks=' . implode('/', $block_ids_late) . '&path=' . $_GET['q'] . $get_params), 'setting');
+    $settings['ajaxblocks']['late'] = 'blocks=' . implode('/', $block_ids_late) . '&path=' . $_GET['q'] . $get_params;
     if ($min_delay_late > 0) {
-      drupal_add_js(array('ajaxblocks_delay_late' => $min_delay_late), 'setting');
+      $settings['ajaxblocks']['delay_late'] = $min_delay_late;
     }
   }
+
+  $settings['ajaxblocks']['enable_cookie_cache'] = variable_get('ajaxblocks_enable_cookie_caching', 0);
+  $settings['ajaxblocks']['blocks'] = $blocks;
+
+  $path = url('ajaxblocks');
+  if ($path !== base_path() . 'ajaxblocks') {
+    // Provide path for AJAX handler directly in non-trivial cases (for instance, language prefix).
+    $settings['ajaxblocks']['path'] = $path;
+  }
+
+  drupal_add_js(drupal_get_path('module', 'ajaxblocks') . '/ajaxblocks.js');
+  drupal_add_js($settings, 'setting');
+
   // Rewrite page-level js.
   $variables['scripts'] = drupal_get_js();
 }
@@ -468,6 +535,22 @@ function _ajaxblocks_in_ajax_handler_impl($set = FALSE) {
   return $in_ajax_handler;
 }
 
+/**
+ * Internal function to check for existance of client side cookie caching libraries
+ *
+ * @return mixed. NULL or string path
+ */
+function _ajaxblocks_get_json2_path(){
+  if (module_exists('libraries')
+    && file_exists(libraries_get_path('json2') . '/json2.js') ) {
+    return libraries_get_path('json2') . '/json2.js';
+  } else {
+    if ( file_exists('sites/all/libraries/json2/json2.js') ) {
+      return 'sites/all/libraries/json2/json2.js';
+    }
+  }
+  return NULL;
+}
 
 /**
  * Returns TRUE if current operation is block loading via AJAX.
