CodeIgniter Forums

Full Version: Mod_Rewrite Voodoo Problem - not just your average magic... [solved - but interesting!]
You're currently viewing a stripped down version of our content. View the full version with proper formatting.

El Forum

[eluser]Narkboy[/eluser]
I'm having a night mare with 3 rewrite requirements for a site. Each works on it's own, but I cannot make them work together!

The site is a little odd:

DNS points several domains to the same CI installation - each for it's own country. foo.it, foo.es, foo.co.uk, foo.ca, foo.nl etc. etc. CI grabs the relevant language / localisation and then displays pages. This adds a level of complexity to the rewrite because they must deal with several different domains.

There are 3 rewrite requirements (ordered correctly, I believe, but then it's not working so maybe not):

1) If no subdomain exosts, redirect to www.foo.<ext>. This is mainly for search engines. Everything on the main site should be served from www.foo.<ext>

2) Apart from www, there are any number of possible subdomains that need to be handled by requesting data from index.php/site/<sub-domain>

3) The usual removal of index.php needs to happen.

Examples:

Req 1)
http://foo.es -> should REDIRECT to http://www.foo.es
http://foo.it -> should REDIRECT to http://www.foo.it

Req 2)
http://bar.foo.ie -> should appear the same but load from http://www.foo.ie/site/bar
http://bar.foo.ie/this/topic -> should appear the same but should load from http://www.foo.ie/site/bar/this/topic

Req 3) Pretty obvious.

Req 1 - Rewrite:
Code:
RewriteCond %{HTTP_HOST} ^foo\.([a-z\.]{2,5})$ [NC]                    
RewriteRule ^(.*)$ http://www.foo.%1/$1 [R,NS]

