views:

13

answers:

1

I've pretty much hit a wall here. As far as I can tell, this should work, but it doesn't.

I have a local development address with a wildcard subdomain of *.localhost, and friendly URLs of /[0-9]/somestring. What I am trying to do is have username.localhost/1/page to localhost?pageId=1&username=username. The complication comes in trying to get both the home page at username.localhost as well as the individual pages i.e. username.localhost/1/page to both work.

RewriteRule ^([0-9]+)/[a-zA-Z0-9\-\_\,\.]+$ - [S=1]
RewriteCond %{HTTP_HOST} !^www.* [NC]
RewriteCond %{HTTP_HOST} ^([^\.]+)\.localhost
RewriteRule ^index.php$ index.php?username=%1
RewriteRule ^([0-9]+)/[a-zA-Z0-9\-\_\,\.]+$ index.php?pageId=$1&username=%1

With /1/page pages, the first rule meant to skip matches and skips, but fails to rewrite correctly. However, if I remove the first 2 rules, it'll rewrite /1/page pages just fine.

It's as if it's not skipping, yet if i change it to S=2, it skips both rules. Argh. Any ideas?

+1  A: 

From what I can tell, it actually is doing what you expect it to do. Only, after it does that, it has a second go at your rule set, which messes things up. What's happening looks more specifically like this:

  • Request to http://username.localhost/1/page is made
  • The input 1/page matches the first rule, S=1 is applied
  • The input 1/page matches the third rule, URL is rewritten to index.php?pageId=1&username=username
  • An internal redirection is performed by mod_rewrite (all good so far, but...)
  • mod_rewrite handles the internal redirection, and starts processing the rules again
  • The input index.php does not match the first rule, S=1 is not applied
  • The input index.php matches the second rule, URL is rewritten to index.php?username=username
  • (the internal redirection occurs again, the same rewrite is performed, but mod_rewrite detects the redirection loop and stops processing now)

There are a few different ways to fix this, but I think the easiest one here is to just make sure the file doesn't exist, and then roll the pattern from the last rule into a condition for the previous one:

# Make sure we haven't rewritten to a file yet (the "directory" gets processed
# before DirectoryIndex index.php is applied)
RewriteCond %{REQUEST_FILENAME} !-f
# Check that the input doesn't match the pattern we want handled later
RewriteCond $0           !^([0-9]+)/[a-zA-Z0-9_,.-]+$
RewriteCond %{HTTP_HOST} !^www.* [NC]
RewriteCond %{HTTP_HOST}  ^([^\.]+)\.localhost
RewriteRule ^.*$ index.php?username=%1

RewriteRule ^([0-9]+)/[a-zA-Z0-9_,.-]+$ index.php?pageId=$1&username=%1

Edit: The above version also catches things that don't match your page pattern and acts like they were index.php?username=username, which might not be desired. The following would avoid that, and is a bit more concise anyway:

RewriteCond %{HTTP_HOST} !^www.* [NC]
RewriteCond %{HTTP_HOST}  ^([^\.]+)\.localhost
RewriteRule ^$ index.php?username=%1

RewriteRule ^([0-9]+)/[a-zA-Z0-9_,.-]+$ index.php?pageId=$1&username=%1
Tim Stone
Thanks for the answer. I can see the logic of how a second run through the rules could throw a wrench in the whole thing. Sorry if I'm a bit dense, though, but I can't follow how your solution resolves it. The REQUEST_FILENAME comes out to be index.php which is always going to exist, and consequently trying this doesn't seem to pan out.
Zurahn
@Zurahn - A request to `http://username.localhost/` will have an initial `%{REQUEST_FILENAME}` of `/website/root/dir/`, since there's no request path after the domain. This isn't a file, so the condition `!-f` is true on the first pass. On subsequent passes (in this case because of a `RewriteRule`, but the same would be true if `DirectoryIndex` was applied), the `%{REQUEST_FILENAME}` becomes `/website/root/dir/index.php`, which is a file, causing the condition to fail and not allowing the first rewrite to be performed again accidentally.
Tim Stone
Thanks for all the help. I'll work with what you've provided and think I should be able to manage it.
Zurahn