add_rewrite_rule confusion

Expanding on my comment:

The Rewriting API is for rewriting URLs, not redirecting. So the URL won’t change. You’re just telling WordPress that /testpage/ should load page_id 81, but the URL will stay /testpage/.

Regardless, I tried the below, (where 501 is an ID that exists on my install) and it worked fine, loading that page’s content and template:

function wpse_283104_rewrite() {
    add_rewrite_rule('^testpage/?', 'index.php?page_id=501', 'top');
}
add_action( 'init', 'wpse_283104_rewrite' );

Make sure you’re flushing rewrite rules though. It won’t work unless you do.

If you just want to do redirects, then .htaccess or a plugin is probably the way to go.

If you really want to use a rewrite rule for a redirect, then what you need to do is rewrite the URL to set a custom query var, then check that query var in the template_redirect hook, and perform a redirect.

First up, we’ll create a custom query var called wpse_283104_redirect. A query var is a query string that WordPress recognises. We need to tell WordPress to see?wpse_283104_redirect=1in a rewrite so that we can check it later. This is done by filtering thequery_vars` array:

function wpse_283104_query_vars( $vars ) {
    $vars[] = 'wpse_283104_redirect';

    return $vars;
}
add_filter( 'query_vars', 'wpse_283104_query_vars' );

Next is the rewrite rule:

function wpse_283104_rewrite() {
    add_rewrite_rule( '^testpage/?', 'index.php?wpse_283104_redirect=1', 'top' );
}
add_action( 'init', 'wpse_283104_rewrite' );

Now /testpage/ will load the homepage, but since we registered wpse_283104_redirect we can see if we’re on /testpage/ (or whatever the rewritten URL is) with [get_query_var()][2].

So inside the template_redirect hook we’ll perform this check and redirect:

function wpse_283104_redirect() {
    if ( get_query_var( 'wpse_283104_redirect' ) == '1' ) {
        wp_redirect( 'http://example.com' );
        exit;
    }
}
add_action( 'template_redirect', 'wpse_283104_redirect' );