# Unpublish Posts on an Expiry Date

> Adds an "Unpublish on" date box to the post editor and moves expired posts back to draft automatically, for time-limited offers and announcements.

- Type: PHP
- Runs: Run everywhere
- Category: [Content](https://wpide.com/library/category/content/)
- Tags: expiry, schedule, posts
- Author: WPIDE Team
- Installs: 0
- Updated: 2026-10-28
- Page: https://wpide.com/library/unpublish-posts-on-date/

## Code

```php
add_action( 'add_meta_boxes', function () {
    add_meta_box( 'wpide-expiry', 'Unpublish on', function ( $post ) {
        wp_nonce_field( 'wpide_expiry_save', 'wpide_expiry_nonce' );
        $value = get_post_meta( $post->ID, 'wpide_expires', true );
        echo '<label class="screen-reader-text" for="wpide-expires">Unpublish on</label>';
        echo '<input type="datetime-local" id="wpide-expires" name="wpide_expires" value="' . esc_attr( $value ) . '" style="width:100%;" />';
        echo '<p class="description">Leave empty to keep the post published.</p>';
    }, 'post', 'side' );
} );

add_action( 'save_post_post', function ( $post_id ) {
    if ( ! isset( $_POST['wpide_expiry_nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['wpide_expiry_nonce'] ) ), 'wpide_expiry_save' ) ) {
        return;
    }

    if ( ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) || ! current_user_can( 'edit_post', $post_id ) ) {
        return;
    }

    $value = isset( $_POST['wpide_expires'] ) ? sanitize_text_field( wp_unslash( $_POST['wpide_expires'] ) ) : '';

    if ( preg_match( '/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}$/', $value ) ) {
        update_post_meta( $post_id, 'wpide_expires', $value );
    } else {
        delete_post_meta( $post_id, 'wpide_expires' );
    }
} );

add_action( 'init', function () {
    if ( ! wp_next_scheduled( 'wpide_unpublish_expired_posts' ) ) {
        wp_schedule_event( time() + 300, 'hourly', 'wpide_unpublish_expired_posts' );
    }
} );

add_action( 'wpide_unpublish_expired_posts', function () {
    $expired = get_posts( array(
        'post_type'   => 'post',
        'post_status' => 'publish',
        'fields'      => 'ids',
        'numberposts' => 50,
        'meta_query'  => array(
            array( 'key' => 'wpide_expires', 'value' => '', 'compare' => '!=' ),
            array( 'key' => 'wpide_expires', 'value' => current_time( 'Y-m-d\TH:i' ), 'compare' => '<=' ),
        ),
    ) );

    foreach ( $expired as $post_id ) {
        wp_update_post( array( 'ID' => $post_id, 'post_status' => 'draft' ) );
    }
} );
```

## Notes

A check runs every hour with WP-Cron, so posts expire within an hour of the chosen time. Dates use the site time zone.

## How to use it

Install the free WPIDE plugin (https://wordpress.org/plugins/wpide/), find "Unpublish Posts on an Expiry Date" 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/
