Forum Moderators: phranque

Message Too Old, No Replies

.htaccess help

         

nehpets

7:55 pm on Dec 1, 2009 (gmt 0)

10+ Year Member



Hi all,

I've been struggling with a rewrite issue for a few days now and I'm stumped, hope I can get some help here.

My work wants to use wildcard DNS so that we can have people sign up for an automatically generated site. Their domain would be something like subdomain.example.com. Now I have a redirect working which grabs the subdomain and delivers the index page with the domain appended in the query string. This value would then later be used in a database to get their specific information. The issue I'm having is with the CSS and JS files. The template were using tries to find the CSS / JS files on the sub domain, obviously they dont exist so I'm just getting an un-styled page.

I've been trying to find a way to use .htaccess to redirect the requests for CSS / JS file to the root domain (example.com). Here is the current rule I've been trying (with no success)


########## Begin - Repoint CSS / JS Requests
#RewriteCond %{HTTP_HOST} ^([^.]+)\.example\.
#RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{THE_REQUEST} ^[A-Z]{3,9}\ /templates/([^/]+)/(css¦js)/([^.]+)\.(css¦js) [NC]
RewriteRule ^(.*)$ http://example.com/templates/%1/%2/%3.%4 [L]
########## End - Repoint CSS / JS Requests

The first two conditions I have commented. I found this approach on a forum and attempted to adapt it for my use here.

Thanks for any help

g1smd

10:26 pm on Dec 1, 2009 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member Top Contributors Of The Month



I'm not so sure you should be redirecting those (your rule produces a 302 redirect), more like rewriting the various URL requests to fetch from a consistent internal server path. Redirecting causes the sites to appear slow as the browser has to make two requests for each file.

jdMorgan

10:45 pm on Dec 1, 2009 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



The subdomain to index.php?user=<subdomain> should be an internal rewrite as g1smd points out, and the 'shared' resources such as CSS and external JS files (as well some as several/many image files too, probably) should be in a location shared by all subdomains to avoid the need for either a rewrite or a redirect on those requests.

Jim

nehpets

11:04 pm on Dec 1, 2009 (gmt 0)

10+ Year Member



I essentially have the subdomains (which are all thru wildcard DNS mind you; *.example.com) directed back to the /www/ folder below the root for that site (its hosted via cpanel). I've a server alias of *.example.com and * in the A record for the DNS zone points back to the servers IP. So as far as I know it should be pointing back to where the CSS and JS files live but apparently when the browser goes out to get them its getting empty files or 404s. I'm using firebug and when I go look at the CSS its pulled down they all say that its empty.

Does anyone have any suggestions of what I can do?

jdMorgan

12:30 am on Dec 2, 2009 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



First stop: Your server error log.

If the css and js files are in or below root, and all subdomains are mapped to /www/ as their DocumentRoot, then this should work, and the subdomain in the requested hostname is irrelevant.

So look at the filepaths to which the server is trying to resolve these css and js URLs, as shown in the server error log. It's usually immediately obvious what the problem is.

Jim

nehpets

1:16 am on Dec 2, 2009 (gmt 0)

10+ Year Member



turns out the server was mapping the sub domains to the public_html folder. So I adapted my rewrite rule to this


########## Begin - Repoint CSS / JS Requests
RewriteCond %{HTTP_HOST} ^([^.]+)\.example\.
RewriteCond %{THE_REQUEST} ^[A-Z]{3,9}\ /([^.]+)/(.*)\.(css¦js¦png¦jpg¦flv¦swf¦gif¦xml) [NC]
RewriteRule ^(.*)$ /home/blue/public_html/%1/%2.%3 [S=1]

# SPECIAL rule for files that don't fit into the genic rule above
RewriteCond %{HTTP_HOST} ^([^.]+)\.example\.
RewriteCond %{THE_REQUEST} ^[A-Z]{3,9}\ /demo\.css [NC]
RewriteRule ^(.*)$ /home/blue/public_html/demo.css
########## End - Repoint CSS / JS Requests

