diff --git a/context_reaction_esi_block.inc b/context_reaction_esi_block.inc
index 5174917..7fd58e9 100644
--- a/context_reaction_esi_block.inc
+++ b/context_reaction_esi_block.inc
@@ -5,13 +5,12 @@
  *   Context module esi code.
  */
 
-
 class context_reaction_esi_block extends context_reaction_block {
   protected function build_block($block, $reset = FALSE) {
     require_once(drupal_get_path('module', 'esi') . '/esi.inc');
-    $esi_config = _esi__block_settings($block->module, $block->delta);
+    $config = esi_get_settings($block->module . '_' . $block->delta);
 
-    if ($esi_config->enabled && variable_get('esi_mode', ESI_MODE)) {
+    if ($config['scope'] && variable_get('esi_mode', ESI_MODE)) {
       $block->content = theme('esi_tag', $block);
     }
     else {
diff --git a/esi.admin.inc b/esi.admin.inc
index 75fa56d..dabe985 100644
--- a/esi.admin.inc
+++ b/esi.admin.inc
@@ -47,7 +47,7 @@ function esi_admin_settings_form() {
   $form['global']['esi_seed_rotation_interval'] = array(
     '#title' => 'Default seed key rotation interval',
     '#type' => 'select',
-    '#options' => _esi__ttl_options($seed),
+    '#options' => esi_max_age_options($seed),
     '#default_value' => $seed,
   );
 
@@ -56,13 +56,28 @@ function esi_admin_settings_form() {
     '#type' => 'fieldset',
     '#title' => t('Block settings'),
   );
-  $block = (int)variable_get('esi_block_default_ttl', ESI_BLOCK_DEFAULT_TTL);
-  $form['blocks']['esi_block_default_ttl'] = array(
-    '#title' => 'Default block TTL',
+  $max_age = (int)variable_get('esi_block_default_max_age', ESI_BLOCK_DEFAULT_MAX_AGE);
+  $form['blocks']['esi_block_default_max_age'] = array(
+    '#title' => t('Cache Maximum Age (TTL)'),
     '#type' => 'select',
-    '#options' => _esi__ttl_options($block),
-    '#default_value' => $block,
-    '#description' => t('Time-to-live on the proxy/browser cache'),
+    '#options' => esi_max_age_options($max_age),
+    '#default_value' => $max_age,
+    '#description' => t('External page caches (proxy/browser) will not deliver cached paged older than this setting; time to live in short.'),
+  );
+  $form['blocks']['esi_block_default_scope'] = array(
+    '#title'          => t('Default Panel Cache Scope'),
+    '#type'           => 'select',
+    '#options'        => array(
+      0 => 'Disabled',
+      1 => 'Not Cached',
+      2 => 'Global',
+      3 => 'Page',
+      4 => 'User Role',
+      5 => 'User Role/Page',
+      6 => 'User ID',
+    ),
+    '#default_value'  => variable_get('esi_block_default_scope', ESI_BLOCK_DEFAULT_SCOPE),
+    '#description'    => t('Disabled - Do not use ESI. <br />Not Cached - Use ESI, but never cache the content. <br />Global - Content is same on every page. <br />Page - Content changes based on the URL. <br />User Role - Content changes based on the user role. <br />User Role/Page - Content changes based on the user role as well as the URL. <br />User ID - Content changes based on the UID; otherwise it is the same as global.'),
   );
 
   // Panel Settings.
@@ -70,13 +85,28 @@ function esi_admin_settings_form() {
     '#type' => 'fieldset',
     '#title' => t('Panels settings'),
   );
-  $panel = (int)variable_get('esi_panel_default_ttl', ESI_PANEL_DEFAULT_TTL);
-  $form['panels']['esi_panel_default_ttl'] = array(
-    '#title' => 'Default panel TTL',
+  $max_age = (int)variable_get('esi_panel_default_max_age', ESI_PANEL_DEFAULT_MAX_AGE);
+  $form['panels']['esi_panel_default_max_age'] = array(
+    '#title' => t('Cache Maximum Age (TTL)'),
     '#type' => 'select',
-    '#options' => _esi__ttl_options($panel),
-    '#default_value' => $panel,
-    '#description' => t('Time-to-live on the proxy/browser cache'),
+    '#options' => esi_max_age_options($max_age),
+    '#default_value' => $max_age,
+    '#description' => t('External page caches (proxy/browser) will not deliver cached paged older than this setting; time to live in short.'),
+  );
+  $form['panels']['esi_panel_default_scope'] = array(
+    '#title'          => t('Default Panel Cache Scope'),
+    '#type'           => 'select',
+    '#options'        => array(
+      0 => 'Disabled',
+      1 => 'Not Cached',
+      2 => 'Global',
+      3 => 'Page',
+      4 => 'User Role',
+      5 => 'User Role/Page',
+      6 => 'User ID',
+    ),
+    '#default_value'  => variable_get('esi_panel_default_scope', ESI_PANEL_DEFAULT_SCOPE),
+    '#description'    => t('Disabled - Do not use ESI. <br />Not Cached - Use ESI, but never cache the content. <br />Global - Content is same on every page. <br />Page - Content changes based on the URL. <br />User Role - Content changes based on the user role. <br />User Role/Page - Content changes based on the user role as well as the URL. <br />User ID - Content changes based on the UID; otherwise it is the same as global.'),
   );
 
   $widget_vis = "
@@ -86,20 +116,20 @@ function esi_update_visibility() {
   if ($('#edit-esi-mode-3:checked').val() !== undefined || $('#edit-esi-mode-2:checked').val() !== undefined) {
     $('#edit-esi-ajax-fallback-wrapper').hide();
     $('#edit-esi-seed-rotation-interval-wrapper').show();
-    $('#edit-esi-panel-default-ttl').parent().parent().parent().show();
-    $('#edit-esi-block-default-ttl').parent().parent().parent().show();
+    $('#edit-esi-panel-default-max-age').parent().parent().parent().show();
+    $('#edit-esi-block-default-max-age').parent().parent().parent().show();
   }
   else if ($('#edit-esi-mode-0:checked').val() !== undefined) {
     $('#edit-esi-ajax-fallback-wrapper').hide();
     $('#edit-esi-seed-rotation-interval-wrapper').hide();
-    $('#edit-esi-panel-default-ttl').parent().parent().parent().hide();
-    $('#edit-esi-block-default-ttl').parent().parent().parent().hide();
+    $('#edit-esi-panel-default-max-age').parent().parent().parent().hide();
+    $('#edit-esi-block-default-max-age').parent().parent().parent().hide();
   }
   else {
     $('#edit-esi-ajax-fallback-wrapper').show();
     $('#edit-esi-seed-rotation-interval-wrapper').show();
-    $('#edit-esi-panel-default-ttl').parent().parent().parent().show();
-    $('#edit-esi-block-default-ttl').parent().parent().parent().show();
+    $('#edit-esi-panel-default-max-age').parent().parent().parent().show();
+    $('#edit-esi-block-default-max-age').parent().parent().parent().show();
   }
 }";
   drupal_add_js($widget_vis, 'inline');
diff --git a/esi.inc b/esi.inc
index cbd2cc1..4d8a5e2 100644
--- a/esi.inc
+++ b/esi.inc
@@ -54,18 +54,14 @@ function _esi__get_roles_hash($rids) {
 /**
  * Get the themed HTML for a particular block
  *
- * @param String $theme
- *   The name of the theme: e.g. "garland"
+ * @param $theme
+ *   The name of the theme: "garland".
  * @param String $region
  *   The name of the region the block is in: e.g. "left"
  * @param String $module
  * @param String $delta
  */
 function _esi__get_block($theme, $region, $module, $delta) {
-  global $theme_key;
-  $theme_key = $theme;
-  init_theme();
-
   $block = db_fetch_object(db_query("SELECT * FROM {blocks} WHERE module = '%s' AND delta = '%s' AND theme = '%s'", $module, $delta, $theme));
   $block->context = $region;
   $block->status = 1;
@@ -89,40 +85,59 @@ function _esi__get_block($theme, $region, $module, $delta) {
 
 /**
  * Get / Set the config for a particular block.
+ *
+ * @param $id
+ *   The ID for the configuration.
+ * @param $config
+ *   (optinal) If this is set, it will save the settings passed in.
+ * @return
+ *   The saved configuration for the given ID.
  */
-function _esi__block_settings($module, $delta, $config = NULL) {
+function esi_get_settings($id, $config = NULL) {
   // TODO: replace with something more suited to in-code config than variables.
-
-  $key = "{$module}_{$delta}";
-  $global = variable_get('esi_block_config', array());
+  $settings = variable_get('esi_block_config', array());
 
   if ($config) {
-    $global[$key] = $config;
-    variable_set('esi_block_config', $global);
+    while (!lock_acquire('esi_block_config')) {
+      lock_wait('esi_block_config');
+    }
+    // Now that we have a lock, grab the settings from the database.
+    $settings = unserialize(db_result(db_query("SELECT value FROM {variable} WHERE name = 'esi_block_config'")));
+    $settings[$id] = $config;
+    variable_set('esi_block_config', $settings);
+    lock_release('esi_block_config');
+  }
+  elseif (!empty($settings[$id])) {
+    $config = $settings[$id];
+  }
+
+  // Set defaults.
+  if (!is_array($config)) {
+    $config = array();
   }
-  elseif (array_key_exists($key, $global)) {
-    $config = $global[$key];
+  if (!isset($config['max_age'])) {
+    $config['max_age'] = (int)variable_get('esi_block_default_max_age', ESI_BLOCK_DEFAULT_MAX_AGE);
   }
-  else {
-    $config = new stdClass;
-    $config->enabled  = variable_get('esi_default_enabled', ESI_DEFAULT_ENABLED);
-    $config->ttl      = variable_get('esi_block_default_ttl', ESI__BLOCK_CONFIG_DEFAULT__TTL);
+  if (!isset($config['scope'])) {
+    $config['scope'] = (int)variable_get('esi_block_default_scope', ESI_BLOCK_DEFAULT_SCOPE);
   }
 
+  // Return the configuration for this ID.
   return $config;
 }
 
 /**
- * Get a list of possible ttl choices.
+ * Get a list of possible max age (ttl) choices.
  */
-function _esi__ttl_options($current_ttl) {
-  $options = drupal_map_assoc(array(15, 30, 60, 120, 180, 240, 300, 600, 900, 1200, 1800, 3600, 7200, 14400, 28800, 43200, 64800, 86400, 86400*2, 86400*3, 86400*4, 86400*5, 86400*6, 86400*7), 'format_interval');
+function esi_max_age_options($current_max_age) {
+  $options = drupal_map_assoc(array(0, 5, 15, 30, 60, 120, 180, 240, 300, 600, 900, 1200, 1800, 3600, 7200, 14400, 28800, 43200, 64800, 86400, 86400*2, 86400*3, 86400*4, 86400*5, 86400*6, 86400*7), 'format_interval');
 
-  // if the given ttl isn't one of our options, add the current ttl as a custom option.
-  if (!isset($options[$current_ttl])) {
-    $options[$current_ttl] = t('Custom: @time', array('@time' => format_interval($current_ttl)));
+  // If the given max age isn't one of our options, add the current max age as a custom option.
+  if (!isset($options[$current_max_age])) {
+    $options[$current_max_age] = t('Custom: @time', array('@time' => format_interval($current_max_age)));
     ksort($options);
   }
+  $options[0] = '<' . t('none') . '>';
 
   return $options;
 }
diff --git a/esi.module b/esi.module
index 5769a68..f77f9d8 100644
--- a/esi.module
+++ b/esi.module
@@ -18,11 +18,17 @@ define('ESI_MODE', TRUE);
 // Default to using ajax as a fallback if ESI is disabled.
 define('ESI_AJAX_FALLBACK', TRUE);
 
-// Default TTL for blocks: 5 minutes.
-define('ESI_BLOCK_DEFAULT_TTL', 300);
+// Default max age for blocks: 5 minutes.
+define('ESI_BLOCK_DEFAULT_MAX_AGE', 300);
 
-// Default TTL for panels: 5 minutes.
-define('ESI_PANEL_DEFAULT_TTL', 300);
+// Default scope for blocks: User Role.
+define('ESI_BLOCK_DEFAULT_SCOPE', 4);
+
+// Default max age for panels: 5 minutes.
+define('ESI_PANEL_DEFAULT_MAX_AGE', 300);
+
+// Default scope for panels: User Role.
+define('ESI_PANEL_DEFAULT_SCOPE', 4);
 
 // ESI setting constants.
 define('ESI__CONFIG_ESI', 1);
@@ -210,30 +216,52 @@ function esi_form_block_admin_configure_alter(&$form, $form_state) {
 
   $module = $form['module']['#value'];
   $delta  = $form['delta']['#value'];
-  $config = _esi__block_settings($module, $delta);
+  $config = esi_get_settings($module . '_' . $delta);
 
   $element['esi_config'] = array(
-    '#type' => 'fieldset',
-    '#title' => t('ESI settings'),
-    '#description' => t('Control how this block is cached on an ESI-enabled reverse proxy.'),
-    '#tree' => TRUE,
+    '#title'        => t('ESI settings'),
+    '#type'         => 'fieldset',
+    '#description'  => t('Control how this block is cached on an ESI-enabled reverse proxy.'),
+    '#tree'         => TRUE,
   );
-
-  $element['esi_config']['enabled'] = array(
-    '#type' => 'checkbox',
-    '#title' => t('Enable ESI'),
-    '#default_value' => $config->enabled,
+  $element['esi_config']['scope'] = array(
+    '#title'          => t('Cache Scope'),
+    '#type'           => 'select',
+    '#options'        => array(
+      0 => 'Disabled',
+      1 => 'Not Cached',
+      2 => 'Global',
+      3 => 'Page',
+      4 => 'User Role',
+      5 => 'User Role/Page',
+      6 => 'User ID',
+    ),
+    '#default_value'  => $config['scope'],
+    '#description'    => t('Disabled - Do not use ESI. <br />Not Cached - Use ESI, but never cache the content. <br />Global - Content is same on every page. <br />Page - Content changes based on the URL. <br />User Role - Content changes based on the user role. <br />User Role/Page - Content changes based on the user role as well as the URL. <br />User ID - Content changes based on the UID; otherwise it is the same as global.'),
   );
-
-  $options = _esi__ttl_options($config->ttl);
-  $element['esi_config']['ttl'] = array(
+  $max_age = $config['max_age'];
+  $element['esi_config']['max_age'] = array(
+    '#title' => t('Cache Maximum Age (TTL)'),
     '#type' => 'select',
-    '#title' => t('TTL'),
-    '#description' => t('Time-to-live on the proxy-cache.'),
-    '#options' => $options,
-    '#default_value' => $config->ttl,
+    '#options' => esi_max_age_options($max_age),
+    '#default_value' => $max_age,
+    '#description' => t('External page caches (proxy/browser) will not deliver cached paged older than this setting; time to live in short.'),
   );
 
+  $widget_vis = "
+$(esi_update_visibility);
+$(function(){ $('#edit-esi-config-scope').change(function (){esi_update_visibility();})});
+function esi_update_visibility() {
+  var esi_scope = $('#edit-esi-config-scope').val();
+  if (esi_scope == '0' || esi_scope == '1') {
+    $('#edit-esi-config-max-age-wrapper').hide();
+  }
+  else {
+    $('#edit-esi-config-max-age-wrapper').show();
+  }
+}";
+  drupal_add_js($widget_vis, 'inline');
+
   // inject our ESI config-fieldset onto the form,
   // just after the 'block_settings' fieldset.
   $i = 1;
@@ -261,11 +289,12 @@ function esi__block_config_save($form, $form_state) {
   $module = $form_state['values']['module'];
   $delta  = $form_state['values']['delta'];
 
-  $config = new stdClass;
-  $config->enabled  = $form_state['values']['esi_config']['enabled'];
-  $config->ttl      = $form_state['values']['esi_config']['ttl'];
+  $config = array();
+  $config['max_age'] = (int)$form_state['values']['esi_config']['max_age'];
+  $config['scope'] = (int)$form_state['values']['esi_config']['scope'];
 
-  _esi__block_settings($module, $delta, $config);
+  // Save the settings.
+  esi_get_settings($module . '_' . $delta, $config);
 }
 
 /**
@@ -274,21 +303,19 @@ function esi__block_config_save($form, $form_state) {
  * Render a particular block.
  */
 function esi__block_handler($bid, $page = NULL) {
-  require_once(drupal_get_path('module', 'esi')  . '/esi.inc');
-
-  /**
-   * Expect the bid format to be theme:region:module:delta
-   * Fail if this doesn't match.
-   */
+  // Expect the bid format to be theme:region:module:delta
   if (substr_count($bid, ':') !== 3) {
     return FALSE;
   }
-  list($theme, $region, $module, $delta) = explode(':', $bid);
+  list($passed_theme, $region, $module, $delta) = explode(':', $bid);
+
+  require_once(drupal_get_path('module', 'esi')  . '/esi.inc');
+  global $custom_theme, $theme_key;
 
   // Block content may change per-page.
   // If this is true for the current block, the origin page url should be
   // provided as an argument.
-  if (isset($page)) {
+  if (isset($page) && strpos($page, 'CACHE=') !== 0) {
     $q = base64_decode($page);
     if ($q == "") {
       $q = variable_get('site_frontpage', 'node');
@@ -296,44 +323,63 @@ function esi__block_handler($bid, $page = NULL) {
     $_GET['q'] = $q;
   }
 
-  // theme set-up.
-  // we need to do this manually, because output is echo'd instead of returned.
+  // Theme set-up.
+  // We need to do this manually, because output is echo'd instead of returned.
+  $custom_theme = $passed_theme;
+  $theme_key = $passed_theme;
   init_theme();
 
-  // get the block into a format that can be passed to view-blockk.
-  $block = _esi__get_block($theme, $region, $module, $delta);
-  $output = theme('block', $block);
-  /**
-   * Pass PER-USER or PER-ROLE cache info to varnish.
-   *
-   * No-cache is header (TTL) controlled.
-   * Per-page is passed as a url argument
-   */
-  if ($block->cache == BLOCK_NO_CACHE) {
-    header("Cache-Control: no-cache, max-age=0");
-  }
-  elseif ($block->cache & BLOCK_CACHE_PER_USER) {
-    // "Cache-control: private" advises proxies that the content is
-    // user-specific.  Most proxies will not cache this data - a clever proxy
-    // config may make this cacheable.
-    header("Cache-Control: private");
-    header("X-BLOCK-CACHE: " . BLOCK_CACHE_PER_USER);
+  // Get the block and theme it.
+  $block = _esi__get_block($passed_theme, $region, $module, $delta);
+  if (!empty($block)) {
+    $output = theme('block', $block);
   }
-  elseif ($block->cache & BLOCK_CACHE_PER_ROLE) {
-    header("Cache-Control: private");
-    header("X-BLOCK-CACHE: " . BLOCK_CACHE_PER_ROLE);
+  else {
+    $output = esi_fast404('block');
   }
 
-  // For all cacheable blocks, set the TTL based on the block config.
-  if ($block->cache != BLOCK_NO_CACHE) {
-    $config = _esi__block_settings($module, $delta);
-    header("Cache-Control: max-age={$config->ttl}");
-  }
+  // Pass PER-USER or PER-ROLE cache info to varnish.
+  // No-cache is header max age (TTL) controlled.
+  // Per-page is passed as a url argument.
+  $config = esi_get_settings($module . '_' . $delta);
+  esi_add_cache_headers($config['scope'], $config['max_age']);
 
   echo $output;
   exit;
 }
 
+function esi_add_cache_headers($scope, $max_age) {
+  switch ($scope) {
+    // Disabled.
+    case 0:
+    // Do no cache.
+    case 1:
+      header("Cache-Control: must-revalidate, max-age=0");
+      break;
+
+    // Global Scope.
+    case 2:
+    // Page Scope.
+    case 3:
+      header("Cache-Control: public, max-age=$max_age");
+      break;
+
+    // User Role.
+    case 4:
+    // User Role/Page.
+    case 5:
+      header("Cache-Control: private, max-age=$max_age");
+      header("X-BLOCK-CACHE: " . BLOCK_CACHE_PER_ROLE);
+      break;
+
+    // User ID.
+    case 6:
+      header("Cache-Control: private, max-age=$max_age");
+      header("X-BLOCK-CACHE: " . BLOCK_CACHE_PER_USER);
+      break;
+  }
+}
+
 /**
  * Implementation of hook_ctools_plugin_directory().
  */
@@ -389,19 +435,18 @@ function esi_panels_pane_content_alter(&$content, $pane, $args, $context) {
  * If the pane uses context, the task_name, context_string and q variables will
  * be set.
  */
-function esi__panel_pane_handler($bid, $task_name = NULL, $context_string = NULL, $q = NULL) {
-  /**
-   * Expect the bid format to be theme:display_id:pane_id
-   * Fail if this doesn't match.
-   */
+function esi__panel_pane_handler($bid, $page = NULL, $task_name = NULL, $context_string = NULL) {
+  // Expect the bid format to be theme:display_id:pane_id
   if (substr_count($bid, ':') !== 2) {
     return FALSE;
   }
-  list($theme, $display_id, $pane_id) = explode(':', $bid);
+  list($passed_theme, $display_id, $pane_id) = explode(':', $bid);
+
+  global $custom_theme, $theme_key;
 
   // Set context.
-  if (isset($q)) {
-    $q = base64_decode($q);
+  if (isset($page) && strpos($page, 'CACHE=') !== 0) {
+    $q = base64_decode($page);
     if ($q == "") {
       $q = variable_get('site_frontpage', 'node');
     }
@@ -410,7 +455,9 @@ function esi__panel_pane_handler($bid, $task_name = NULL, $context_string = NULL
 
   // theme set-up.
   // we need to do this manually, because output is echo'd instead of returned.
-  //init_theme();
+  $custom_theme = $passed_theme;
+  $theme_key = $passed_theme;
+  init_theme();
 
   $display = panels_load_display($display_id);
 
@@ -423,6 +470,7 @@ function esi__panel_pane_handler($bid, $task_name = NULL, $context_string = NULL
   }
 
   // Switch ESI off so the contents of the pane are served.
+  $config = $display->content[$pane_id]->cache;
   unset($display->content[$pane_id]->cache);
 
   if (!function_exists('panels_get_renderer_handler')) {
@@ -435,7 +483,15 @@ function esi__panel_pane_handler($bid, $task_name = NULL, $context_string = NULL
     $renderer->display->content[$pane_id]->access['plugins'] = array_filter($renderer->display->content[$pane_id]->access['plugins'], "filter_out_path_visibility_access");
   }
   $renderer->prepare_panes($renderer->display->content);
-  $content = $renderer->render_pane($renderer->prepared['panes'][$pane_id]);
+  $content = '';
+  if (!empty($renderer->prepared['panes'][$pane_id])) {
+    $content .= $renderer->render_pane($renderer->prepared['panes'][$pane_id]);
+  }
+  else {
+    $content .= esi_fast404('panel');
+  }
+
+  esi_add_cache_headers($config['settings']['scope'], $config['settings']['lifetime']);
 
   echo $content;
   exit;
@@ -539,3 +595,29 @@ function esi_context_registry_alter(&$registry) {
     $registry['reactions']['block']['plugin'] = 'context_reaction_esi_block';
   }
 }
+
+/**
+ * Generate a fast 404
+ *
+ * @return
+ *   Full html page displaying the 404 message.
+ */
+function esi_fast404($msg = '') {
+  global $base_path;
+  if (!headers_sent()) {
+    header($_SERVER['SERVER_PROTOCOL'] . ' 404 Not Found');
+    header('X-ESI: Failed To Render. ' . $msg);
+  }
+  $output = '';
+
+  $output .= '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">' . "\n";
+  $output .= '<html>';
+  $output .= '<head><title>404 Not Found</title></head>';
+  $output .= '<body><h1>Not Found</h1>';
+  $output .= '<p>The requested URL was not found on this server.</p>';
+  $output .= '<p><a href="' . $base_path . '">Home</a></p>';
+  $output .= '<!-- esi_fast404 -->';
+  $output .= '</body></html>';
+
+  return $output;
+}
diff --git a/esi.theme.inc b/esi.theme.inc
index f31baaf..e5c33b5 100644
--- a/esi.theme.inc
+++ b/esi.theme.inc
@@ -17,8 +17,8 @@ function esi__theme_blocks($region) {
   if ($list = block_list($region)) {
     foreach ($list as $key => $block) {
       // if ESI's enabled on the block, add an ESI tag instead of block content.
-      $esi_config = _esi__block_settings($block->module, $block->delta);
-      $output .= ($esi_config->enabled && variable_get('esi_mode', ESI_MODE))
+      $config = esi_get_settings($block->module . '_' . $block->delta);
+      $output .= ($config['scope'] && variable_get('esi_mode', ESI_MODE))
         ? theme('esi_tag', 'block', $block)
         : theme('block', $block);
     }
@@ -31,21 +31,21 @@ function esi__theme_blocks($region) {
 }
 
 function esi_theme_block($block) {
-  if (esi_theme_is_esied($block->content)) {
+  if (isset($block->content) && esi_theme_is_esied($block->content)) {
     return $block->content;
   }
   return theme('esi_alt_block', $block);
 }
 
 function esi_theme_panels_pane($pane) {
-  if (esi_theme_is_esied($pane->content)) {
+  if (isset($pane->content) && esi_theme_is_esied($pane->content)) {
     return $pane->content;
   }
   return theme('esi_alt_panels_pane', $pane);
 }
 
 function esi_theme_panels_stylizer_stylizer_style_render_pane($pane) {
-  if (esi_theme_is_esied($pane->content)) {
+  if (isset($pane->content) && esi_theme_is_esied($pane->content)) {
     return $pane->content;
   }
   return theme('esi_alt_panels_stylizer_stylizer_style_render_pane', $pane);
@@ -65,42 +65,36 @@ function esi_theme_is_esied($content) {
  */
 function theme_esi_tag($type, $data) {
   global $theme_key, $base_root, $base_path;
+  $added_to_url = array();
 
   if ($type == 'block') {
     $src = $base_path . "esi/block/{$theme_key}:{$data->region}:{$data->module}:{$data->delta}";
     $css_id = "{$theme_key}-{$data->region}-{$data->module}-{$data->delta}";
-
-    // if the block uses per-page cacheing, add the page param to the URL.
-    if ($data->cache & BLOCK_CACHE_PER_PAGE) {
-      $src .= '/' . base64_encode($_GET['q']);
-    }
-
-    // Finally, if the cache-mode is PER-USER or PER-ROLE, advise the proxy.
-    // Ideally, the proxy will remove this parameter.
-    if ($data->cache & BLOCK_CACHE_PER_USER || $data->cache & BLOCK_CACHE_PER_ROLE) {
-      $src .= '/CACHE=' . ($data->cache & BLOCK_CACHE_PER_USER ? 'USER' : 'ROLE');
-    }
+    $config = esi_get_settings($data->module . '_' . $data->delta);
+    $scope = $config['scope'];
   }
   elseif ($type == 'panel') {
     // Build the esi tag.
     $src = $base_path . "esi/panels_pane/{$theme_key}:{$data->did}:{$data->pid}";
     $css_id = "{$theme_key}-{$data->did}-{$data->pid}";
-
+    $scope = isset($data->cache['settings']['scope']) ? $data->cache['settings']['scope'] : (int)variable_get('esi_panel_default_scope', ESI_PANEL_DEFAULT_SCOPE);
     if (!empty($data->configuration['context']) && is_string($data->configuration['context'])) {
       $display = panels_get_current_page_display();
       // The cache-key variable is set by the panel-context render function.
       list($context, $task_name, $handler_name) = explode(':', $display->cache_key);
 
+      // Add the URL the panel was requested from.
+      $src .= '/' . base64_encode($_GET['q']);
+      $added_to_url['page'] = TRUE;
+
       // Add the page-manager task name.
       $src .= '/' . $task_name;
+      $added_to_url['task'] = TRUE;
 
       // Add the name of the context which is supplied to this pane.
       $src .= '/' . $data->configuration['context'];
-
-      // Add the URL the panel was requested from.
-      $src .= '/' . base64_encode($_GET['q']);
+      $added_to_url['context'] = TRUE;
     }
-    $src .= "/CACHE=ROLE";
   }
   if ($type == 'external') {
     $src = $data->cache['settings']['external_text'];
@@ -111,10 +105,52 @@ function theme_esi_tag($type, $data) {
       $parsed = parse_url($src);
       $glue_string = empty($parsed['query']) ? '?' : '&';
       $src .= $glue_string . 'esi_full=' . $full . '&esi_internal=' . $internal;
+      $added_to_url['page'] = TRUE;
+      $added_to_url['fullpage'] = TRUE;
     }
     $css_id = "{$theme_key}-{$data->did}-{$data->pid}";
   }
 
+  if (!empty($scope)) {
+    switch ($scope) {
+      // Global Scope.
+      case 0:
+      case 1:
+      case 2:
+        break;
+
+      // Page Scope.
+      case 3:
+        if (empty($added_to_url['page'])) {
+          $src .= '/' . base64_encode($_GET['q']);
+          $added_to_url['page'] = TRUE;
+        }
+        break;
+
+      // User Role.
+      case 4:
+        $src .= "/CACHE=ROLE";
+        $added_to_url['cache'] = TRUE;
+        break;
+
+      // User Role/Page.
+      case 5:
+        if (empty($added_to_url['page'])) {
+          $src .= '/' . base64_encode($_GET['q']);
+          $added_to_url['page'] = TRUE;
+        }
+        $src .= "/CACHE=ROLE";
+        $added_to_url['cache'] = TRUE;
+        break;
+
+      // User ID.
+      case 6:
+        $src .= "/CACHE=USER";
+        $added_to_url['cache'] = TRUE;
+        break;
+    }
+  }
+
   $setting = variable_get('esi_mode', ESI_MODE);
   switch ($setting) {
     case ESI__CONFIG_ESI:
diff --git a/plugins/cache/esi.inc b/plugins/cache/esi.inc
index 6bfa21b..aebad5a 100644
--- a/plugins/cache/esi.inc
+++ b/plugins/cache/esi.inc
@@ -98,15 +98,33 @@ function esi_esi_cache_settings_form($conf, $display, $pid) {
 
   ctools_include('dependent');
   require_once(drupal_get_path('module', 'esi') . '/esi.inc');
+  $conf['lifetime'] = isset($conf['lifetime']) ? $conf['lifetime'] : (int)variable_get('esi_panel_default_max_age', ESI_PANEL_DEFAULT_MAX_AGE);
 
   // The contexts-definition for this pane is in $display->configuration['context']
-  $form['lifetime'] = array(
-    '#title' => t('Lifetime'),
-    '#type' => 'select',
-    '#options' => _esi__ttl_options($conf['lifetime']),
-    '#default_value' => $conf['lifetime'],
+  $form['scope'] = array(
+    '#title'          => t('Default Panel Cache Scope'),
+    '#type'           => 'select',
+    '#options'        => array(
+      0 => 'Disabled',
+      1 => 'Not Cached',
+      2 => 'Global',
+      3 => 'Page',
+      4 => 'User Role',
+      5 => 'User Role/Page',
+      6 => 'User ID',
+    ),
+    '#default_value'  => isset($conf['scope']) ? $conf['scope'] : (int)variable_get('esi_panel_default_scope', ESI_PANEL_DEFAULT_SCOPE),
+    '#description'    => t('Disabled - Do not use ESI. <br />Not Cached - Use ESI, but never cache the content. <br />Global - Content is same on every page. <br />Page - Content changes based on the URL. <br />User Role - Content changes based on the user role. <br />User Role/Page - Content changes based on the user role as well as the URL. <br />User ID - Content changes based on the UID; otherwise it is the same as global.'),
   );
 
+  $max_age = $conf['max_age'];
+  $element['esi_config']['max_age'] = array(
+    '#title' => t('Cache Maximum Age (TTL)'),
+    '#type' => 'select',
+    '#options' => esi_max_age_options($max_age),
+    '#default_value' => $max_age,
+    '#description' => t('External page caches (proxy/browser) will not deliver cached paged older than this setting; time to live in short.'),
+  );
   $form['aligner_start'] = array('#value' => '<div class="option-text-aligner">');
   $form['external'] = array(
     '#type' => 'checkbox',
@@ -130,5 +148,28 @@ function esi_esi_cache_settings_form($conf, $display, $pid) {
     '#title' => t('Pass the current url to the external url as a base64 encoded string.'),
     '#id' => 'context-checkbox',
   );
+
+  // Add some JS inline.
+  $widget_vis = '
+<script type="text/javascript">
+<!--//--><![CDATA[//><!--' . "
+function esi_update_visibility() {
+  var esi_scope = $('#edit-settings-scope').val();
+  if (esi_scope == '0' || esi_scope == '1') {
+    $('#edit-settings-lifetime-wrapper').hide();
+  }
+  else {
+    $('#edit-settings-lifetime-wrapper').show();
+  }
+}
+$(esi_update_visibility);
+$(function(){ $('#edit-settings-scope').change(function (){esi_update_visibility();})});
+//--><!]]>
+</script>";
+  $form['js'] = array(
+    '#type' => 'markup',
+    '#value' => $widget_vis,
+  );
+
   return $form;
 }
