diff --git a/lib/cron.php b/lib/cron.php
index 5981b793bb48c420076d069a78a2e2e1fb64c6d7..55630eec58d9edd099d6b17b48fb49398b13b911 100644
--- a/lib/cron.php
+++ b/lib/cron.php
@@ -1,12 +1,34 @@
 <?php
 
+function is_hook_safe($hook) {
+    if (strlen($hook) > 2 && substr($hook, 0, 3) == "wp_") {
+        return true;
+    }
+    if ($hook == "delete_expired_transients") {
+        return true;
+    }
+    return false;
+}
+
 // Run cron jobs for the current blog.
-// (Use switch_to_blog() before calling this). 
+// (Use switch_to_blog() before calling this).
+//
+// We can deal with core Wordpress events inline, but not with events
+// associated with plugins, as they aren't properly loaded when
+// switch_to_blog() is called. So we detect when "unsafe" events
+// should be scheduled, and only for those blogs we spawn a wp-cli
+// process (much slower) to run them in the specific context of the
+// blog.
 function noblogs_run_cron_for_current_blog() {
   $crons = wp_get_ready_cron_jobs();
 
+  $has_unsafe_hooks = false;
   foreach ($crons as $timestamp => $cronhooks) {
     foreach ($cronhooks as $hook => $keys) {
+      if (!is_hook_safe($hook)) {
+        $has_unsafe_hooks = true;
+        continue;
+      }
       foreach ($keys as $k => $v) {
         echo "  {$k} -> {$hook}()\n";
         $schedule = $v['schedule'];
@@ -28,4 +50,10 @@ function noblogs_run_cron_for_current_blog() {
       }
     }
   }
+
+  if ($has_unsafe_hooks) {
+    $domain = get_option('domain');
+    echo "  executing wp-cli for unsafe hooks ({$domain})";
+    system("wp cron event run --due-now --url={$domain}");
+  }
 }