and it seems to be working. I'm a bit annoyed that I had to have a special rewrite just for this one CSS that isn't with the rest of its buddies in this template, but oh well (I'm a bit of a perfectionist like that). Images are all coming in or so it seems. I'll be going over the site a bit more in details to see if there are any issues or anything broken.

Also, correct me if I'm wrong here, but the [S=1] on the first rewrite rule should cause it to ignore the next rewrite block (the one for the demo.css), correct? I just started working with .htaccess about 4 hours ago and so far I like it, bit complex off the bat, but very useful.

jdMorgan

2:37 am on Dec 2, 2009 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



You don't need to use a RewriteCond to test THE_REQUEST here, just put the (css¦js¦...) pattern into the rule itself.

Also, don't use ".*" unless that's really what you want: Everything, anything, or nothing. This is a maximally-greedy and promiscuous pattern. And really, really try to avoid ever using more than one ".*" subpattern in a pattern -- It forces many, many, many "back-off-and-retry" matching attempts.

I doubt that you need "[S=1]" here, as [L] is the most appropriate end-flag in almost all circumstances.

Get in the habit of ending all rules with [L] for the sake of efficiency, and always order your rules with your external redirects first, in order from most-specific patterns and conditions to least-specific, followed by your internal rewrites, again in order from most-specific patterns and conditions to least-specific. This may save you untold hours of grief by avoiding unexpected rewrite behavior and exposure of internal filepaths as URLs.

Note that parentheses can be nested: Count left parentheses to resolve back-references.

Distilling all of that, your rules will look like this:


# Rewrite demo.css to subdirectory
RewriteCond %{HTTP_HOST} ^[^.]+\.example\.com
RewriteRule ^demo\.css$ /home/blue/public_html/demo.css [L]
#
# Rewrite CSS, JS, media, and XML requests to subdirectory
RewriteCond %{HTTP_HOST} ^[^.]+\.example\.com
RewriteRule ^([^/]+/[^.]+\.(css¦js¦png¦jpg¦flv¦swf¦gif¦xml))$ /home/blue/public_html/$1 [L]

Replace all broken pipe "¦" characters with solid pipes before use; Posting on this forum modifies the pipe characters.

Jim

nehpets

5:02 pm on Dec 2, 2009 (gmt 0)

10+ Year Member



Thanks Jim,

I gave those patterns a try and they didn't work. The reason for the .* sub pattern (I'm not new to Regex, not a pro, but not new) is because some of the files are something.somethingelse.extension so doing something like [^.]+ wouldn't catch those particular items. If you know of a better way to match these I'd be all... well eyes I guess, it is a forum ;-)

I do wonder if you can clear something up for me, I've not been able to really tell what the first portion of a RewriteRule statement is matching the regex against. For instance you're using the patterns as the regex in the RewriteRule statement and only using a RewriteCond to ensure that the requset is coming from a sub domain. Now this approach makes sense to me as less conditions means less work for the server and faster load times, so I'll try to implement it with my working pattern, but I've never used the regex in the RewriteRule section is actually matching against. Can you shed some light on that for me?

Thanks for explaining the orders for rewrites to occur. I hadn't thought that far into my .htaccess yet, was still focused on getting a good solution for my issue.

g1smd

5:25 pm on Dec 2, 2009 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member Top Contributors Of The Month



some of the files are something.somethingelse.extension so doing something like [^.]+ wouldn't catch those particular items. If you know of a better way to match these

Something like this...

/([^.]+)/[b]([^.]+\.)+[/b](css¦js¦png¦jpg¦flv¦swf¦gif¦xml)

jdMorgan

6:27 pm on Dec 2, 2009 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



g1smd's got it as far as the pattern problem for x.y.css -type URLs goes.

The RewriteRule pattern in .htaccess or within a <Directory> container in a server config file matches against the localized URL-path.

Starting with a URL of "http://example.com.:80/dir/subir/x.y.php#anchor?name=value", the URL-path is "/dir/subir/x.y.php" -- Note that the protocol, hostname, port number, URL-fragment identifier, and query string are not part of the URL-path.

The localized URL-path depends on the directory specified in the <Directory> container, or on the location of the .htaccess file with this code in it. For example, if the .htaccess file is located in your Web root directory (where your robots.txt and 'home page' would also normally be located), then the local URL-path for the URL cited above would be "dir/subir/x.y.php".

If the .htaccess file is in /dir, then the localized URL-path would be "subir/x.y.php", and if it is located in /dir/subdir, then just "x.y.php". So the RewriteRule pattern needs to change depending on the code location, but in no case starts with a slash.

On the other hand, when RewriteCond looks at REQUEST_URI, it's looking at the full URL-path, as is RewriteRule when located in a server config file, but *not* within any <Directory> container.

Clear as mud?

Jim

nehpets

6:52 pm on Dec 2, 2009 (gmt 0)

10+ Year Member



If mud were the color of raw sewage I'd be clear. A bit more of an understanding on whats going on thou, thanks.

If I'm understanding you correct, assuming I have my .htaccess in the /www/ folder for the domain (servers path is /home/blue/www/...) and I'm say trying to rewrite all .htm files to .php I'd do...


RewriteRule ([^.]+)\.htm $1.php

That would obviously only work for files that are also in the root directory, if I were to say have a file in a sub directory...

RewriteRule ([^/]+)+/([^.]+)\.html $1/$2.php

Sadly I'm a bit confused by what you say REQUEST_URI is looking for. An example I think may be helpful (and oddly enough I'm trying to do this now!). Lets say that I have the same setup I've been plodding on with all week. If I were to want to ensure that subdomain.example.com/admin AND subdomain.example.com/admin AND example.com/admin would all perform a redirect to example.com/administrator what would I do?

I've got this going on, which works for subodmain.example.com/administrator pointing back to example.com/administrator; the rest however eludes me.


