2

I'd like to exclude a path that matches a rule from booting WordPress. The normal way I'd approach this is using the last flag [L] in a rule before all the others.

To keep things simple in this example, I'll just pretend I want to match a simple path /foo/.

<IfModule mod_rewrite.c>
RewriteEngine On
RewriteRule ^foo/?$ - [L]
RewriteBase /
RewriteRule ^index\.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]
</IfModule>

However, this does not work. A few other options are suggested in this older Stack Overflow post, but none of them work (neither for myself nor anyone in the comments of that post).

I did try this rewrite condition instead of the rule:

RewriteCond %{REQUEST_URI} ^/?(foo/.*)$

As well as adding ErrorDocument 401 default to the end of the .htaccess document.

  • How exactly is it not working? Is WordPress showing its error 404 page? – Sally CJ Jun 27 at 5:49
  • Yeah, the redirect just fails, so if the page exists it goes to /foo, if the page doesn't exist it displays the theme's 404. Basically the rule doesn't preclude Wordpress from being routed to. – Orun Jun 27 at 16:26
  • I'm actually also getting the error 404, but that should be the expected outcome in my case since the path (/foo) doesn't exist and the rewrite is not being redirected to any scripts. Try deactivating plugins and/or try a default theme? Edit: I mean, I'm seeing the default error page that's served by Apache and not WordPress. – Sally CJ Jun 27 at 16:35
  • 1
    Yes, the RewriteRule ^foo/?$ - [L] and I put it in the exact same place as in the question - below the RewriteEngine On. But that IfModule block is the only content of my .htaccess file. Could it be a caching issue? Try clearing your browser and site cache, and try a different browser as well, and see if that helps? – Sally CJ Jun 27 at 20:42
  • 1
    Thanks for testing, must be something in my apache config – Orun Jun 28 at 15:49
2
+50

One small hiccup is that you'll need to update RewriteRule ./ in that second to last line. Here is an updated (and tested) snippet for you:

# BEGIN WordPress
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^index\.php$ - [L]
RewriteCond %{REQUEST_URI} !^/foo/.*$
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ./ /index.php [L]
</IfModule>

# END WordPress

I tested both:

Hope that helps!!

  • 1
    That was it, thank you. Although I'm not sure why . wouldn't match ./, it should have matched both. For anyone else having this issue in the future: I optimized slightly by changing to ./? and wrote a quick service to check the .htaccess file and make sure some plugins or updates didn't overwrite/reset it. – Orun Jul 2 at 21:07
  • 1
    @Orun not a good idea to set a service which disallows modification for the file. instead, only that part should be monitored, because many useful plugins write some other blocks, which shouldnt be disallowed. – T.Todua Jul 4 at 20:01
  • Yeah I agree. The file is still totally writable. I actually already had a service that writes my own block to .htaccess within my theme, so I just extended that. It only looks for a particular line, modifies it if a condition is met and then logs the modification in a special log. – Orun Jul 5 at 17:21
2

You should use RewriteCond instead of RewriteRule. Use this:

RewriteCond %{REQUEST_URI} !/foo/

So, for example, the full code could be like this:

# BEGIN WordPress
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^index\.php$ - [L]
RewriteCond %{REQUEST_URI} !/foo/
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]
</IfModule>
# END WordPress
1

The following rule works for me

RewriteRule ^(foo)($|/) - [L]

meaning, any path beginning with foo like /foo/, /foo/bar/ or whatever, leads to the Apache 404 instead of the themes 404; under the assumption that there is no actual directory with that path.

The rule has to be before the last standard line of the WordPress block:

RewriteRule . /index.php [L]

And it doesn't really matter, if it is after RewriteBase /, it can be before it; it was previously just hastily and sloppily worded on my part.

Which would give you the chance to leave the block between # BEGIN WordPress and # END WordPress untouched, if you'd like to do so. By putting

<IfModule mod_rewrite.c>
RewriteRule ^(foo)($|/) - [L]
</IfModule>

in front of # BEGIN WordPress.

You might want to prevent the path /foo/ from being created in WordPress, this came to mind:

add_filter( 'wp_unique_post_slug', 'no_more_foo_wp_unique_post_slug', 2, 6 );
function no_more_foo_wp_unique_post_slug( 
  $slug, $post_ID, $post_status, $post_type, $post_parent, $original_slug 
) {
  if ( $post_parent === 0 ) {
    $pattern = '/^foo$/';
    $replace = 'bar';
    $slug   = preg_replace( $pattern, $replace, $slug );
  }
  return $slug;
}
  • You're right that I should have set my rule after the base, not sure why I did it before in this post! Actually in the production .htaccess it was set after. Thanks for mentioning though. It was an oversight from me when I was editing the markdown here. – Orun Jul 5 at 17:23
  • @Orun I clarified my answer a bit. – Nicolai Jul 5 at 19:58

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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