Internal happenings explained
When you look at wp_login_form()
, then you will see, that the redirect
argument will ask for an absolute URl. So either use admin_url()
or site_url()
if you want to link internally.
The action
/ target of this form is not meant to be changed (unless you set echo
to FALSE
and use some \DOMDocument
replacement or some Regex to change it in the output string):
action="' . esc_url( site_url( 'wp-login.php', 'login_post' ) ) . '" method="post"
When you then look at wp_login.php
and the case : 'login'
|default
, you will note two things:
There's a filter to adjust the redirect, which might override anything set in your arguments. Keep in mind that this might even switch depending on the user who performs the login action (typically used to redirect users of different roles to different welcome screens):
apply_filters( 'login_redirect', $redirect_to, $requested_redirect_to, $user );
Then the user gets checked if it is authenticated and not an erroneous object. Depending on that, if the request is not directed at the /wp-admin
side of things, the following function is called:
exit( wp_safe_redirect( $target ) );
…and using wp_safe_redirect()
means that you can not link outside (figured that from @TheDeadMedic comment – thanks!).
Solutions
As usual in WordPress, you have options:
- This function is inside
pluggable.php
, which means you can replace it with your own function. This is not very stable (every other plugin can do the same), but it's ok if you use it on your personal site, document this is in place and fix things when there's a conflict. More info in this question and the answer by @ChipBennet on it.
The better solution is to look at wp_validate_redirect()
which is internally used by wp_safe_redirect()
and leverage the filter that WP offers to extend the list of allowed hosts.
// End of `wp_validate_redirect()`
$allowed_hosts = (array) apply_filters(
'allowed_redirect_hosts',
array( $wpp['host'] ),
isset( $lp['host'] ) ? $lp['host'] : ''
);
if (
isset($lp['host'])
&& ( !in_array($lp['host'], $allowed_hosts)
&& $lp['host'] != strtolower($wpp['host']))
)
$location = $default;
In your case a (mu-)plugin to allow that redirect would look like the following:
<?php /* Plugin Name: Allow external login redirect */
add_filter( 'allowed_redirect_hosts', function( Array $hosts, $check )
{
return $hosts + [ 'http://etomv2.bambergmarketing.com' ];
}, 10, 2 );
Also take a look at the second argument inside wp_safe_redirect( $target, $fallback )
that is used for wp_validate_redirect()
. It is set as filter. That means, that you can redirect to another external URl in case your login fails. Or you can redirect locally to some error page (or just the current page, which is the default):
apply_filters( 'wp_safe_redirect_fallback', admin_url(), $status )
The second solution is what I would use. You might also want to use it in case you are using some external OAuth provider that you need to validate users against.
Note
In case you read the docs and wonder about login_post
as "scheme" in there: The site_url()
function is a wrapper for get_site_url()
which uses set_url_scheme()
internally. There login_post
, login
and rpc
are equal to admin
, which adjusts the action properly:
$scheme = is_ssl() || force_ssl_admin() ? 'https' : 'http';
redirect
param withwp_login_form
on a "vanilla" install and it works exactly as expected - thus it must be a rogue plugin and/or your theme. The only way to find the culprit is disable all of them, then one by one re-activate and test until it stops working again!wp_safe_redirect
inwp-login.php:840