########## Begin - Administration redirect
RewriteCond %{HTTP_HOST} ^([^.]+)\.example\.
RewriteCond %{REQUEST_URI} /administrator
RewriteRule ^administrator/(.*)$ http://example.com/administrator/$1 [R,L]
########## End - Administration redirect

This does not address the /admin section of my question from eathier a sub domain or the root domain.

jdMorgan

7:44 pm on Dec 2, 2009 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



If I'm understanding you correct, assuming I have my .htaccess in the /www/ folder for the domain (servers path is /home/blue/www/...) and I'm say trying to rewrite all .htm files to .php I'd do...

RewriteRule ([^.]+)\.htm $1.php

That would obviously only work for files that are also in the root directory, if I were to say have a file in a sub directory...


That would rewrite any URLs (the rewriterule pattern doesn't consider 'files') that contained at least one non-period character preceding ".htm" and followed by anything at all. Further, it would open your site up to a certain hacking method, because you do not control the initial filepath.

In fact it would 'harder' to make this *not* work for subdirectories, because you'd have to exclude slashes as well as periods. And because 'directory names' in the URL could contain periods followed by "htm", this rule is deficient in several respects (mostly due to lack of anchoring).

Your second rule:

RewriteRule ([^/]+)+/([^.]+)\.html $1/$2.php

That's better, but could be improved a bit, especially for security:


# Rewrite any .html page URL in any directory to /page-dir/page-name.php
RewriteRule ^(([^/]+/)*[^.]+)\.html$ /$1.php [L]

...or for only root-directory page requests:


# Rewrite any .html page URL requests in root to /page-name.php
RewriteRule ^([^/.]+)\.html$ /$1.php [L]

As for this code...

########## Begin - Administration redirect
RewriteCond %{HTTP_HOST} ^([^.]+)\.example\.
RewriteCond %{REQUEST_URI} /administrator
RewriteRule ^admin/(.*)$ http://example.com/administrator/$1 [R,L]
########## End - Administration redirect

Given that you didn't understand my previous post, I'll just say simply, "Quit it with the redundant and unnecessary RewriteCond %{REQUEST_URI} lines"... They are hardly ever useful, except for negative-pattern-match exclusions. And the more compact and concise your comments, the better. Remember that this code is parsed for each and every HTTP request to the server; Single rules don't need 'section level' start and end comments with a bunch of ###s on them. (Please take this as friendly but serious 'style' advice, since it is ultimately performance-affecting). And specify that .com tld, too, unless you want to do hackers a favor...

# Redirect subdomain.example.com and example.com /admin/<whatever> requests
# to example.com/administrator/<whatever>
RewriteCond %{HTTP_HOST} ^([^.]+\.)?example\.com
RewriteRule ^administrator/(.*)$ http://example.com/administrator/$1 [R=301,L]

Now two of the "subdomain/admin" requirements in your description above were the same. In case you meant to say that you also want to redirect requests for just "/admin" as well, then the pattern, substitution, and comment would change slightly:

# Redirect subdomain.example.com and example.com /admin/<whatever> or /admin
# requests to example.com/administrator<whatever>
RewriteCond %{HTTP_HOST} ^([^.]+\.)?example\.com
RewriteRule ^admin(/.*)?$ http://example.com/administrator$1 [R=301,L]

In neither case is a "RewriteCond %{REQUEST_URI}" needed... ;)

Jim

nehpets

9:33 pm on Dec 2, 2009 (gmt 0)

10+ Year Member



Ok,I think I'm starting to get the hang of this. The RewriteRule has something of a condition in itself in that its comparing the Regex to the requested file (path, file, and I think query string). So if I'm looking to redirect particular folders there is no need to have any RewriteCond(s) as the "conditional" part of the RewriteRule will take care of that. The RewriteCond would be good if I were to only want to rewrite to occur when something else already has, such as its coming from www. or has a sub domain, or is from a particular IP.

Also, I do include the .com, the owners of the company I work for are a bit paranoid, hence why I change the domain to .example and leave out the .net/.com etc, they think it'll make it harder for someone "evil" to find it.

Can you suggest any reference I can go over to broaden my horizons? I'm probably going to end up writing a few .htaccess files for some of our older sites and I'd prefer to not have to bug you guys all the time ;-)

jdMorgan

10:54 pm on Dec 2, 2009 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



> The RewriteRule has something of a condition in itself in that its comparing the Regex to the requested file (path, file, and I think query string)

Yes the RewriteRule is conditional, based on its pattern matching the requested URL-path. And RewriteRule does not check anything about *files* -- It looks at URL-paths only (big difference, there).

No, the pattern in the rule is matched only with the URL-path. For the client-requested URL "http://www.example.com/dir/index.php#named-anchor?name=value", only "dir/index.php" will be tested by the RewriteRule pattern -- if any other parts need to be tested, use a RewriteCond examining the appropriate server or request variable.

For more information, see the Apache mod_rewrite documentation and the Apache URL Rewriting Guide as a start.

Jim