# Verify Email Addresses of New Users

> Sends new members a confirmation link and blocks them from logging in until they click it, which keeps fake and mistyped email addresses out.

- Type: PHP
- Runs: Run everywhere
- Category: [Users](https://wpide.com/library/category/users/)
- Tags: registration, email, verification
- Author: WPIDE Team
- Installs: 0
- Updated: 2026-10-28
- Page: https://wpide.com/library/email-verification-for-new-users/

## Code

```php
// Hours a verification link stays valid.
$wpide_verify_hours = 48;

$wpide_send_verify = function ( $user_id ) {
    $user  = get_userdata( $user_id );
    $token = wp_generate_password( 32, false );

    update_user_meta( $user_id, 'wpide_email_verify', array( 'hash' => wp_hash( $token ), 'time' => time() ) );

    $link = add_query_arg( array( 'wpide_verify' => $user_id, 'key' => $token ), wp_login_url() );

    wp_mail(
        $user->user_email,
        sprintf( '[%s] Please confirm your email address', wp_specialchars_decode( get_bloginfo( 'name' ), ENT_QUOTES ) ),
        sprintf( "Hi %s,\n\nPlease confirm your email address to activate your account:\n%s\n\nIf you did not sign up, you can ignore this email.", $user->display_name, $link )
    );
};

add_action( 'user_register', function ( $user_id ) use ( $wpide_send_verify ) {
    // Skip accounts made by administrators, WP-CLI or scheduled tasks.
    if ( current_user_can( 'create_users' ) || ( defined( 'WP_CLI' ) && WP_CLI ) || wp_doing_cron() ) {
        return;
    }

    $wpide_send_verify( $user_id );
}, 20 );

add_filter( 'authenticate', function ( $user ) {
    if ( $user instanceof WP_User && get_user_meta( $user->ID, 'wpide_email_verify', true ) ) {
        return new WP_Error( 'wpide_unverified', '<strong>Please confirm your email address.</strong> We sent you a link when you signed up.' );
    }

    return $user;
}, 60 );

add_action( 'login_init', function () use ( $wpide_verify_hours, $wpide_send_verify ) {
    if ( empty( $_GET['wpide_verify'] ) || empty( $_GET['key'] ) ) {
        return;
    }

    $user_id = absint( $_GET['wpide_verify'] );
    $data    = get_user_meta( $user_id, 'wpide_email_verify', true );
    $key     = sanitize_text_field( wp_unslash( $_GET['key'] ) );

    if ( ! is_array( $data ) ) {
        wp_safe_redirect( add_query_arg( 'wpide_verified', 'already', wp_login_url() ) );
        exit;
    }

    if ( time() - (int) $data['time'] > $wpide_verify_hours * HOUR_IN_SECONDS ) {
        $wpide_send_verify( $user_id );
        wp_safe_redirect( add_query_arg( 'wpide_verified', 'expired', wp_login_url() ) );
        exit;
    }

    if ( hash_equals( $data['hash'], wp_hash( $key ) ) ) {
        delete_user_meta( $user_id, 'wpide_email_verify' );
        wp_safe_redirect( add_query_arg( 'wpide_verified', 'yes', wp_login_url() ) );
        exit;
    }
} );

add_filter( 'login_message', function ( $message ) {
    $state    = isset( $_GET['wpide_verified'] ) ? sanitize_key( $_GET['wpide_verified'] ) : '';
    $messages = array(
        'yes'     => 'Thanks, your email address is confirmed. You can log in now.',
        'already' => 'This link was already used. You can log in.',
        'expired' => 'That link had expired, so we have emailed you a new one.',
    );

    if ( isset( $messages[ $state ] ) ) {
        $message .= '<p class="message">' . esc_html( $messages[ $state ] ) . '</p>';
    }

    return $message;
} );
```

## Notes

Most useful when members choose their own password at sign-up; accounts created by administrators and users who existed before activation are treated as verified.

## How to use it

Install the free WPIDE plugin (https://wordpress.org/plugins/wpide/), find "Verify Email Addresses of New Users" 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/
