diff --git src/wp-admin/customize.php src/wp-admin/customize.php
index 3cfa0c7..2a3fb57 100644
--- src/wp-admin/customize.php
+++ src/wp-admin/customize.php
@@ -16,15 +16,20 @@ if ( ! current_user_can( 'customize' ) ) {
 	wp_die( __( 'Cheatin&#8217; uh?' ) );
 }
 
+global $wp_scripts, $wp_customize;
+
 wp_reset_vars( array( 'url', 'return' ) );
 $url = wp_unslash( $url );
 $url = wp_validate_redirect( $url, home_url( '/' ) );
+
 if ( $return ) {
 	$return = wp_unslash( $return );
 	$return = wp_validate_redirect( $return );
 }
 if ( ! $return ) {
-	if ( $url ) {
+	if ( ! $wp_customize->is_theme_active() ) {
+		$return = admin_url( 'themes.php' );
+	} elseif ( $url ) {
 		$return = $url;
 	} elseif ( current_user_can( 'edit_theme_options' ) || current_user_can( 'switch_themes' ) ) {
 		$return = admin_url( 'themes.php' );
@@ -33,8 +38,6 @@ if ( ! $return ) {
 	}
 }
 
-global $wp_scripts, $wp_customize;
-
 $registered = $wp_scripts->registered;
 $wp_scripts = new WP_Scripts;
 $wp_scripts->registered = $registered;
diff --git src/wp-admin/js/customize-controls.js src/wp-admin/js/customize-controls.js
index 85b171d..af082c5 100644
--- src/wp-admin/js/customize-controls.js
+++ src/wp-admin/js/customize-controls.js
@@ -811,8 +811,13 @@
 						}
 					}
 
-					clearTimeout( timeout );
-					timeout = setTimeout( callback, self.refreshBuffer );
+					try {
+						clearTimeout( timeout );
+						timeout = setTimeout( callback, self.refreshBuffer );
+					} catch ( e ) {
+						// Handle Uncaught InvalidAccessError: Failed to execute 'setTimeout' on 'Window': No script context is available in which to execute the script.
+						return callback();
+					}
 				};
 			})( this );
 
@@ -877,6 +882,109 @@
 
 			// Update the URL when the iframe sends a URL message.
 			this.bind( 'url', this.previewUrl );
+
+			this.addHistory();
+		},
+
+		/**
+		 * Given a URL query string, return the query vars contained within it
+		 *
+		 * @param {String} queryString
+		 * @returns {Object}
+		 */
+		parseQueryVars: function ( queryString ) {
+			var queryVars = {};
+			if ( queryString ) {
+				$.each( queryString.split( '&' ), function () {
+					var key, value, pair;
+					pair = this.split( '=', 2 );
+					key = decodeURIComponent( pair[0] );
+					value = pair[1] ? decodeURIComponent( pair[1] ) : null;
+					queryVars[ key ] = value;
+				} );
+			}
+			return queryVars;
+		},
+
+		/**
+		 * Add support for history for navigation in Customize preview
+		 */
+		addHistory: function () {
+			var self, backLink, pushStatePreviewUrl, popState;
+			if ( ! history.pushState ) {
+				return;
+			}
+			self = this;
+			backLink = $( '.back.button:first' );
+
+			// Push state
+			pushStatePreviewUrl = function ( url ) {
+				var state, parentLocation, queryVars;
+				state = { customizePreviewUrl: url };
+				parentLocation = location.pathname;
+				queryVars = self.parseQueryVars( location.search.substr( 1 ) );
+				queryVars.url = url;
+				parentLocation += '?' + $.param( queryVars );
+				parentLocation += location.hash;
+
+				// parent frame is for the sake of customizer loaded into iframe for Live Preview
+				if ( api.parent.targetWindow() ) {
+					api.parent.send( 'pushstate', [ state, '', parentLocation ] );
+				} else {
+					window.history.pushState( state, '', parentLocation );
+				}
+
+				if ( api.settings.theme.active ) {
+					backLink.prop( 'href', url );
+				}
+			};
+			this.previewUrl.bind( pushStatePreviewUrl );
+
+			// Pop state
+			popState = function ( state, windowLocation ) {
+				var url, queryVars;
+
+				queryVars = self.parseQueryVars( windowLocation.search.substr( 1 ) );
+
+				// Handle hitting back to themes page after having reloaded on a page in the Customizer
+				if ( ! state && /themes\.php$/.test( windowLocation.pathname ) ) {
+					location.replace( windowLocation.href );
+					return;
+				}
+
+				if ( state && state.customizePreviewUrl ) {
+					url = state.customizePreviewUrl;
+				} else if ( queryVars.url ) {
+					/*
+					 * When popped to initial state, then state is null and so
+					 * we don't have customizePreviewUrl, so we can grab it from
+					 * the URL query parameter. Note that the previewUrl value
+					 * has a validator in place which will prevent illegal
+					 * or malicious URLs from being injected into the preview.
+					 */
+					url = queryVars.url;
+				} else {
+					// Use the homepage as the preview URL as default
+					url = api.settings.url.home;
+				}
+				self.previewUrl.unbind( pushStatePreviewUrl );
+				self.previewUrl( url );
+				self.previewUrl.bind( pushStatePreviewUrl );
+
+				if ( api.settings.theme.active ) {
+					backLink.prop( 'href', url );
+				}
+			};
+
+			if ( api.parent.targetWindow() ) {
+				api.parent.bind( 'popstate', function ( args ) {
+					popState.apply( null, args );
+				} );
+			} else {
+				$( window ).on( 'popstate', function ( e ) {
+					popState( e.originalEvent.state, window.location );
+				} );
+			}
 		},
 
 		query: function() {},
