You’ll want to use a rewrite rule combined with a custom query variable and hooking into template_redirect
to catch your /go/...
urls.
First up: the rewrite rule. This just maps a regex pattern to an set of query params on index.php
. I’m going to use a post ID here rather than a slug. Makes things a bit easier in our catch/redirect function.
add_action('init', 'wpse205416_add_rule');
function wpse205416_add_rule()
{
add_rewrite_rule(
'^go/(\d+)/?$',
'index.php?wpse205416_go=$matches[1]',
'top'
);
}
the wpse205416_go
bit is significant, we’ll fetch it later. WordPress discards query variables it does’t know about so we have to register it by hooking into query_vars
.
add_filter('query_vars', 'wpse205416_add_var');
function wpse205416_add_var(array $vars)
{
$vars[] = 'wpse205416_go';
return $vars;
}
And now we can hook into template_redirect
and look for our wpse205416_go
variable. If present, fetch the post (to make sure it exists) and fetch the URL and redirect. The onlything interesting here is the _wpse205416_not_found
function which just makes sure we actually 404. Calling $wp_query->set_404()
doesn’t send the correct HTTP status.
function _wpse205416_not_found()
{
global $wp_query;
status_header(404);
$wp_query->set_404();
}
add_action('template_redirect', 'wpse205416_catch_go');
function wpse205416_catch_go()
{
$id = get_query_var('wpse205416_go');
if (!$id) {
return; // not a redirect
}
$post = get_post($id);
if (!$post) {
return _wpse205416_not_found(); // not a valid post ID, 404 it!
}
// whatever your meta key is
$url = get_post_meta($post->ID, '_wpse205416_go_url', true);
if (!$url) {
return _wpse205416_not_found(); // no url, 404 it!
}
// always exit after redirecting
wp_safe_redirect($url, 302);
exit;
}
If you’re interested in learning more about the rewrite api, I wrote a fairly extensive tutorial about it.