Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

mod_rewrite problem with relative path css/js

Hi I have a problem. I want to get all requests to redirect to index file in main directory and I've achieved this but there are problems with relative paths.
When I put address like: mydomain.com/something it works ok as the paths are relative to the main directory.
The problem is when I put something like: mydomain.com/something/somethingelse.

the .htaccess file:


Options FollowSymLinks
RewriteEngine On
# ignore anything that's an actual file
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} !-f
# redirect all other traffic to the index page
RewriteRule . index.php [L]

Any ideas on how to get css/js working?


Edit:

The problem is that css/js files aren't loaded when the path entered have multiple slashes
like:mydomain.com/something/somethingelse

like image 434
Hubert_J Avatar asked Apr 29 '11 09:04

Hubert_J


2 Answers

It is no doubt better to use absolute path for static files (css, js, images etc). But if you lots of those instances in several pages then consider using HTML base tag to specify a default URL for relative paths. eg:

<base href="http://www.example.com/static/" />
like image 75
anubhava Avatar answered Oct 15 '22 00:10

anubhava


Using the <base>-tag is a nice solution and most browsers seem to handle it well. Except there are some issues with IE, as was to be expected... Apparently you can also run into some other funny problems, see discussion here.

So for people where this is not an option, i have looked into the alternative (the "hard way").

Usually you store css/js/static images/other stuff like this:

index.php
js/
css/
imgs/

and you want the javascript and stylesheets etc. to be available, no matter how many slashes there are in the url. If your url is /site/action/user/new then your browser will request

/site/action/user/css/style.css
/site/action/user/css/framework/fonts/icons.ttf
/site/action/user/js/page.js
/site/action/user/js/jquery/jquery.min.js
/site/action/user/js/some/library/with/deep/dir/structure/file.map

So here are some rewrite rules for apache to solve this... First, if the target actually exists on disk, do not rewrite:

RewriteCond %{REQUEST_FILENAME} -d [OR]
RewriteCond %{REQUEST_FILENAME} -f
RewriteRule ^.*$ - [L,QSA]

In words, IF reqest filename is a directory OR IF request filename is a file then do not rewrite (-), last rule (L) and pass any GET parameters (QSA, query string append). You can also use

RewriteCond %{REQUEST_FILENAME} -d [OR]
RewriteCond %{REQUEST_FILENAME} -f [OR]
RewriteCond %{REQUEST_FILENAME} -l
RewriteRule ^.*$ - [L,QSA]

if you also need symlinks. Next we want the javascript and stylesheets to be found even if the requests assume a wrong base directory as shown above.

RewriteRule ^.*/js/(.*)$ js/$1 [L]      
RewriteRule ^.*/css/(.*)$ css/$1 [L]

The pattern is pretty obvious, just replace 'css' with the directory name. There is still a problem with this, especially for large websites with lots of javascript and stylesheets, libraries etc. - The regex is greedy. For example, if you have a javascript directory like this:

js/some/library/js/script.js

and your request goes to /site/action/user/new, the browser will request /site/action/user/new/js/some/library/js/script.js, which the rewrite-engine will then rewrite to

js/script.js

because the first .* is greedy and matches /site/action/user/new/js/some/library. Switching to non-greedy regex does not really make sense, since "the rewrite engine repeats all the rules until the URI is the same before and after an iteration through the rules."

There is another problem, and that is that for every directory that needs to be exempted from rewriting, a relatively "expensive" regex is needed. Both problems can be fixed by just putting every static component into a subdirectory with an "unusual" name (and really this is the best solution imo - anyone with a better idea please post it).

The directory structure would then look like this:

index.php
mystrangedir/js/
mystrangedir/css/
mystrangedir/imgs/

Of course, this needs to be inserted everywhere in the code - for projects with a large existing codebase this can be tricky. However, you only need a single regex for directory exemption then:

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

Automated build systems (like gulp, grunt....) can be used to check if "mystrangedir" does not exist as directory anywhere below itself (which would again throw off the rewrite engine).

Feel free to rename mystrangedir to something more sensible like static_content but the more sensible it gets, the more probable it is that the directory name is already used in some library. If you want an absolutely safe directory name that has certainly never been used before, use a cryptographic hash, e.g. 010f8cea4cd34f820a9a01cb3446cb93637a54d840a4f3c106d1d41b030c7bcb. This is pretty long to match; you can make a tradeoff between uniqueness and regex performance by shorting it.

like image 43
user3648611 Avatar answered Oct 14 '22 23:10

user3648611