Index: wp-admin/edit-form-advanced.php
===================================================================
--- wp-admin/edit-form-advanced.php	(revision 29683)
+++ wp-admin/edit-form-advanced.php	(working copy)
@@ -71,11 +71,11 @@
 	 5 => isset($_GET['revision']) ? sprintf( __('Post restored to revision from %s'), wp_post_revision_title( (int) $_GET['revision'], false ) ) : false,
 	 6 => sprintf( __('Post published. <a href="%s">View post</a>'), esc_url( get_permalink($post_ID) ) ),
 	 7 => __('Post saved.'),
-	 8 => sprintf( __('Post submitted. <a target="_blank" href="%s">Preview post</a>'), esc_url( add_query_arg( 'preview', 'true', get_permalink($post_ID) ) ) ),
+	 8 => sprintf( __('Post submitted. <a target="_blank" href="%s">Preview post</a>'), esc_url( get_preview_post_link( $post_ID ) ) ),
 	 9 => sprintf( __('Post scheduled for: <strong>%1$s</strong>. <a target="_blank" href="%2$s">Preview post</a>'),
 		/* translators: Publish box date format, see http://php.net/date */
 		date_i18n( __( 'M j, Y @ G:i' ), strtotime( $post->post_date ) ), esc_url( get_permalink($post_ID) ) ),
-	10 => sprintf( __('Post draft updated. <a target="_blank" href="%s">Preview post</a>'), esc_url( add_query_arg( 'preview', 'true', get_permalink($post_ID) ) ) ),
+	10 => sprintf( __('Post draft updated. <a target="_blank" href="%s">Preview post</a>'), esc_url( get_preview_post_link( $post_ID ) ) ),
 );
 $messages['page'] = array(
 	 0 => '', // Unused. Messages start at index 1.
@@ -86,9 +86,9 @@
 	 5 => isset($_GET['revision']) ? sprintf( __('Page restored to revision from %s'), wp_post_revision_title( (int) $_GET['revision'], false ) ) : false,
 	 6 => sprintf( __('Page published. <a href="%s">View page</a>'), esc_url( get_permalink($post_ID) ) ),
 	 7 => __('Page saved.'),
-	 8 => sprintf( __('Page submitted. <a target="_blank" href="%s">Preview page</a>'), esc_url( add_query_arg( 'preview', 'true', get_permalink($post_ID) ) ) ),
+	 8 => sprintf( __('Page submitted. <a target="_blank" href="%s">Preview page</a>'), esc_url( get_preview_post_link( $post_ID ) ) ),
 	 9 => sprintf( __('Page scheduled for: <strong>%1$s</strong>. <a target="_blank" href="%2$s">Preview page</a>'), date_i18n( __( 'M j, Y @ G:i' ), strtotime( $post->post_date ) ), esc_url( get_permalink($post_ID) ) ),
-	10 => sprintf( __('Page draft updated. <a target="_blank" href="%s">Preview page</a>'), esc_url( add_query_arg( 'preview', 'true', get_permalink($post_ID) ) ) ),
+	10 => sprintf( __('Page draft updated. <a target="_blank" href="%s">Preview page</a>'), esc_url( get_preview_post_link( $post_ID ) ) ),
 );
 $messages['attachment'] = array_fill( 1, 10, __( 'Media attachment updated.' ) ); // Hack, for now.
 
