mod_rewrite problem with relative path css/js - regex

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

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/" />

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.

RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule . index.php [L]
Should obviously work despite the comments.
Try to add the RewriteLog and RewriteLogLevel directive to give us better details.

This is a path resolution issue: When using the relative path ./css on the base path /something it is resolved to /css while on /something/somethingelse it is resolved to /something/css.
This can’t (or rather shouldn’t) be fixed with mod_rewrite. Use absolute paths instead of relative paths, so /css instead of ./css.

Related

My apache rewrite only works for the first folder level

We have a website where we show clients creative work we have produced for them. We upload raw assets to a path like this:
x.com/clients/clientName/campaignName/size/
I have a PHP script which adds our branding, contact information and other information and pulls in the raw creative (usually a swf object). It is in this directory x.com/clients/index.php and it accepts a query string parameter ?path so it knows where to look for the creative.
I am trying to do an apache rewrite in .htaccess so that our designers can upload directly to the known folder structure but so that when you go to x.com/clients/clientName/campaignName/size/ it should rewrite to x.com/clients/index.php?path=clientName/campaignName/size/
I am currently using the following rewrite rule, which works for the first folder level e.g. x.com/clients/clientName/ does successfully rewrite, but any subsequent folders do not.
RewriteRule ^clients/([^/\.]+)/?$ /clients/index.php?path=$1 [L]
My RegEx's are terrible, so I'm stuck on what to do. Any help appreciated, thank you kindly.
Your regex is only matching urls like clients/xxxxxx/ because your pattern [^/\.]+ means one or many characters except "/" or "."
With your rule, it can't work for other subdirectories.
You can change your rule by this one
RewriteRule ^clients/(.+)$ /clients/index.php?path=$1 [L]
To avoid internal server error (code 500 which means an infinite loop in this case), you can do it this way
RewriteRule ^clients/index\.php$ - [L]
RewriteRule ^clients/(.+)$ /clients/index.php?path=$1 [L]
Is there a special reason you want to use regex? In my opinion you can just catch everything coming after /clients:
RewriteEngine on
RewriteCond %{REQUEST_URI} !^(.*/)?index\.php$ [NC]
RewriteRule ^clients/(.*)$ /clients/index.php?path=$1 [L]
The second line is to prevents redirect loops, because the index.php is also in the folder /clients and this would cause never ending redirects.

htaccess->show file if in cache directory, if not continue

Im playing arround with cache. What I want is the following logic:
if ( file_exist(/cache/$request_uri.txt) ){
1. show that file
2. Stop all rewrite actions, but perform other actions (like charset, error_pages)
}
else{
do whatever you normally do
}
Small example. I have the following files
http://domain.com/example-123.htm (the requested page)
http://domain.com/cache/example-123.htm.txt (the cache-version of that page)
http://domain.com/someDir/example-456.htm (the requested page)
http://domain.com/cache/someDir/example-456.htm.txt (the cache-version of that page)
Instead of getting the index.php to parse the url and build the page, I want to show that file and stop.
I though this would do it, but it doesn't:
RewriteCond ^cache%{REQUEST_URI}\.txt -f # check if the file exists in cache dir
RewriteRule ^(.*) /cache/$1\.txt [L] # i so, rewrite rule to it and stop
The [L] does, according to my cheatsheet, "Last - stop processing rules". If that only means the rewrite rules, than thats what I need.
I cant get it to work, I could use a push in the right direction :)
I've marked an answer as solution it, did exaclty what it should. The reason I want this in the .htaccess file is because this way my index.php doesn't get called, nor does the database. A very fast and light method.
However, this creates a new problem: Some items (like the menu) can change (often). That would mean I'd have to delete all cache files every change, which prohibits it from working efficient.
To tackle this problem, im going to see if I can use some clever .shtml files to fix that problem (might need to allow php to woth in shtml files).
I'll update this post as soon as I've got something nice working for those interested
You can try this rule:
# check if the file exists in cache dir
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{DOCUMENT_ROOT}cache/$1.txt -f
RewriteRule ^(.+?)/?$ /cache/$1.txt [L]

Removing a string of characters in a URL through htaccess

I'm struggling to come up with the correct code to do what I need. I've searched through SO and other sites and found answers close to what I want, but I just can't quite piece it all together right, and .htaccess is a huge weakness of mine.
I'm trying to make it so an entire folder level gets removed from all URLs on a site, otherwise preserving the structure. After that, I need to add ".html" to the end. The addition isn't anything hard, but I'm missing what I need to strip out the folder.
Starting URL: www.domain.com/ANYFOLDER/any-page-name
(Bonus: www.domain.com/ANYFOLDER/ANYDEPTH/any-page-name)
Ending URL: www.domain.com/any-page-name.html
We have a client who is moving from a static site to CMS-driven, has some great ranks/traffic for his URLs, and is petrified he will lose this (we will not take Permanent Redirects as a solution).
You can use this rule for this redirect:
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(?:[^/]*/)*((?!.+?\.html$)[^/]*)$ /$1.html [L,R=302,NC]

.htaccess with 2 parameters messing up css, js, images paths

