3

I'm struggling with mod_rewrite as always. We have a number of client portals running through WordPress multisite, all accessed through a subdirectory: portal.

So for example: http://www.mydomain.com/portal/clientA/

I'd like to be able to get there just by typing http://www.mydomain.com/clientA/ and it would redirect me to http://www.mydomain.com/portal/clientA/

Here's what I have so far, and it's not producing any rewrite that I can tell:

RewriteCond %{REQUEST_URI} /portal/
RewriteCond %{REQUEST_FILENAME} -f
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule . - [S=1]

RewriteRule /clientA(/?) /portal/clientA/

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

The second part I can't touch because WordPress needs it. My pattern is also trying to anticipate someone not putting in the trailing slash, hence the (/?)

EDIT: I should also note that I don't want to create a more general rule - I'm comfortable having to add a rewrite rule for each new client and increasing the S=x number each time.

EDIT (Aug 11), So after a little more puttering this is what my .htaccess is at:

RewriteEngine On
RewriteRule ^clientA(/?) /portal/clientA/ [R]

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

# END WordPress

Needless to say it doesn't work. However, the first part works IF I delete the entire WordPress section. I need them BOTH to work simultaneously. WHAT is it about the WordPress piece that is causing the failure of the first section? I suppose it's the combination of RewriteBase and the very last rule which aliases anything else to /index.php, which frankly is a bit of a bummer. In fact I don't truly understand how that rule could even work in a multisite context, and yet it seems to.

FINAL SOLUTION thanks to LazyOne for the correct answer! For others' reference, the final solution I used was:

RewriteEngine On
RewriteRule ^clientA(/.+)? /portal/clientA$1 [R,L]
RewriteRule ^clientB(/.+)? /portal/clientB$1 [R,L]


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

# END WordPress
Tom Auger
  • 19,421
  • 22
  • 81
  • 104

2 Answers2

2

As simple as this:

RewriteCond %{REQUEST_URI} !^/portal/
RewriteRule (.*) /portal/$1 [L]

It will rewrite (internal redirect) all requests into /portal/ folder (e.g. /clientA/something => /portal/clientA/something).

If you need to do it for some clients only (or, better say, only specific folders that are clients while still having some general/common folders as is), you can use this rule for each client:

RewriteRule ^clientA(.*) /portal/clientA$1 [L]

So that .htaccess will look like this:

RewriteRule ^clientA(.*) /portal/clientA$1 [L]
RewriteRule ^clientB(.*) /portal/clientB$1 [L]

# BEGIN WordPress
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^index\.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]
</IfModule>
# END WordPress
LazyOne
  • 158,824
  • 45
  • 388
  • 391
  • Thanks for this suggestion. The only problem is that with the [L]ast directive, the WordPress rewrite's won't happen and they need to happen (even for the client folders) in order to enable pretty permalinks in WP – Tom Auger Aug 11 '11 at 14:33
  • @Tom Why "won't happen"? After seeing the `[L]` flag rewrite goes to next iteration (that is how it works -- `L` does not mean "stop rewriting and leave immediately") .. and will still reach the WordPress rewrite rules. But yes -- you can speed it up a *tiny bit* by removing the `L` flag. The rest is up to WordPress if it can handle the `/portal/clientA/something` link. **About [L] flag**: http://stackoverflow.com/questions/6797998/rewriterule-last-l-flag-not-working . If you have your rewrite rules in server config, then better to skip the [L] flag indeed. – LazyOne Aug 11 '11 at 15:09
  • Ah! Thanks for clarifying [L]. I did think it stopped processing .htaccess immediately after that. Now, if you look at my updated post, you can see it's still not working right. Is it because of RewriteBase? – Tom Auger Aug 11 '11 at 16:14
  • @Tom What does not work, which URL? How it supposed to work (accordingly to the rules you have created)? – LazyOne Aug 11 '11 at 16:22
  • Thanks for helping me. So, typing 'mydomain.com/clientA gives me a mydomain.com/portal/404 error page. HOWEVER, I have to be honest and say that I haven't implemented your solution of capturing the rest of the url and adding it back in, so I'll try that and report back. – Tom Auger Aug 11 '11 at 17:15
  • Okay I tried your solution verbatim and I still got the 404 error. Ah! Brilliant, you have to add [R] for it to work! And I guess you need [L] to prevent the WordPress rewriting during that cycle. thanks for your help! – Tom Auger Aug 11 '11 at 17:27
  • @LazyOne, I also have an url redirect question simile this one, post in wordpress.stackexchange `http://wordpress.stackexchange.com/questions/68078/why-my-htacess-rewrite-return-404`, can u help me? thanx. – fish man Oct 13 '12 at 08:53
  • 1
    @fishman Do you have the actual folder called `def` (or whatever real name it is)? Maybe you have some other rewrite rules there (in .htaccess file in that folder) that prevent you from further rewriting. The current rules seems fine to me. – LazyOne Oct 13 '12 at 10:20
  • I know this is frowned upon, but the problem with this solution is that by using the [R] (redirect) command, you lose the base path in the URL. This might be fine and dandy for some, but I found a way to get around this by updating line 147 in wp-includes/class-wp.php. I changed the line to this: `$req_uri = preg_replace(@'/^\/?p\/([-a-zA-Z0-9_]+)\//', '', $_SERVER['REQUEST_URI']);` Where my subdirectory rewrite pattern is /p/anyword/(take_to_root). Hope that helps somebody! Was a pain to find a way to do it. – Lawrence Johnson Nov 21 '12 at 22:29