Index: wp-admin/includes/ajax-actions.php
===================================================================
--- wp-admin/includes/ajax-actions.php	(revision 29683)
+++ wp-admin/includes/ajax-actions.php	(working copy)
@@ -1469,7 +1469,7 @@
 function wp_ajax_get_permalink() {
 	check_ajax_referer( 'getpermalink', 'getpermalinknonce' );
 	$post_id = isset($_POST['post_id'])? intval($_POST['post_id']) : 0;
-	wp_die( add_query_arg( array( 'preview' => 'true' ), get_permalink( $post_id ) ) );
+	wp_die( get_preview_post_link( $post_id ) );
 }
 
 /**
Index: wp-admin/includes/class-wp-posts-list-table.php
===================================================================
--- wp-admin/includes/class-wp-posts-list-table.php	(revision 29683)
+++ wp-admin/includes/class-wp-posts-list-table.php	(working copy)
@@ -678,9 +678,7 @@
 				if ( $post_type_object->public ) {
 					if ( in_array( $post->post_status, array( 'pending', 'draft', 'future' ) ) ) {
 						if ( $can_edit_post ) {
-							$preview_link = set_url_scheme( get_permalink( $post->ID ) );
-							/** This filter is documented in wp-admin/includes/meta-boxes.php */
-							$preview_link = apply_filters( 'preview_post_link', add_query_arg( 'preview', 'true', $preview_link ), $post );
+							$preview_link = get_preview_post_link( $post->ID );
 							$actions['view'] = '<a href="' . esc_url( $preview_link ) . '" title="' . esc_attr( sprintf( __( 'Preview &#8220;%s&#8221;' ), $title ) ) . '" rel="permalink">' . __( 'Preview' ) . '</a>';
 						}
 					} elseif ( 'trash' != $post->post_status ) {
Index: wp-admin/includes/meta-boxes.php
===================================================================
--- wp-admin/includes/meta-boxes.php	(revision 29683)
+++ wp-admin/includes/meta-boxes.php	(working copy)
@@ -38,21 +38,10 @@
 <div id="preview-action">
 <?php
 if ( 'publish' == $post->post_status ) {
-	$preview_link = esc_url( get_permalink( $post->ID ) );
+	$preview_link = esc_url( get_preview_post_link( $post->ID, false ) );
 	$preview_button = __( 'Preview Changes' );
 } else {
-	$preview_link = set_url_scheme( get_permalink( $post->ID ) );
-
-	/**
-	 * Filter the URI of a post preview in the post submit box.
-	 *
-	 * @since 2.0.5
-	 * @since 4.0.0 $post parameter was added.
-	 *
-	 * @param string  $preview_link URI the user will be directed to for a post preview.
-	 * @param WP_Post $post         Post object.
-	 */
-	$preview_link = esc_url( apply_filters( 'preview_post_link', add_query_arg( 'preview', 'true', $preview_link ), $post ) );
+	$preview_link = esc_url( get_preview_post_link( $post->ID ) );
 	$preview_button = __( 'Preview' );
 }
 ?>
Index: wp-admin/includes/post.php
===================================================================
--- wp-admin/includes/post.php	(revision 29683)
+++ wp-admin/includes/post.php	(working copy)
@@ -1225,9 +1225,7 @@
 
 	if ( isset( $view_post ) ) {
 		if( 'draft' == $post->post_status ) {
-			$preview_link = set_url_scheme( get_permalink( $post->ID ) );
-			/** This filter is documented in wp-admin/includes/meta-boxes.php */
-			$preview_link = apply_filters( 'preview_post_link', add_query_arg( 'preview', 'true', $preview_link ), $post );
+			$preview_link = get_preview_post_link( $post->ID );
 			$return .= "<span id='view-post-btn'><a href='" . esc_url( $preview_link ) . "' class='button button-small' target='wp-preview-{$post->ID}'>$view_post</a></span>\n";
 		} else {
 			$return .= "<span id='view-post-btn'><a href='" . get_permalink( $post ) . "' class='button button-small'>$view_post</a></span>\n";
@@ -1399,19 +1397,19 @@
 
 	if ( $locked ) {
 		if ( get_post_type_object( $post->post_type )->public ) {
-			$preview_link = set_url_scheme( add_query_arg( 'preview', 'true', get_permalink( $post->ID ) ) );
+			$query_args = array( 'preview' => 'true' );
 
 			if ( 'publish' == $post->post_status || $user->ID != $post->post_author ) {
 				// Latest content is in autosave
 				$nonce = wp_create_nonce( 'post_preview_' . $post->ID );
-				$preview_link = add_query_arg( array( 'preview_id' => $post->ID, 'preview_nonce' => $nonce ), $preview_link );
+				$query_args['preview_id'] = $post->ID;
+				$query_args['preview_nonce'] = $nonce;
 			}
 		} else {
-			$preview_link = '';
+			$query_args = false;
 		}
 
-		/** This filter is documented in wp-admin/includes/meta-boxes.php */
-		$preview_link = apply_filters( 'preview_post_link', $preview_link, $post );
+		$preview_link = get_preview_post_link( $post->ID, $query_args );
 
 		/**
 		 * Filter whether to allow the post lock to be overridden.
@@ -1610,10 +1608,7 @@
 			$query_args['post_format'] = empty( $_POST['post_format'] ) ? 'standard' : sanitize_key( $_POST['post_format'] );
 	}
 
-	$url = add_query_arg( $query_args, get_permalink( $post->ID ) );
-
-	/** This filter is documented in wp-admin/includes/meta-boxes.php */
-	return apply_filters( 'preview_post_link', $url, $post );
+	return get_preview_post_link( $post->ID, $query_args );
 }
 
 /**
Index: wp-includes/admin-bar.php
===================================================================
--- wp-includes/admin-bar.php	(revision 29683)
+++ wp-includes/admin-bar.php	(working copy)
@@ -494,9 +494,7 @@
 			&& ( $post_type_object->show_in_admin_bar ) )
 		{
 			if( 'draft' == $post->post_status ) {
-				$preview_link = set_url_scheme( get_permalink( $post->ID ) );
-				/** This filter is documented in wp-admin/includes/meta-boxes.php */
-				$preview_link = apply_filters( 'preview_post_link', add_query_arg( 'preview', 'true', $preview_link ), $post );
+				$preview_link = get_preview_post_link( $post->ID );
 				$wp_admin_bar->add_menu( array(
 					'id' => 'preview',
 					'title' => $post_type_object->labels->view_item,
Index: wp-includes/link-template.php
===================================================================
--- wp-includes/link-template.php	(revision 29683)
+++ wp-includes/link-template.php	(working copy)
@@ -1151,6 +1151,46 @@
 }
 
 /**
+ * Retrieve preview post link.
+ *
+ * Get the preview post URL. Allow any number of query args to be appended.
+ *
+ * @since 4.1.0
+ *
+ * @param int $id Optional. Post ID.
+ * @param mixed boolean/array $preview Optional. If preview query arg should be added. Or array of query args to be added.
+ * @param string boolean/array $preview_link Optional. If a link other than the permalink should be used. Used by _wp_link_page.
+ * @return string
+ */
+function get_preview_post_link( $id = 0, $preview = true, $preview_link = '' ) {
+	if ( ! $post = get_post( $id ) )
+		return;
+
+	if( get_post_type_object( $post->post_type )->public ) {
+
+		if( ! $preview_link )
+			$preview_link = get_permalink( $post->ID );
+
+		if( $preview ) {
+			$query_args = is_array( $preview ) ? $preview : array( 'preview' => 'true' );
+
+			$preview_link = add_query_arg( $query_args, $preview_link );
+		}
+	}
+
+	/**
+	 * Filter the URI of a post preview in the post submit box.
+	 *
+	 * @since 2.0.5
+	 * @since 4.0.0 $post parameter was added.
+	 *
+	 * @param string  $preview_link URI the user will be directed to for a post preview.
+	 * @param WP_Post $post         Post object.
+	 */
+	return apply_filters( 'preview_post_link', $preview_link, $post );
+}
+
+/**
  * Retrieve edit posts link for post.
  *
  * Can be used within the WordPress loop or outside of it. Can be used with
Index: wp-includes/post-template.php
===================================================================
--- wp-includes/post-template.php	(revision 29683)
+++ wp-includes/post-template.php	(working copy)
@@ -840,6 +840,7 @@
 function _wp_link_page( $i ) {
 	global $wp_rewrite;
 	$post = get_post();
+	$query_args = array();
 
 	if ( 1 == $i ) {
 		$url = get_permalink();
@@ -853,16 +854,15 @@
 	}
 
 	if ( is_preview() ) {
-		$url = add_query_arg( array(
-			'preview' => 'true'
-		), $url );
 
+		$query_args['preview'] = 'true';
+
 		if ( ( 'draft' !== $post->post_status ) && isset( $_GET['preview_id'], $_GET['preview_nonce'] ) ) {
-			$url = add_query_arg( array(
-				'preview_id'    => wp_unslash( $_GET['preview_id'] ),
-				'preview_nonce' => wp_unslash( $_GET['preview_nonce'] )
-			), $url );
+				$query_args['preview_id'] = wp_unslash( $_GET['preview_id'] );
+				$query_args['preview_nonce'] = wp_unslash( $_GET['preview_nonce'] );
 		}
+
+		$url = get_preview_post_link( $post->ID, $query_args, $url );
 	}
 
 	return '<a href="' . esc_url( $url ) . '">';