I was working with URLs on my webpage but I can't solve issue for URLs with 2 parameters.
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^([a-zA-Z0-9-z\-]+)/?$ index.php?strona=$1 [L]
RewriteRule ^([a-zA-Z0-9-z\-]+)/([a-zA-Z0-9-z\-]+)/?$ index.php?strona=$1&id=$2 [L]
URLs seem fine except that when my current URL has 2 parameters (for example I'm on http://example.com/subpage/5 whole webpage is broken (stylesheets, navigation etc) because .htaccess changed all links to:
(for example navigation):
http://example.com/subpage_with_2_parameters/home
instead of
http://example.com/home
Pages with one parameter (example: http://example.com/contact) work fine.
Only solution (which is horrible) I have on mind are absolute links.
You're not the only one dealing with this problem of css, js, images paths getting messed up after implementing so-called pretty URLs. I am seeing these problems being reported on SO almost every day.
You can solve this problem in 3 ways:
Best solution is to use absolute paths for images, css and js files i.e. start your path with / or http://
Another option is to use base href tag in HTML head section like this:
<base href="http://www.example.com/">
3rd option is via mod_rewrite
Put these lines above your other RewriteLine in your .htaccess file:
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-l
RewriteCond %{DOCUMENT_ROOT}/$1 -f
RewriteRule ^[^/]+/([^.]+\.(?:js|css|jpe?g|png|gif))$ /$1 [L,R=301,NC]
It's not your rewrite rules which are breaking your stylesheets, but your actual HTML. The same thing would happen if you had an actual directory called foo and placed index.php in there.
If you write Home, that link is relative to the current directory (in URL-space) so on a page with a URL like http://example.com/foo/bar/baz it links to http://example.com/foo/bar/home.
What you want instead is for the link to be relative to the root of your domain; for that, you need a leading slash: Home
The only reason this seemed to work before is that all your URLs were in the root directory, so "current directory" and "root of domain" were the same thing.

Explain this .htaccess snippet

Can someone explain the following htacess lines, I understand parts, but would like a deeper knowledge. As a note I assumes it works as intended, this is not currently live, I am just reading through some workbooks and this was printed.
// Don't understand this line
Options -Multiviews
// Don't understand this line
Options +FollowSymLinks
// Understand this line
RewriteEngine On
// Don't ~fully~ understand this line, esp. in context
RewriteBase /portfolio
// Don't ~fully~ understand this line
// I understand that its asking if the filename is a valid file or dir
// but is it overall saying if valid file or valid dir perform rewrite?
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
// Don't understand, $1 is the STRING, and the rest the condition, but
// how do you read this condition?
RewriteCond $1 !^(index\.php|images|robots\.txt|css)
// Don't understand (but do understand the RewriteRule PATTERN REPLACE, but is it
// saying replace 'all' with index.php/all ?
RewriteRule ^(.*)$ index.php?/$1
Options -Multiviews
This disables the Multiviews Apache option. Basically, the option allows the server to look for content in the same directory using different file names based on the content types and languages accepted by the client. The directive is just disabled in this case to make sure Apache doesn't serve any unexpected files.
Multiviews enables content negotiation, which is explained at: http://httpd.apache.org/docs/current/content-negotiation.html
Options +FollowSymLinks
This makes sure the FollowSymLinks option is enabled. This setting allows Apache to follow symbolic file links in the directory if they exist. This setting exists in case there are symbolic file links to make files physically exist elsewhere on the server than what is requested.
Longer explanation at: http://www.maxi-pedia.com/FollowSymLinks
RewriteBase /portfolio
This setting is for defining the base path for the url used by the rewrite engine. When the rewrite engine rewrites the url in .htaccess, it strips away the path to the current directory. Once the url rewriting is complete, it will add it back based on the current file directory. However, sometimes the url that is requested does not have the same path as the directory structure on the server itself. The RewriteBase tells the rewritengine what the URL path is to the current directory. In this case, for example, the files may be stored in /foo/bar, but they are accessed via the browser as www.example.com/portfolio. The RewriteBase tells the engine to add /portfolio to the url, instead of /foo/bar.
For complete explanation, see: http://httpd.apache.org/docs/current/mod/mod_rewrite.html#rewritebase (the url also contains explanations to the other Rewrite parts of the .htaccess).
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
These lines make sure that any url that is an actual existing file or directory wont get rewritten. The ! before the condition is negation. So these two conditions should be read as ifNotFile AND ifNotDirectory.
RewriteCond $1 !^(index\.php|images|robots\.txt|css)
The $1 here refers to the sub pattern capture 1 of the actual rewrite. In other words, it means the part captured by (.*) in the RewriteRule. Basically, this rule simply checks that the RewriteRule wont rewrite any url that starts with "index.php", "images", "robots.txt" or "css".
RewriteRule ^(.*)$ index.php?/$1
This simply tells the rewrite engine that any request (that isn't prevented by the rewrite conditions, of course) should be rewritten to index.php? with the actual request following it. Just like you said, a request foo/bar will be forwarded to index.php?foo/bar. The point is to allow index.php to handle the file requests (which can access them via $_SERVER['QUERY_STRING']), which is very common practice in CMS systems and frameworks.
I hope these explanations will help. I don't have extensive experience on all these directives, so slight inaccuracies may exist, please comment if so.