htaccess: relative paths not working properly - php

First, what I am trying to achieve:
I am testing under the base http://example.com/subdir and this is where all subfolders , index.php and .htaccess are.
I have 3 groups of rules as shown in the .htaccess (extra rules removed):
<IfModule mod_rewrite.c>
Options +FollowSymlinks
RewriteEngine on
# Convert item/edit/xx -> index.php?p=edit&id=xx
# Convert item/remove/xx -> index.php?p=remove&id=xx
#RewriteCond %{REQUEST_URI} ^/item [NC]
#RewriteCond %{QUERY_STRING} ^id=([0-9]+)$ [NC]
RewriteRule ^item/([a-z]+)/([0-9]+)$ index.php?p=$1&id=$2 [NC,L]
# Convert category/yyyy -> customer/pagination.php?category=yyyy
#RewriteCond %{QUERY_STRING} category=([a-zA-Z\s]+)$
RewriteRule ^customer/category/([a-zA-Z\s]+)$ customer/pagination.php?category=$1 [NC,L]
# Convert action/about -> index.php?p=about
# Convert action/terms -> index.php?p=terms
#RewriteCond %{QUERY_STRING} p=([a-z]+)$
RewriteRule ^action/([a-z]+)$ index.php?p=$1 [NC,L]
</IfModule>
The first problem I faced was the RewriteCond not working (giving Path not found), so it is commented out for the moment.
RewriteRule works fine with absolute path (e.g. RewriteRule ^action/([a-z]+)$ http://example.com/subdir/index.php?p=$1 [NC,L]), however this causes the browser to display the real URL therefore I'm trying to make it work with relative paths.
My issue is that after a first redirect the first part of the link on the left is added to the path, i.e. http://example.com/subdir becomes http://example.com/subdir/action after clicking action/about link.
Defining RewriteBase or prepending slashes to the URLs only makes things worse.
I will be grateful to an eagle-eyed expert who can spot the root cause of my problem.

The first problem is that %{REQUEST_URI} always includes the full path. So, your condition could be changed to:
RewriteCond %{REQUEST_URI} ^/subdir/item [NC]
The second problem would actually not be solved using .htaccess. You just need to tell the browser what you want. So, you can use one of two methods.
Use the <base> element (which will apply to all relative URIs on the page):
<base href="/subdir" />
Use a relative path to the URL shown in the browser:
Click here
If you really wanted to solve the problem using .htaccess, the only real way to do so would be to remove the duplicated directory after it has been requested.

Related

Set .htaccess rules to specify different folders

I want to set a rule in .htaccess if I enter in the url www.mydomain.com/compare.php set 'public_html' as root otherwise anything come in the url set root as 'public' folder.
RewriteRule ^(?!compare-source.php).*)$ public/$1 [L]
I want to achieve following result.
if url is www.mydomain.com/compare.php hit following file.
public_html/compare.php
if urls are www.mydomain.com/ OR www.mydomain.com/home etc hit following file.
public_html/public/index.php
I am weak in regex and in these apache rules always :-( can someone give me the solution with good description?
Your answers are welcome, please can you describe how this crazy things work in detail. Thanks.
Try:
RewriteEngine On
RewriteCond %{REQUEST_URI} !^/public
RewriteRule ^((?!compare\.php).*)$ /public/$1 [L]
The RewriteEngine directive enables or disables the runtime rewriting engine.
The RewriteCond directive defines a rule condition. The following Rule is only used if this condition is met; In our case, if REQUEST_URI (the path component of the requested URL) does not (because of !) begin (because of ^) with /public. We need this condition because we don't want to rewrite already rewritten URL - that would cause loop and Internal error 500.
Finally, the RewriteRule will match regex Pattern (^((?!compare\.php).*)$) against part of the URL after the hostname and port, and without the leading slash. If the pattern is matched, the Substitution (public/$1) will replace the original URL-path.
In plain language, if URL path does not begin with compare.php (because of ?!), pick everything (.*) between beginning (^) and end ($) and place it in variable $1. Then replace the original URL path with /public/$1.
#Anubhava's answer is also correct, he just placed both conditions in RewriteRule, and also it could be written even more readable as:
RewriteCond %{REQUEST_URI} !^/public
RewriteCond %{REQUEST_URI} !^/compare\.php
RewriteRule ^(.*)$ /public/$1 [L]
You can use this .htaccess in site root:
RewriteEngine On
# route /home/ or /home to /
RewriteRule ^home/?$ / [L,NC]
# if not compare-source.php or public/* then route to /public/*
RewriteRule ^(?!public/|compare-source\.php$).*)$ public/$1 [L,NC]

