Take the 2-minute tour ×
WordPress Development Stack Exchange is a question and answer site for WordPress developers and administrators. It's 100% free, no registration required.

I noticed a strange behavior in WordPress where it automatically 301 redirects certain URL structures for posts.

For example, I have a post entry for: mysite.com/999/about-us

Then, you would think the following link would produce a 404: mysite.com/567891/about-us-1

However, WordPress automatically redirects to /999/about-us

Is there a way to turn off this specific kind of redirect? It seems like WP is looking for the closest "like" slug. Note that I have no entries for about-us-1 in the posts table, and no revisions or anything that would cause WP to forward like this.

I found this snippit that turns off canonical 301 redirects:

remove_filter('template_redirect','redirect_canonical');

However, this isn't the solution because it produces undesired URL structures on the site.

share|improve this question
add comment

2 Answers

This appears to have to do with redirect_guess_404_permalink() called on line 96 of wp-includes/canonical.php. Just to test, I added a return false; to the first line of the redirect_guess_404_permalink() function, and that seemed to stop this odd behavior. I'm poking around a bit, but so far I don't see a good way to fix this without editing that core WordPress file (which I am personally opposed to doing in a production environment, since it makes core updates more difficult and accident prone). I wish there was a good filter/action hook to use in redirect_guess_404_permalink to shortcut this behavior. I'll keep poking a bit and update this answer if I find a good solution.

EDIT

I may have found a fix, which I tested briefly and worked.

Edit (again) Added some logic (which replicates the checks done in canonical.php to perform the redirect) to check for certain query parameters. Not as well tested as the last edit, so let me know how it works. If not 100% working, it should at least get you in the right direction (and check canonical.php).

add_action('template_redirect', 'remove_404_redirect', 1);
function remove_404_redirect(){
  if (is_404()){
    $id = max(get_query_var('p'), get_query_var('page_id'), get_query_var('attachment_id'));
    $redirect_url = false;
    if ($id && $redirect_post = get_post($id)) {
      $post_type_obj = get_post_type_object($redirect_post->post_type);
      if ($post_type_obj->public)
        $redirect_url = get_permalink($redirect_post);
    }
    if (!$redirect_url)
      remove_filter('template_redirect', 'redirect_canonical');
  }
}

This will work because the undesired redirects are only occurring if the page is initially a 404, so we just check for 404 and, if it is, remove the redirecting filter. YAY!

share|improve this answer
    
Awesome! Thanks for finding that - I dug for a while but wasn't able to find it. An elegant solution sounds spectacular. –  Alex Cook Mar 16 '12 at 6:24
    
Hey @AlexCook , check my edit of the answer. I think that should work to fix this issue. –  William Mar 16 '12 at 12:38
    
It works! Thanks so much! –  Alex Cook Mar 19 '12 at 19:41
    
Looks like using this broke ?p=xxx forwarding, which is potentially a big issue. When I added this hack, it prevented those types of forwards from working. –  Alex Cook Mar 20 '12 at 3:13
    
@AlexCook Good catch, I updated my answer again. While not as elegant, it should resolve this issue. It replicates more exactly the checks done in canonical.php in the is_404() check around line 83. Hope that fixes it for good :) –  William Mar 21 '12 at 14:15
add comment

Here's a simpler take. Only turn off the redirecting if none of the query_vars are present. In this way we get to keep the functionality, without the need to duplicate the logic already at redirect_canonical.

add_filter('redirect_canonical', 'no_redirect_on_404', 10, 2);
function no_redirect_on_404($redirect_url, $requested_url){
    $id = max(get_query_var('p'), get_query_var('page_id'), get_query_var('attachment_id'), 
            get_query_var('day'), get_query_var('monthnum'), get_query_var('year'));
    if (is_404() && !$id){
        return false;
    }
    return $redirect_url;
}
share|improve this answer
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.