# Partager un brouillon sans créer de compte : lien de prévisualisation

> Générez un lien signé et temporaire vers un brouillon pour un client, sans lui créer de compte WordPress ni exposer le contenu publiquement.

- Auteur : Clément Hadrot
- Publié le : 2025-10-01
- Mis à jour le : 2025-10-01
- Catégorie : Tips
- URL : https://wpmoderne.dev.wordpress-developpement.fr/tips/partager-brouillon-lien-previsualisation-signe/

## L’essentiel

- Un jeton unique stocké en meta, avec expiration
- Aucun compte WordPress à créer pour le destinataire
- Le lien natif de prévisualisation reste réservé aux comptes connectés

Un client textile voulait valider chaque fiche produit avant publication, mais refusait catégoriquement qu'on lui crée un compte WordPress : « je ne veux pas apprendre un back-office pour trois clics par semaine ». Le lien de prévisualisation natif de WordPress, généré par `get_preview_post_link()`, ne fonctionne pourtant que pour un utilisateur connecté disposant des droits sur le contenu : impossible de le partager tel quel à quelqu'un d'extérieur.

La solution consiste à générer un jeton unique, stocké en meta avec une date d'expiration, et à vérifier ce jeton via une règle de réécriture ou un paramètre d'URL contrôlé, sans jamais exposer le brouillon publiquement pour autant.

## Générer un jeton de partage

Le jeton est créé à la demande, généralement depuis une métabox ou un bouton dans l'écran d'édition, et enregistré avec sa date d'expiration :

```
function generer_lien_previsualisation( $post_id ) {
    $jeton = wp_generate_password( 32, false );
    $expiration = time() + ( 72 * HOUR_IN_SECONDS );

    update_post_meta( $post_id, '_jeton_previsualisation', $jeton );
    update_post_meta( $post_id, '_jeton_previsualisation_expire', $expiration );

    return add_query_arg(
        [
            'apercu_client' => $jeton,
        ],
        home_url( '?p=' . $post_id )
    );
}
```

## Vérifier le jeton avant d'afficher le brouillon

Le hook `template_redirect` intercepte la requête avant tout affichage, vérifie le jeton et, si tout concorde, autorise temporairement l'affichage du brouillon pour cette seule requête :

> L'essentiel à retenir : Un jeton unique stocké en meta, avec expiration ; Aucun compte WordPress à créer pour le destinataire ; Le lien natif de prévisualisation reste réservé aux comptes connectés

```
add_action( 'template_redirect', function () {
    if ( empty( $_GET['apercu_client'] ) || ! is_singular() ) {
        return;
    }

    $post_id = get_queried_object_id();
    $jeton_attendu = get_post_meta( $post_id, '_jeton_previsualisation', true );
    $expiration = (int) get_post_meta( $post_id, '_jeton_previsualisation_expire', true );

    if ( ! hash_equals( (string) $jeton_attendu, (string) $_GET['apercu_client'] ) ) {
        wp_die( __( 'Lien de prévisualisation invalide.', 'mon-theme' ), 403 );
    }

    if ( time() > $expiration ) {
        wp_die( __( 'Ce lien de prévisualisation a expiré.', 'mon-theme' ), 410 );
    }

    add_filter( 'posts_results', function ( $posts ) use ( $post_id ) {
        foreach ( $posts as $post ) {
            if ( (int) $post->ID === $post_id && 'draft' === $post->post_status ) {
                $post->post_status = 'publish'; // Autorisation temporaire, en mémoire seulement
            }
        }
        return $posts;
    } );
} );
```

## Sécuriser le jeton

L'usage de `hash_equals()` plutôt qu'une simple comparaison `===` évite les attaques par mesure de temps sur la comparaison de chaînes. Le jeton doit aussi être suffisamment long : trente-deux caractères aléatoires générés par `wp_generate_password()` avec le second paramètre à `false` pour exclure les caractères spéciaux qui poseraient problème dans une URL.

- Ne jamais indexer ces URL : ajouter un en-tête `X-Robots-Tag: noindex` lors de l'affichage via ce jeton.
- Invalider le jeton après une seule consultation si le contenu est particulièrement sensible, ou le laisser réutilisable pendant sa fenêtre de validité pour un usage plus souple.
- Supprimer les deux metas une fois le contenu publié, pour ne pas laisser traîner un jeton devenu inutile.

```
add_action( 'transition_post_status', function ( $nouveau_statut, $ancien_statut, $post ) {
    if ( 'publish' === $nouveau_statut ) {
        delete_post_meta( $post->ID, '_jeton_previsualisation' );
        delete_post_meta( $post->ID, '_jeton_previsualisation_expire' );
    }
}, 10, 3 );
```

> Un lien de prévisualisation signé n'est pas une porte dérobée si son jeton est long, comparé avec hash_equals et limité dans le temps : c'est un compromis raisonnable entre confort client et sécurité du contenu.

## Limites de cette approche

Ce mécanisme reste adapté à un partage ponctuel avec un nombre limité de destinataires de confiance. Pour un usage massif, avec des dizaines de contenus partagés en parallèle, une table dédiée plutôt que des metas répétées sur chaque article facilite la purge automatique des jetons expirés via une tâche planifiée.

## En résumé

Un jeton signé, une vérification stricte à l'affichage et une expiration claire suffisent à donner un accès temporaire et sécurisé à un brouillon, sans jamais avoir à créer de compte WordPress pour un client qui n'en a pas l'usage au quotidien.
