Take the 2-minute tour ×
Server Fault is a question and answer site for professional system and network administrators. It's 100% free, no registration required.

I am using mod_rewrite in an .htaccess file in our root web directory so that we can use cleaner URLs for some specific cases. For example, we want to translate:

http://example.com/Topic/ABCD/Type/Description

into

http://example.com/Topic.php?l=ABCD&e=Type&t=Description

This seemed easy enough; the .htaccess file consists of:

RewriteEngine on
RewriteRule ^Topic/([^/\.]+)/([^/\.]+)/([^/\.]+)/?$ /Topic.php?l=$1&e=$2&t=$3 [L]

However, all the page's content (.CSS files, images, etc.) ends up 404'ing because instead of, say, loading "/Common.css" from the directory relative to the rewritten URL:

http://example.com/Common.css

it loads it from the un-rewritten URL's directory:

http://example.com/Topic/ABCD/Type/Description/Common.css

which obviously does not exist. However, I've found countless tutorials all over the web that advocate some variant on what I'm doing (one example here), but make no mention of this problem I'm having, so I'm wondering if there's just some configuration I'm overlooking. They can't all rely on having these 'virtual' directories exist and contain the page's content. I also really don't want to make everything into absolute paths.

I have tried adding rules earlier in the .htaccess file to try to short-circuit content requests, such as:

RewriteRule \.(css|jpe?g|gif|png)$ - [L]

but it seems to have no effect. Regardless, I wouldn't have thought such content would even match the original rule, as the regex in that rule does not allow for anything following the (optional) trailing slash.

If I change the rule such that its flag is set to redirect ([R,L] instead of just [L]), then it all works fine -- except now the browser displays the rewritten php parameter string, which is what we were trying to avoid. In the past, we've just accepted this as a work-around, but now I'm trying to understand why it's working this way, and what I can do about it.

In case it's relevant, I'm using CentOS 6.2, and Apache/2.2.15.

share|improve this question
1  
Check the html code instead -- quite likely links to css/js/images are relative to the current document (e.g. href="css/main.css"). If so -- use relative to the website root -- href="/css/main.css" (notice the leading slash). Plus -- of course, that last rewrite rule: RewriteRule \.(css|jpe?g|gif|png)$ - [L] –  LazyOne Apr 24 '12 at 0:01
    
Looks like you and Jon Kloske had the same right idea. It seems that building it to work entirely off relative locations just can't work with the rewriting scheme I'm using. –  Doug Kavendek Apr 24 '12 at 14:41
    
Well -- you can try using <base href="http://www.example.com/possible-virtual-folder-etc/" /> tag in the header -- since you most likely generating the page in PHP, you should be able to make it work (I'm just not a big fan of this since it can be hard to do (or bad/absent browser support) in some cases). –  LazyOne Apr 24 '12 at 15:04
add comment

3 Answers 3

up vote 2 down vote accepted

The problem is most likely that your code generates pages that include CSS like so:

<link rel="stylesheet" type="text/css" href="Common.css" />

Since the browser sees this and sees the URL it fetched as a subpath, it makes a request for Common.css at that subpath.

You could (but shouldn't, for reasons I'll explain in a moment) make some more rewrite rules that rewrite Common.css etc to the single one at the top level, eg:

RewriteEngine on
RewriteRule ^Topic/([^/.]+)/([^/.]+)/([^/.]+)/Common.css$ /Common.css [L]

HOWEVER, that would be silly, since the whole point of CSS is to allow for caching etc. A much better approach is to simply add a / at the front of Common.css in your code, so it produces CSS like so:

<link rel="stylesheet" type="text/css" href="/Common.css" />

That way, all the pages use the same Common.css file, and the browser doesn't have to fetch a copy of the same file in multiple logical locations on every page, and what's more the browser knows to fetch it at the top level, not on a subpage.

PS Jakub: the regex for the RewriteRule already excludes anything with a period in it, which already covers the .css, .jpg, etc, so adding the additional RewriteCond won't help.

share|improve this answer
    
the reason the redirect option works is that the browser is then fetching the page from the root path, which is where the Common.css file actually lives. –  Jon Kloske Apr 24 '12 at 0:01
    
Looks like my problem was that I expected relative paths to resolve relatively to the host-side location of the main document, but since it's the client requesting the linked content, it's relative to the URL the browser thinks it's at. Switching to use the leading slash then works fine; I was hoping to avoid absolute paths, but it seems now that it's just inevitable if I'm going to be rewriting and not redirecting. Thanks! –  Doug Kavendek Apr 24 '12 at 14:40
    
Yep, plus absolute paths for common includes actually makes sense for a lot of reasons. –  Jon Kloske Apr 26 '12 at 2:14
add comment

You have to use RewriteCond

RewriteEngine on
RewriteCond $1 !^(\.css|\.js|\.png|\.jpg)
RewriteRule ^Topic/([^/\.]+)/([^/\.]+)/([^/\.]+)/?$ /Topic.php?l=$1&e=$2&t=$3 [L]
share|improve this answer
add comment

The answer is relatively simple - you have to use base href in your html code because the process of rewriting the URI resets the Base URL which causes the 404s that you have been experiencing.

There is a good article on this at http://pixelcode.co.uk/tutorials/Clean+URLs+with+mod_rewrite.html

Regards Miles

share|improve this answer
    
That's the first article I've seen about clean URLs that actually brought up this issue; wish I had found it earlier! –  Doug Kavendek May 22 '12 at 14:05
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.