Index: src/wp-admin/customize.php
===================================================================
--- src/wp-admin/customize.php	(revision 36678)
+++ src/wp-admin/customize.php	(working copy)
@@ -55,6 +55,9 @@
 wp_enqueue_script( 'customize-controls' );
 wp_enqueue_style( 'customize-controls' );
 
+// Setup heartbeat to keep nonces up to date.
+wp_enqueue_script( 'heartbeat' );
+
 /**
  * Enqueue Customizer control scripts.
  *
Index: src/wp-admin/js/customize-controls.js
===================================================================
--- src/wp-admin/js/customize-controls.js	(revision 36678)
+++ src/wp-admin/js/customize-controls.js	(working copy)
@@ -2946,6 +2946,19 @@
 				active: $.Deferred()
 			};
 
+			// When the heartbeat returns a refreshed nonce array, apply it.
+			$( document ).on( 'heartbeat-tick.wp-refresh-nonces', function( e, data ) {
+				if ( 'undefined' !== typeof data['wp-customize-nonces'] ) {
+					api.trigger( 'nonce-refresh', data['wp-customize-nonces'] );
+				}
+			} );
+
+			// Attach our existing nonce to the heartbeat request, so it can be checked for expiration.
+			$( document ).on( 'heartbeat-send.wp-refresh-nonces', function( e, data ) {
+				data['wp-customize-nonces'] = api.settings.nonce;
+				data['isCustomizer']        = true;
+			} );
+
 			/*
 			 * Wrap this.refresh to prevent it from hammering the servers:
 			 *
Index: src/wp-includes/class-wp-customize-manager.php
===================================================================
--- src/wp-includes/class-wp-customize-manager.php	(revision 36678)
+++ src/wp-includes/class-wp-customize-manager.php	(working copy)
@@ -304,9 +304,54 @@
 
 		// Export the settings to JS via the _wpCustomizeSettings variable.
 		add_action( 'customize_controls_print_footer_scripts', array( $this, 'customize_pane_settings' ), 1000 );
+
+		// Refresh the customizer nonces using the hearbeat api.
+		add_filter( 'heartbeat_received', array( $this, 'wp_heartbeat_refresh_customizer_nonces' ), 10,  3 );
+		add_filter( 'heartbeat_settings', array( $this, 'wp_heartbeat_settings_customizer_filter' ) );
+
 	}
 
 	/**
+	 * Filter heartbeat settings for the Customizer.
+	 *
+	 * @since 4.5.0
+	 *
+	 * @param array $settings  Current settings to filter.
+	 * @return array
+	 */
+		public function wp_heartbeat_settings_customizer_filter( $settings ) {
+			global $pagenow;
+
+			if ( 'customize.php' !== $pagenow ) {
+				return $settings;
+			}
+			$settings['screenId'] = 'customize';
+
+			return $settings;
+		}
+
+	/**
+	 * Return refreshed customizer nonces when needed.
+	 *
+	 * @since 4.5.0
+	 *
+	 * @param array  $response  The Heartbeat response.
+	 * @param array  $data      The $_POST data sent.
+	 * @param string $screen_id The screen id.
+	 * @return array The Heartbeat response.
+	 */
+	function wp_heartbeat_refresh_customizer_nonces( $response, $data, $screen_id ) {
+		if ( array_key_exists( 'wp-customize-nonces', $data ) && current_user_can( 'customize' ) ) {
+			$received = $data['wp-customize-nonces'];
+			if ( 2 === wp_verify_nonce( $received['save'], 'save-customize_' . $this->get_stylesheet() ) ) {
+				$response['wp-customize-nonces'] = $this->get_nonces();
+			}
+		}
+
+		return $response;
+	}
+
+	/**
 	 * Return true if it's an AJAX request.
 	 *
 	 * @since 3.4.0
Index: src/wp-includes/js/heartbeat.js
===================================================================
--- src/wp-includes/js/heartbeat.js	(revision 36678)
+++ src/wp-includes/js/heartbeat.js	(working copy)
@@ -367,6 +367,11 @@
 				has_focus: settings.hasFocus
 			};
 
+			if ( heartbeatData.isCustomizer  ) {
+				ajaxData.wp_customize = 'on';
+				delete ajaxData.data.isCustomizer;
+			}
+
 			settings.connecting = true;
 			settings.xhr = $.ajax({
 				url: settings.url,