This works (on it's own): if HTTP_HOST starts with 'foo', there is no subdomain, so redirect to www.foo. I've not tackled what happens if you enter "foo.foo.co.uk" for example.

Req 2- Rewrite:
Code:
RewriteCond %{HTTP_HOST} !^(www\.foo.*)$ [NC]                    
RewriteCond %{HTTP_HOST} ^([a-z\-\_]*)\.foo\.([a-z\.]{2,5})$ [NC]            

RewriteRule ^(.*)$ index.php/site/%1

This works (on it's own): if the HTTP_HOST is not "www.foo<anything>" AND if the HOST has a subdomain, then request from index.php/site/<subdomain>. I've not tackled appending the rest of the URI as yet.

Req 3 - Rewrite:
Code:
RewriteCond $1 !^(index\.php|img|style|license\.txt|robots\.txt|script) [NC]
RewriteRule ^(.*)$ index.php/$1 [L]

This also works (on all my CI apps) to resolve anything except the words listed as index.php/blah.

Running each one on it's own is fine. Adding them together puts me into a loop. The loop is part of req 2 - effectively, entering http://bar.foo.ie (and hoping for http://www.foo.ie/site/bar to be displayed) gives me an infinate loop whereby "site/bar" is requested over and over.

I'm going round in circles and getting really dizzy with this. Please please please - a fresh pair of eyes and maybe some insight?

Thoughts anyone?

Thanks in advance!

Ben

El Forum

[eluser]mddd[/eluser]
I think you could just check in routine #2 if the requested uriis not already /site/...
That way, the loop is broken. If someone goes to bar.foo.com, the redirection goes to /site/bar and at that time it will no longer match the condition so it will not redirect again.

Code:
RewriteCond %{HTTP_HOST} !^(www\.foo.*)$ [NC]                    
RewriteCond %{REQUEST_URI} !^site/ [NC]
RewriteCond %{HTTP_HOST} ^([a-z\-\_]*)\.foo\.([a-z\.]{2,5})$ [NC]
RewriteRule ^(.*)$ index.php/site/%1

El Forum

[eluser]Volder[/eluser]
Code:
RewriteCond %{HTTP_HOST} !^(www\.foo.*)$ [NC]                    
RewriteCond %{HTTP_HOST} ^([a-z\-\_]*)\.foo\.([a-z\.]{2,5})$ [NC]            

RewriteRule ^(.*)$ index.php/site/%1

it looks like the rule 2 as it is, has an infinite loop by itslef - so strange that it is working fine by itself (maybe apache or whatever server you use automatically detects a loop and stops it).

So if you give as an input right now:
http://bar.foo.ie/this/topic
1 iteration: http://bar.foo.ie/site/bar
2 iteration: http://bar.foo.ie/site/bar/site/bar

so 2 mistakes I see: you don't specify fully the domain and subdomain here + you loose everything in your parameters.

So try the following fix:
Code:
RewriteCond %{HTTP_HOST} !^(www\.foo.*)$ [NC]                    
RewriteCond %{HTTP_HOST} ^([a-z\-\_]*)\.foo\.([a-z\.]{2,5})$ [NC]            

RewriteRule ^(.*)$ http://www.foo.%3/index.php/site/%2/$1

I use %2, %3 because %1 should relate to first RewriteCond (which we don't want), right? I don't remember for sure.

I have not tried it (just thoughts), so probably it is not a working soluition, but could be of some help for you.

El Forum

[eluser]Narkboy[/eluser]
Ok - thanks to mddd and Voldor, I have something of an improved version:

Code:
# Check that 'foo.xx' is the host:
RewriteCond %{HTTP_HOST} ^foo\.([a-z\.]{2,5})$ [NC]

# If the condition is true (no subdomain) redirect to the correct version:
RewriteRule ^(.*)$ http://www.foo.%1/$1 [R,NC]

# Second - get site subdomains to the relevant controller:

RewriteCond %{HTTP_HOST} !^(www\.foo.*)$ [NC]

RewriteCond %{REQUEST_URI} !^\/index\.php\/site/ [NC]
            
RewriteCond %{HTTP_HOST} ^([a-z\-\_]*)\.foo\.([a-z\.]{2,5})$ [NC]            

RewriteRule ^(.*)$ index.php/site/%1/$1

# Third - remove the index.php:

RewriteCond $1 !^(index\.php|img|style|license\.txt|robots\.txt|script) [NC]
RewriteRule ^(.*)$ index.php/$1

This works with pretty much everything, except:

http://bar.ie

Gives me a 302 - Found, with a link to the url we're on. Useless. The Apache error log tells me -

Code:
(38)Filename too long: Cannot map GET / HTTP/1.1 to file

Which is just weird.

Commenting out the last cond + rule means http://bar.ie redirects to http://www.bar.ie, but that means www.foo.ie/welcome does not. Reqs 1 & 3 are killing each other Sad

Thanks for the help guys - I'm going to fiddle some more..

Ben

El Forum

[eluser]mddd[/eluser]
Are you sure this rule is working:
RewriteCond %{REQUEST_URI} !^\/index\.php\/site/ [NC]
Should the first / be in there or should it just be !^index\.php
Or you could even just check for !index.php -- that way the redirect will never happen if there is index.php anywhere in the uri.
If that rule doesn't 'catch', the system will turn bar.foo.ie into bar.foo.ie/index.php/site/bar and then into bar.foo.ie/index.php/site/bar/index.php/site/bar etc.

El Forum

[eluser]Narkboy[/eluser]
I've tried changing it to !index.php but it dosen't seem to make a difference.

I see your logic - but Apache is not reporting a loop. Unless it is iterating enough to create a filename that's too long for windows (error 38) BUT not reaching the limit of 10 internal redirects, then I don't see how it could fail but not kick an error in the log.

That said, the log reports nothing of use!

Thanks!

El Forum

[eluser]Narkboy[/eluser]
This is driving me nuts:

See Apache access.log details:

http://www.docvad.es

gives:

127.0.0.1 - - [13/Apr/2010:15:34:56 +0100] "GET / HTTP/1.1" 200 1069

BUT

http://docvad.es

gives:

127.0.0.1 - - [13/Apr/2010:15:35:06 +0100] "GET / HTTP/1.1" 403 -

How does that make sense????

El Forum

[eluser]Narkboy[/eluser]
Ok, it makes perfect sense.

I bumped the RewriteLogLevel up a bit. Seems that the current script is generating request URIs which run to several thousand chars within 4-5 iterations. Oops.

Smile

El Forum

[eluser]Narkboy[/eluser]
I have fixed it. The final loop was down to an incorrect backreference in req 3, disguised by some excellent work from the caching on Firefox.

Code:
RewriteEngine on

RewriteBase /

# First - redirect if no 'www' is appended:

    # Condition is true if there is no subdomain
RewriteCond %{HTTP_HOST} ^foo\.([a-z\.]{2,5})$ [NC]

    # If the condition is true (no subdomain) redirect to the correct version:
RewriteRule ^(.*)$ http://www.foo.%1/$1 [R=301]

# Second - get site subdomains to the relevant controller:

    # Condition is true if the subdomain is NOT 'www':
RewriteCond %{HTTP_HOST} !^(www\.foo.*)$ [NC]

    # Condition is true if we are not already requesting the CI URL - loop prevention:
RewriteCond %{REQUEST_URI} !index.php [NC]

    # Condition is true if a subdomain is included:
RewriteCond %{HTTP_HOST} ^([a-z\-\_]*)\.foo\.([a-z\.]{2,5})$ [NC]            

    # Rewrite to the CI URL is all 3 conditions are true:
RewriteRule ^(.*)$ index.php/site/%1/$1

# Third - remove the index.php:

RewriteCond $1 !^(index\.php|img|style|license\.txt|robots\.txt|script) [NC]
RewriteRule ^(.*)$ index.php/$1 [L,NS]

So - this is what you want if you need to serve multiple tlds on the same site, want to require the use of 'www' and have a variable list of subdomains which need serving by a CI controller.

http://foo.fr/bar becomes http://www.foo.fr/bar
http://bar.foo.ca/foobar reads from http://www.foo.ca/site/bar/foobar
http://www.foo.nl/welcome reads from http://www.foo.nl/index.php/welcome

^seemples^

*phew* An _entire_ day wasted on this, and I'm not 100% I'm getting the contract yet.

Thanks for everyones help!

Smile

Ben

El Forum

[eluser]mddd[/eluser]
Glad it's ok now. Hope you get the project!