@@ -992,7 +1100,7 @@
 		if ( ! $.support.postMessage || ( ! $.support.cors && api.settings.isCrossDomain ) )
 			return window.location = api.settings.url.fallback;
 
-		var parent, topFocus,
+		var topFocus,
 			body = $( document.body ),
 			overlay = body.children( '.wp-full-overlay' ),
 			title = $( '#customize-info .theme-name.site-title' ),
@@ -1009,6 +1117,12 @@
 			}
 		});
 
+		// Create a potential postMessage connection with the parent frame.
+		api.parent = new api.Messenger( {
+			url: api.settings.url.parent,
+			channel: 'loader'
+		} );
+
 		// Initialize Previewer
 		api.previewer = new api.Previewer({
 			container:   '#customize-preview',
@@ -1200,18 +1314,12 @@
 			} );
 		}
 
-		// Create a potential postMessage connection with the parent frame.
-		parent = new api.Messenger({
-			url: api.settings.url.parent,
-			channel: 'loader'
-		});
-
 		// If we receive a 'back' event, we're inside an iframe.
 		// Send any clicks to the 'Return' link to the parent page.
-		parent.bind( 'back', function() {
+		api.parent.bind( 'back', function() {
 			closeBtn.on( 'click.customize-controls-close', function( event ) {
 				event.preventDefault();
-				parent.send( 'close' );
+				api.parent.send( 'close' );
 			});
 		});
 
@@ -1225,21 +1333,22 @@
 		// Pass events through to the parent.
 		$.each( [ 'saved', 'change' ], function ( i, event ) {
 			api.bind( event, function() {
-				parent.send( event );
+				api.parent.send( 'saved' );
 			});
 		} );
 
 		// When activated, let the loader handle redirecting the page.
 		// If no loader exists, redirect the page ourselves (if a url exists).
 		api.bind( 'activated', function() {
-			if ( parent.targetWindow() )
-				parent.send( 'activated', api.settings.url.activated );
-			else if ( api.settings.url.activated )
+			if ( api.parent.targetWindow() ) {
+				api.parent.send( 'activated', api.settings.url.activated );
+			} else if ( api.settings.url.activated ) {
 				window.location = api.settings.url.activated;
+			}
 		});
 
 		// Initialize the connection with the parent frame.
-		parent.send( 'ready' );
+		api.parent.send( 'ready' );
 
 		// Control visibility for default controls
 		$.each({
diff --git src/wp-includes/js/customize-base.js src/wp-includes/js/customize-base.js
index 6c41b40..a4ddc11 100644
--- src/wp-includes/js/customize-base.js
+++ src/wp-includes/js/customize-base.js
@@ -524,8 +524,14 @@ window.wp = window.wp || {};
 		 * @param  {object} options       Extend any instance parameter or method with this object.
 		 */
 		initialize: function( params, options ) {
-			// Target the parent frame by default, but only if a parent frame exists.
-			var defaultTarget = window.parent == window ? null : window.parent;
+			var defaultTarget;
+			try {
+				// Target the parent frame by default, but only if a parent frame exists.
+				defaultTarget = window.parent === window ? null : window.parent;
+			} catch ( e ) {
+				// Handle case where window goes away in race condition. Uncaught TypeError: Cannot read property 'parent' of null
+				defaultTarget = null;
+			}
 
 			$.extend( this, options || {} );
 
diff --git src/wp-includes/js/customize-loader.js src/wp-includes/js/customize-loader.js
index 2ee0c0f..9d25924 100644
--- src/wp-includes/js/customize-loader.js
+++ src/wp-includes/js/customize-loader.js
@@ -66,7 +66,7 @@ window.wp = window.wp || {};
 			var state = e.originalEvent.state;
 			if ( state && state.customize ) {
 				Loader.open( state.customize );
-			} else if ( Loader.active ) {
+			} else if ( Loader.active && ( ! state || ! state.customizePreviewUrl ) ) {
 				Loader.close();
 			}
 		},
@@ -75,7 +75,7 @@ window.wp = window.wp || {};
 			var hash = window.location.toString().split('#')[1];
 
 			if ( hash && 0 === hash.indexOf( 'wp_customize=on' ) ) {
-				Loader.open( Loader.settings.url + '?' + hash );
+				Loader.open( Loader.settings.url.customize + '?' + hash );
 			}
 
 			if ( ! hash && ! $.support.history ){
@@ -95,6 +95,7 @@ window.wp = window.wp || {};
 		 * @param  string src URL to load in the Customizer.
 		 */
 		open: function( src ) {
+			var hash, messenger;
 
 			if ( this.active ) {
 				return;
@@ -115,19 +116,30 @@ window.wp = window.wp || {};
 			this.iframe.one( 'load', this.loaded );
 
 			// Create a postMessage connection with the iframe.
-			this.messenger = new api.Messenger({
+			this.messenger = messenger = new api.Messenger({
 				url: src,
 				channel: 'loader',
 				targetWindow: this.iframe[0].contentWindow
 			});
 
 			// Wait for the connection from the iframe before sending any postMessage events.
-			this.messenger.bind( 'ready', function() {
+			messenger.bind( 'ready', function() {
 				Loader.messenger.send( 'back' );
-			});
+			} );
 
 			this.messenger.bind( 'close', function() {
+				var goBackToThemesPage;
 				if ( $.support.history ) {
+					goBackToThemesPage = function ( e ) {
+						var state;
+						state = e.originalEvent.state;
+						if ( state && ( state.customize || state.customizePreviewUrl ) ) {
+							history.back();
+						} else {
+							$( window ).off( 'popstate', goBackToThemesPage );
+						}
+					};
+					$( window ).on( 'popstate', goBackToThemesPage );
 					history.back();
 				} else if ( $.support.hashchange ) {
 					window.location.hash = '';
@@ -160,10 +172,23 @@ window.wp = window.wp || {};
 		pushState: function ( src ) {
 			var hash;
 
-			// Ensure we don't call pushState if the user hit the forward button.
-			if ( $.support.history && window.location.href !== src ) {
-				history.pushState( { customize: src }, '', src );
-			} else if ( ! $.support.history && $.support.hashchange && hash ) {
+			if ( $.support.history ) {
+				// Ensure we don't call pushState if the user hit the forward button.
+				if ( window.location.href !== src ) {
+					history.pushState( { customize: src }, '', src );
+				}
+
+				// Allow customizer to control history of parent
+				messenger.bind( 'pushstate', function( args ) {
+					history.pushState.apply( history, args );
+				} );
+
+				// Forward popstate events to customizer
+				$( window ).on( 'popstate', function ( e ) {
+					messenger.send( 'popstate', [ e.originalEvent.state, window.location ] );
+				} );
+
+			} else if ( $.support.hashchange && hash ) {
 				hash = src.split( '?' )[1];
 				window.location.hash = 'wp_customize=on&' + hash;
 			}
diff --git src/wp-includes/theme.php src/wp-includes/theme.php
index 85119ed..1c9b3c0 100644
--- src/wp-includes/theme.php
+++ src/wp-includes/theme.php
@@ -1927,7 +1927,10 @@ function _wp_customize_loader_settings() {
 	);
 
 	$settings = array(
-		'url'           => esc_url( admin_url( 'customize.php' ) ),
+		'url'           => array(
+			'customize' => esc_url_raw( admin_url( 'customize.php' ) ),
+			'home'      => esc_url_raw( home_url( '/' ) ),
+		),
 		'isCrossDomain' => $cross_domain,
 		'browser'       => $browser,
 		'l10n'          => array(
