Excluding a directory from WordPress

I’m working on a site, and we have been storing archive copies of our newsletter on our site like this:

http://example.com/newsletter/2015-05/

The site itself has been a homegrown PHP solution which I’m finally, after years, converting to WordPress. One challenge is this folder structure. I don’t really want to mess with it. But I need http://example.com to run with WordPress. So I need to exclude the newsletter directory (folder, whatever).

I looked around and found a great tip on excluding a directory which shows just how to write the proper code into my .htaccess file, like this:

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

The salient line that’s added is saying the REQUEST_URI must NOT be either ‘mydir’ or ‘mydir/whatever’. It’s great, works a treat.

But there’s one small problem.

If you change your permalinks, your rewrite rules are flushed, and the .htaccess file is overwritten. Now, you could just make sure your .htaccess file is not writable, but where’s the fun in that? Instead I want to tap into WordPress’s WP_Rewrite API and make sure that, should we change the file, this rule will be preserved.

Luckily the API has lots of hooks so we can manipulate things. I initially looked using the add_rewrite_rule function to add to the rewrite_rules array—since WordPress at one point has an array of all the rewrite rules that seemed handy. But then I noticed that the action to hook into that only seems to be working for rewrite RULES, not rewrite CONDITIONS.

Someone had already asked if WordPress had a function to generate a rewrite condition on StackExchange. The answer given was very helpful, leading me to use the mod_rewrite_rules hook to mess with the entire string of content just before it gets written to .htaccess.

First I need to define my new rule, like this:

// Create our condition.
$exclude_newsletter_cond = PHP_EOL . 'RewriteCond %{REQUEST_URI} !^/(newsletter/.*)$';

Then I’m going to insert this condition just after the line with RewriteBase—that’s a unique point that comes somewhere in the midst of the rules. This means the condition will get inserted BEFORE the first RewriteRule. But it works just fine. (YMMV)

I’m going to do a preg_replace to search for ‘RewriteBase.+’ and replace it that with itself, plus the string I created above. My search looks like this—remember you need the forward slashes in your search string for preg_replace:

$search = '/RewriteBase.+/';

 

And then we just replace it. You can see this in the full code example below.

 

/**
* We need to parse the rewrite rules string, and insert something
* INSIDE it. If this were a rewrite RULE, then we could insert it
* into the array that gets prepped before getting converted into a
* string. If you want to do that, may want to check out the Codex:
* https://codex.wordpress.org/Class_Reference/WP_Rewrite
* (Note: this would be a different hook , too)
*
* In our case, since we are creating a CONDITION, we need to muscle
* our way into the string.
*/
function my_custom_htaccess( $rules ){

// Create our condition.
$exclude_newsletter_cond = PHP_EOL . 'RewriteCond %{REQUEST_URI} !^/(newsletter/.*)$';

// Look for a place to insert it.
// We are going to put this just after the RewriteBase line.
// This is not in the same place as all the other RewriteCond statements,
// But it seems to work.
$search = '/RewriteBase.+/';

// Replace w/ found string, plus condition
$replace = '$0' . $exclude_newsletter_cond;

// Rock and roll.
$rules = preg_replace($search, $replace, $rules);

return $rules;
}
add_filter('mod_rewrite_rules', 'my_custom_htaccess');

This produces the following set of WordPress rules:

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

So yes, the RewriteCond does come before the first RewriteRule, but it worked locally. I’ll update this post if I find other issues.

If you found this, and it was helpful, let me know!

Join the Conversation

2 Comments

  1. I was wondering if I could use this rule to exclude ?wc-api=WC_Gateway_Paypal
    as it’s being rewritten as wc-api/WC_Gateway_Paypal/ and my paypal IPN notifications won’t work as they should.
    Many thanks, Ana

  2. Hi Ana,

    I’m not sure what’s happening with your IPN notifications — debugging those can be tricky. My favorite trick is to send an email w/ a var dump of relevant array info at the point in the code I’m trying to fix.

    But you could try it. I think your RewriteCond would look like this:
    RewriteCond %{REQUEST_URI} !^.+wc-api=WC_Gateway_Paypal.+

    That means, roughly, “if the REQUEST_URI does NOT feature the pattern ‘(0 or more characters before)(wc-api=WC_Gateway_Paypal)(0 or more characters after)'”.

    That might do the trick. But I might see if there’s another way to double-check your IPN notifications — that might not be the problem. Good luck!

Leave a comment

Your email address will not be published. Required fields are marked *