Apache Mod_Rewrite Multiple Levels

My goal is to make some friendly URL's using .htaccess. I need a mod_rewrite (for .htaccess) solution that allows me to have URLs like:
http://www.example.com/admin/lots -> /admin/lots/index.php
http://www.example.com/admin/lots/edit/1 -> /admin/lots/edit.php?id=1
http://www.example.com/admin/lots/view/1 -> /admin/lots/view.php?id=1
http://www.example.com/admin/lots/view/more/1 -> /admin/lots/view/more.php?id=1
http://www.example.com/admin/settings -> /admin/settings/index.php
http://www.example.com/admin/settings/edit/1 -> /admin/settings/edit.php?id=1
http://www.example.com/admin/project/status/edit/1 -> /admin/project/status/edit.php?id=1
There may be 1 to 10 directory levels with some of the URLs. It's highly unlikely, but if this RewriteCond/Rule could be expandable, that'd be great.
However, the "edit", the "id", and file names may be different. I think that explaining this is very hard to achieve especially because of the hundreds of possibilities.
Basically, I'd like to be shown the file if it exists, and there is nothing else after the file name in the url. If it's a file and has more after the file name, assume it's a variable. If it's a directory with nothing else after it, I'd prefer if it could go to the directory index (index.php in this case). If it's a directory with a file name after it, I'd like to be shown that file.
Keep in mind that if a part of the URL is neither a directory or a file, assume it is part of the query, such as ?id=1
If necessary, I can put it in the server config, however, I'd like to keep it stupid simple.
I recognize this URL pattern from Zend Framework. How Zend Framework does it, is rewrite everything to an index.php, which bootstraps the application, and does the actual routing according to the routing configuration set in the application.
If this is only in the /admin/ subdirectory, you could create the following rewrite (based on Zend's default .htaccess):
RewriteCond %{REQUEST_FILENAME} -s [OR]
RewriteCond %{REQUEST_FILENAME} -l [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^admin/.*$ - [NC,L] # Do not rewrite if it is an actual file/link/directory
RewriteRule ^admin/.*$ /admin/router.php [NC,L] # Rewrite to the router.php
And in the router.php, you can use the $_SERVER['REQUEST_URI'] to parse the URL, set variables in the $_SERVER['QUERY_STRING'], and include the proper PHP for the given URL, for example.
For the RewriteCond usage, check: http://httpd.apache.org/docs/2.2/mod/mod_rewrite.html#rewritecond
Let's start
Firstly, you'll need to put the following code in a file named .htaccess, in whichever directory you are dealing with:
RewriteEngine on
Now the mod_rewrite module has been turned on, and is ready to accept further instructions. The RewriteRule command is the root of the module, which tells mod_rewrite what to do. Here is its syntax:
RewriteRule Pattern Substitute (Optional Flags)
Here's an example:
RewriteRule /articles/([0-9]+) /articles.php?id=$1
This will replace http://www.yoursite.com/articles/1/ with
http://www.yoursite.com/articles.php?id=1.
You don't have to limit yourself to numbers either, you can use [a-z] or [A-Z] too!
Here are some flags you can use:
f: send a 403 forbidden error to the browser
NC: make the rule case-insensitive
g: send a 410 gone error to the browser
Not only can you rewrite URLs using rules, but you can also add conditions to these rules, so they won't be executed in every case:
RewriteCond Test Condition
Here's an example of what you can do with conditions:
RewriteCond %{HTTP_USER_AGENT} ^Opera.*
RewriteRule ^/$ /index_opera.php [L]
RewriteCond %{HTTP_USER_AGENT} ^Netscape.*
RewriteRule ^/$ /index_netscape.php [L]
RewriteRule ^/$ /index.php [L]
This will load a special page for Opera and Netscape, and load a default page for people not using Netscape. Here are some variables you can use in your conditions:
HTTP_USER_AGENT (browser)
HTTP_REFERER (referring page)
HTTP_HOST (domain name - yoursite.domain.com)
TIME, TIME_YEAR, TIME_DAY, TIME_HOUR, TIME_MIN, TIME_SEC
Get to work!
Now that I've (hopefully) wet your appetite, you can get working on some great uses of mod_rewrite. Here are some examples of what can be achieved:
/books/456/ » /index.php?mode=books&id=456
/books/456/buy » /index.php?mode=buy&id=456
/book_456.html » /index.php?book=456
... and Create beautiful url’s with mod_rewrite
The Apache rewrite engine is mainly used to turn dynamic url’s such as :
www.yoursite.com/product.php?id=123 into static and user friendly
url’s such as www.yoursite.com/product/123
RewriteEngine on
RewriteRule ^product/([^/.]+)/?$ product.php?id=$1 [L]
Another example, rewrite from:
www.yoursite.com/script.php?product=123 to
www.yoursite.com/cat/product/123/
RewriteRule cat/(.*)/(.*)/$ /script.php?$1=$2
You can use this code in your /admin/.htaccess:
RewriteEngine On
RewriteBase /admin/
RewriteCond %{ENV:REDIRECT_STATUS} 200 [OR]
RewriteCond %{REQUEST_FILENAME} -d [OR]
RewriteCond %{REQUEST_FILENAME} -f [OR]
RewriteCond %{REQUEST_FILENAME} -l
RewriteRule ^ - [L]
RewriteRule ^([^/]+)/?$ $1/index.php [L]
RewriteRule ^(.+?)/([^/]+)/([^/]+)$ $1/$2.php?id=$3 [L]

htaccess Redirect Causes Errors

I'm working on a website that has been built sloppily.
The website is filled with regular links that are translated into the corresponding .php pages by the .htaccess page.
This is it:
RewriteEngine on
RewriteRule ^koral/(.*)/$ page.php?name=$1
RewriteRule ^koral/(.*)$ page.php?name=$1
RewriteRule ^(.*).html/(.*)/(.*)/(.*)$ cat.php?cat=$1&page=$2&order=$3&dir=$4
RewriteRule ^(.*).html$ cat.php?cat=$1
RewriteRule ^(.*)/(.*).html$ product.php?cat=$1&product=$2
<IfModule mod_security.c>
SecFilterEngine Off
</IfModule>
First of all, I would love some help regarding whether or not this page has everything it should. I've never messed with it before.
Secondly and my main issue, if, for example, I would write the address www.thewebsite.com/foobar.html, it would be translated into www.thewebsite.com/cat.php?cat=foobar by the .htaccess page, and it would give a database error (and reveal information about the database).
I've put a check into cat.php which checks if the category exists, but I can't redirect the user to the 404 error page. There's a page called 404.shtml in the website, but redirecting the user to it causes the .htaccess to just change it again to cat.php?cat=404.
Is the way they used the .htaccess page normal? Should I change this system?
And how are users sent to error pages? From what I understood the server should be doing it on its own?
I would love some clarification... There is some much about this subject I don't understand.
Update:
This is my new .htaccess page
RewriteEngine on
RewriteRule ^error.php?err=(.*)$ Error$1.html
# Only apply this rule if we're not requesting a file...
RewriteCond %{REQUEST_FILENAME} !-f [NC]
# ...and if we're not requesting a directory.
RewriteCond %{REQUEST_FILENAME} !-d [NC]
RewriteRule ^koral/(.*)/$ page.php?name=$1
RewriteRule ^koral/(.*)$ page.php?name=$1
RewriteRule ^(.*).html/(.*)/(.*)/(.*)$ cat.php?cat=$1&page=$2&order=$3&dir=$4
RewriteRule ^(.*).html$ cat.php?cat=$1
RewriteRule ^(.*)/(.*).html$ product.php?cat=$1&product=$2
<IfModule mod_security.c>
SecFilterEngine Off
</IfModule>
Because the redirecting is in the code and the user cannot see it, I allowed myself to write the link in a non-clean way. I tried turning it into a clean URL but the following does not do anything:
RewriteRule ^error.php?err=(.*)$ Error$1.html
Can someone please help me understand why? I thought since error.php is a real page, I should put it before the conditional but it didn't work. BTW, I saw in an article about .htaccess that the page should start with Options +FollowSymLinks. It seems to me that everyone sort of has their own way of writing it. Is there a guide or something like that, which I can be sure is authentic and covers all the bases there is about .htaccess?
Thank you so much!!
Using rewrite rules to work around links to .html pages that don't exist is unusual in my experience, but it's really just a different take on "pretty" URLs, e.g. www.thewebsite.com/foobar/ gets routed to cat.php?cat=foobar on the backend.
Your 404 issue is different. You need to be able to display error pages.
One option here is to rewrite requests as long as they don't request an existing file. This is very common for serving up static content like images, CSS files, and the like. To do this, you can use the -d and -f options to RewriteCond, which apply when requesting a directory and file respectively:
RewriteEngine On
# Only apply this rule if we're not requesting a file...
RewriteCond %{REQUEST_FILENAME} !-f [NC]
# ...and if we're not requesting a directory.
RewriteCond %{REQUEST_FILENAME} !-d [NC]
RewriteRule ^([^.]+)\.html$ cat.php?cat=$1 [L,QSA]
Now, requests to 404.shtml should go through, because you're requesting an existing file on the filesystem.
Note that the RewriteConds only apply to the single RewriteRule that immediately follows. For additional RewriteRules, also include additional RewriteConds.
Your regex is wrong anywhere. Literal dot needs to be escaped using otherwise it will match any character. Also it is better to use L and QSA flags to end each rule properly.
RewriteEngine on
RewriteBase /
RewriteRule ^koral/([^/]+)/?$ page.php?name=$1 [L,QSA]
RewriteRule ^([^.]+)\.html/([^/]+)/([^/]+)/([^/]*)/?$ cat.php?cat=$1&page=$2&order=$3&dir=$4 [L,QSA]
RewriteRule ^([^.]+)\.html$ cat.php?cat=$1 [L,QSA]
RewriteRule ^([^/]+)/([^.]+)\.html$ product.php?cat=$1&product=$2 [L,QSA]

Apache mod_rewrite module issue

I have the following code in my .htaccess.
RewriteEngine On
RewriteRule ^/(\w+)/?$ /?user=$1
I'm trying to rewrite
http://domain.com/?user=username into http://domain.com/username. Unfortunately this code doesn't rewrite anything. Please help
Note:
I checked phpinfo() and mod_rewrite is loaded.
Update
I need to get username from url like http://facebook.com/username. But this code rewrites every folder in root folder, so my /css folder become http://domain.com/css/?u=common. How to allow this code works only for http://domain.com/index.php
The mistake you are doing is the use of / in the beginning of the line ^/(\w+)/?$
rewrite rules strips off the / from the beginning of the pattern to be matched in .htaccess and directory context.
Try doing this:
RewriteEngine On
RewriteRule ^(\w+)/?$ /?user=$1
From RewriteRule Directive docs :
What is matched?
In VirtualHost context, The Pattern will initially be matched against the part of the URL after the hostname and port, and before the query string (e.g. "/app1/index.html").
In Directory and htaccess context, the Pattern will initially be matched against the filesystem path, after removing the prefix that lead the server to the current RewriteRule (e.g. "app1/index.html" or "index.html" depending on where the directives are defined).
If you wish to match against the hostname, port, or query string, use a RewriteCond with the %{HTTP_HOST}, %{SERVER_PORT}, or %{QUERY_STRING} variables respectively.
Edit: Answer updated as per OP's request:
Add this :
RewriteEngine On
#do nothig if URL is trying to access the folder CSS.
RewriteRule *css/* - [L]
#checks where the URL is a valid file/folder.
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(\w+)/?$ /?user=$1
I think that you are doing it the right way round, but explained it the wrong way round!
Is the problem that you don't need the initial / as the URL passed to test doesn't include it!?
I suspect it should be RewriteRule ^(\w+)/?$ /?u=$1
Also, be careful you don't end up with a loop!

mod_rewrite with Trailing Slash breaks CSS/IMG/SCRIPTS paths

I'm trying to make mod_rewrite the first sub-directory string from url in order to create similar functionality as 'jsfiddle.net saved url's within a class/db. The script works fine and does the rewrite.
e.g. of url
http://jsfiddle.net/RyEue/
This works fine (loads all css, scripts, etc.):
http://www.domain.com/787HHJ2
This is what I've used in the past which does the trick.
The problem Is when ending URL with last slash, script, css and others loose path.
http://www.domain.com/787HHJ2/
rewrite script:
DirectoryIndex index.php index.html
Options +FollowSymlinks
RewriteEngine On # Turn on the rewriting engine
#RewriteBase /
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{QUERY_STRING} !.
RewriteRule ^.+/?$ index.php [QSA,L]
Unsure if this has to do with Rewritebase, I've tried multiple ways.
PS. I've tried setting paths to absolute (e.g. src="/img/theimage.jpg") without luck.
1. Make sure you have css/images/js etc linked relative to root folder (with leading slash): /styles/main.css
2. Add one of these ruls before current one:
# do not touch files with .css/.js/.jpg etc extensions
RewriteRule \.(css|js|jpg|png|gif)$ - [L]
or
# do not touch any resources in images/css/js folders
RewriteRule ^(images|css|js)/ - [L]
3. Clear browser caches and restart (sometimes browser may display cached page/resource when rewrite rule was fixed which brings a lot of confusion).
Try escaping
RewriteRule ^.+\/?$ index.php [QSA,L]

Categories