0

I posted part of this as a comment, but figured it might be more clear for people who land here to do this as an answer. To the OPs point, he found a way to do this using the [R] (redirect) line, but this eliminates the subdirectory URL structure you created that would be preferable in most URL Rewrites. So, the answer previously posted is right, I'm not contesting that, but depending on your implementation, you may still get WordPress 404 errors. Here is a solution to my situation, which I think might be more common.

In my case, I needed a URL structure like this:

http://mysite.com/p/profile_name/

Each user who comes to register can create his/her own profile on the fly, and aside from a few modifications to the content, for the most part all of the WordPress content at the root is what will be displayed. Essentially, I need this:

http://mysite.com/p/profile_name/(.*)

To be rewritten to this:

http://mysite.com/$1

This is the .htaccess code posted in the other answer that WILL handle that rule correctly:

RewriteRule ^p/([-a-zA-Z0-9_]+)(/.*) $2 [L]

The problem with this is that WordPress does not care about your rewrite in terms of understanding what $2 is because WordPress uses $_SERVER['REQUEST_URI'], which no matter what you rewrite to, it's always what's in the user's browser window. The OP found a way to get around this by using the [R] option, but that causes you to lose your URL:

RewriteRule ^p/([-a-zA-Z0-9_]+)(/.*) $2 [R,L]

Redirects:

http://mysite.com/p/profile/(.*)

To:

http://mysite.com/$1

But, the user loses his unique URL this way. At best you can add a query string to at least retain the data, but then you lose the point of the pretty URLs to begin with.

I did come up with a solution; however, it involves hacking the WordPress include files :( If someone has a better way, please update. Here we go:

SOLUTION

I set my .htaccess file equal to this:

# BEGIN WordPress
<IfModule mod_rewrite.c>
RewriteEngine On
#My addition:
RewriteRule ^p/([-a-zA-Z0-9_]+)(/.*) $2 [L]

RewriteBase /
RewriteRule ^index\.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]
</IfModule>
# END WordPress

Then, I knew WordPress used the REQUEST_URI variable, so I did a recursive search, and located this line of code in /wp-includes/class-wp.php (line 147 v3.4.2):

$req_uri = $_SERVER['REQUEST_URI'];

I changed it to this:

$req_uri = preg_replace(@'/^\/?p\/([-a-zA-Z0-9_]+)\//', '', $_SERVER['REQUEST_URI']);

Which basically just tricks WordPress by filtering out the profile stuff at the beginning of the URI.

Lastly, I also needed a solution for the links within the site. For that, I added this filter:

NOTE: Some of these regexes might be a little bonkers; I was filtering out the site specific stuff, so please use this as a concept and not a copy/paste.

function mysite_wp_make_link_relative( $link ) {
    $sBaseUrl = (preg_match('/^\/(p\/[-a-zA-Z0-9_]+\/)(.*)/', $_SERVER['REQUEST_URI'], $matches)) ? $matches[1] : '';
    return preg_replace( '|https?://[^/]+/(.*)|i', '/' . $sBaseUrl . '$1', $link );
}
function rw_relative_urls() {
    $filters = array(
        'page_link', // Page link
        'home_url',
        'site_url',
        'get_site_url',
        'home_link',
    );
    foreach ( $filters as $filter ) {
        add_filter( $filter, 'mysite_wp_make_link_relative' );
    }
}

Some of those filters might not be relevant; I'm pretty sure page_link and home_url are the only important ones. Anyway, you need that code for your internal linking to work.

I hope that helps and if anyone has any comments suggestions for improving this, I would greatly appreciate it.

Lawrence Johnson
  • 3,924
  • 2
  • 17
  • 30