# Share a Draft With a Secret Link

> Adds a "Share draft" link under unpublished posts that creates a secret preview address, so a client or reviewer can read the draft without a WordPress account. Links expire after a set number of days.

- Type: PHP
- Runs: Run everywhere
- Category: [Content](https://wpide.com/library/category/content/)
- Tags: drafts, preview, collaboration
- Author: WPIDE Team
- Installs: 0
- Updated: 2026-10-28
- Page: https://wpide.com/library/content-share-draft-link/

## Code

```php
// Days a shared link stays valid.
$wpide_share_days = 7;

add_filter( 'post_row_actions', function ( $actions, $post ) {
    if ( ! in_array( $post->post_status, array( 'draft', 'pending', 'future' ), true ) || ! current_user_can( 'edit_post', $post->ID ) ) {
        return $actions;
    }
    $token   = get_post_meta( $post->ID, '_wpide_share_token', true );
    $expires = (int) get_post_meta( $post->ID, '_wpide_share_expires', true );
    if ( $token && $expires > time() ) {
        $actions['wpide_share'] = sprintf(
            '<a href="%s" target="_blank" rel="noopener">Shared link</a> | <a href="%s">Stop sharing</a>',
            esc_url( add_query_arg( array( 'p' => $post->ID, 'wpide_share' => $token ), home_url( '/' ) ) ),
            esc_url( wp_nonce_url( admin_url( 'admin-post.php?action=wpide_share_draft&stop=1&post=' . $post->ID ), 'wpide_share_' . $post->ID ) )
        );
    } else {
        $actions['wpide_share'] = sprintf( '<a href="%s">Share draft</a>', esc_url( wp_nonce_url( admin_url( 'admin-post.php?action=wpide_share_draft&post=' . $post->ID ), 'wpide_share_' . $post->ID ) ) );
    }
    return $actions;
}, 10, 2 );

add_action( 'admin_post_wpide_share_draft', function () use ( $wpide_share_days ) {
    $post_id = isset( $_GET['post'] ) ? absint( $_GET['post'] ) : 0;
    check_admin_referer( 'wpide_share_' . $post_id );
    if ( ! current_user_can( 'edit_post', $post_id ) ) {
        wp_die( 'Not allowed.', 403 );
    }

    if ( ! empty( $_GET['stop'] ) ) {
        delete_post_meta( $post_id, '_wpide_share_token' );
        delete_post_meta( $post_id, '_wpide_share_expires' );
    } else {
        update_post_meta( $post_id, '_wpide_share_token', wp_generate_password( 24, false ) );
        update_post_meta( $post_id, '_wpide_share_expires', time() + $wpide_share_days * DAY_IN_SECONDS );
    }

    wp_safe_redirect( wp_get_referer() ? wp_get_referer() : admin_url( 'edit.php' ) );
    exit;
} );

// Let visitors with a valid token see the draft.
add_filter( 'posts_results', function ( $posts, $query ) {
    if ( is_admin() || empty( $_GET['wpide_share'] ) || 1 !== count( $posts ) || ( ! $query->is_main_query() && ! $query->get( 'p' ) ) ) {
        return $posts;
    }

    $post    = $posts[0];
    $token   = (string) get_post_meta( $post->ID, '_wpide_share_token', true );
    $expires = (int) get_post_meta( $post->ID, '_wpide_share_expires', true );

    if ( '' !== $token && $expires > time() && hash_equals( $token, sanitize_text_field( wp_unslash( $_GET['wpide_share'] ) ) ) && in_array( $post->post_status, array( 'draft', 'pending', 'future' ), true ) ) {
        $post->post_status = 'publish'; // In memory only, for this request.
        add_filter( 'wp_robots', 'wp_robots_no_robots' );
        nocache_headers();
    }
    return $posts;
}, 10, 2 );

// Drafts are not public, so the main query needs to be allowed to find them first.
add_action( 'pre_get_posts', function ( $query ) {
    if ( ! is_admin() && $query->is_main_query() && ! empty( $_GET['wpide_share'] ) && $query->get( 'p' ) ) {
        $query->set( 'post_status', array( 'publish', 'draft', 'pending', 'future' ) );
    }
} );

add_action( 'transition_post_status', function ( $new_status, $old_status, $post ) {
    if ( 'publish' === $new_status ) {
        delete_post_meta( $post->ID, '_wpide_share_token' );
        delete_post_meta( $post->ID, '_wpide_share_expires' );
    }
}, 10, 3 );
```

## Notes

Change $wpide_share_days; publishing the post or clicking "Stop sharing" disables the link. Shared drafts are marked noindex.

## How to use it

Install the free WPIDE plugin (https://wordpress.org/plugins/wpide/), find "Share a Draft With a Secret Link" in its snippet library and add it in one click; or create a new PHP snippet in WPIDE (or any code snippets plugin) and paste the code above. More snippets: https://wpide.com/library/
