How to Use wp_redirect and wp_safe_redirect in WordPress

Redirecting users is a routine task — after a form submission, after login, when content moves. WordPress provides two functions for this: wp_redirect() and the safer wp_safe_redirect(). Knowing when to use each one matters.

Problem: How do you redirect users to another URL in WordPress without calling header() directly?

Solution: Use wp_redirect() for any URL or wp_safe_redirect() to restrict redirects to the same site — always call exit() immediately after to stop further script execution.

wp_redirect() sends any URL. wp_safe_redirect() restricts the destination to the same site (or a whitelist of allowed hosts), which prevents open redirect attacks:

// Redirect to an external URL (use sparingly)
wp_redirect( 'https://example.com', 302 );
exit;

// Redirect within the same site — safe by default
wp_safe_redirect( home_url( '/thank-you/' ), 302 );
exit;

// Permanent redirect (tells search engines the URL has moved)
wp_redirect( home_url( '/new-page/' ), 301 );
exit;

A common pattern — redirect non-logged-in users away from a protected page:

add_action( 'template_redirect', 'redirect_guests_from_dashboard' );

function redirect_guests_from_dashboard() {
    if ( is_page( 'dashboard' ) && ! is_user_logged_in() ) {
        wp_safe_redirect( wp_login_url( get_permalink() ) );
        exit;
    }
}

To allow additional external hosts with wp_safe_redirect():

add_filter( 'allowed_redirect_hosts', function( $hosts ) {
    $hosts[] = 'app.example.com';
    return $hosts;
} );

NOTE: Always call exit immediately after wp_redirect() or wp_safe_redirect(). Without it, PHP continues executing the current script even though the redirect header has been sent. Use template_redirect (not init) for page-level redirects to avoid interfering with AJAX and REST API requests.