htaccess and php routing - php

I was learning today about Rewrite and PHP routing. I wrote the following .htaccess file:
RewriteEngine On
RewriteRule ^(.+)$ testing.php?url=$1 [NC,L]
The testing.php has the following code:
<?php
echo $_GET["url"];
?>
When I enter the following URL for example: http://localhost/tests/Hello
The following is shown in the browser:
testing.php
But I am expecting "Hello" to show instead of "testing.php". I tried to change NC to QSA and it worked successfully but why the first case is not working?
Thank you.

It's likely that the URL is being rewritten more than once.
The first time rewrites /Hello to testing.php but then internally a separate request is made to testing.php and ran through the rewrite engine again. That will give you testing.php as the URL parameter.
Using QSA means that on the second request it is appending to the query string as opposed to overwriting it. If you looked at $_SERVER['QUERY_STRING'] you would likely see something like url=testing.php&url=Hello (or similar) and php is just using the last value in url.
Using NC is not necessary as the pattern you are using has no case (.+ matches one or more of any character regardless of case).
Using L means to stop processing rules in this request, but doesn't stop processing rules in subsequent requests (including transparent/internal subsequent requests).
Usually this is fixed by telling mod_rewrite to ignore the request if it is made to a file that exists using a RewriteCond like:
RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.+)$ testing.php?url=$1
Which tells apache that if the requested filename is not a file and not a directory, process the rule. With this in place, the request to testing.php will see the file exists, condition fails and so the rule isn't processed additional times. Nothing would stop additional rules from applying though if they exist. And also, just an FYI, conds only apply to the very next rule. So if you multiple rules, you need these cond for each of them.

QSA stands for Query-String-Append, which means that the Query string that was first sent gets appended. With NC, you were only doing nocase, meaning that the RewriteRule will be matched in a case-insensitive manner.
L just means that RewriteRule will stop processing it after that rule.

Related

Is there a way to change a php URL with htaccess?

I am trying to change:
example.com/profile.php?id=abcdefgh
To simply:
example.com/abcdefgh
I searched here on StackOverflow and I understood that I need to do something with my .htaccess, I tried this code:
RewriteEngine On
RewriteRule ^([^/]*)$ /profile.php?id=$1 [L]
But it seems nothing changes, when type this URL (example.com/profile.php?id=abcdefgh) it doesn't get rewritten.
P.S. I don't know if the above code is right, i tried it because where I got it from had a similar problem to mine.
This should work.
Options -MultiViews
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.+)$ profile.php?id=$1 [QSA,L]
You should be requesting the "pretty" URL, ie. /abcdefgh. Your mod_rewrite directive in .htaccess then internally rewrites this to the "real" URL that actually handles the request (ie. /profile.php?id=$1). That directive is expecting a URL of the form /abcdefgh. However, in its current state I should expect a rewrite loop:
Request /abcdefgh
Request is rewritten to /profile.php?id=abcdefgh.
Processing starts over...
Request is rewritten to /profile.php?id=profile.php (because the regex ^([^/]*)$ matches the profile.php part of the rewritten URL.
GOTO #3
In this example you can avoid the rewrite loop by simply making the RewriteRule pattern (regex) more restrictive. eg. Include a dot (.) in the negated character class (assuming your new "pretty" URLs do not contain a dot).
For example:
RewriteRule ^([^/.]+)$ /profile.php?id=$1 [L]
You then need to actually change the URLs on your website to the new "pretty" URLs.
However, if you are currently getting a 404 then maybe this directive isn't being processed at all? Are .htaccess files enabled? Do you have other directives in your .htaccess file?
UPDATE: in the id the only characters allowed are A-Z
In that case you should be more specific with the regex and match just the characters required. This helps to avoid conflicts and avoids the need for filesystem checks (to some extent) - which are relatively "expensive". Note, however, that you've stated "A-Z" but your example includes lowercase letters. For the sake of argument I'll assume the id can include a-z and A-Z.
So, this now becomes:
RewriteRule ^([a-z]+)$ /profile.php?id=$1 [NC,L]
No other filesystem checks (preceding RewriteCond directives) are necessary. The NC (nocase) makes the regex match case-insensitive.
There is no need to check that the request does not map to a file (with a preceding condition), since the regex ^([a-z]+)$ could never match a file (that includes a file extension).
There is also no need to check that the request does not map to a directory, unless you are requesting directories in the document root directly - which is probably "unlikely". However, therein lies an inherent conflict with this URL structure. If you needed to be able to access a directory then those directory names become invalid IDs - so you would need other checks elsewhere in your system to ensure no IDs were generated that map to physical directories in the document root.

Apache2 rewrite module, flag [L]

I have next rewrite rules:
RewriteEngine ON
RewriteBase /
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^([a-z]+)/(js|css|img)/(.+\.jpg|gif|png|js|css)$ media/myfiles/$1/$2/$3 [L]
RewriteRule .* index.php
I my application i have route class, that can process url's for my needs.
When i try to open file, that contains extension that will match to rewrite rule, i move to next rewrite rule, and my router class process this url...
Any ideas why apache doesn't stop after rule match first time?
P.S. first rule works after disabling second rule.
Take a look here: http://httpd.apache.org/docs/2.2/rewrite/flags.html
If you are using RewriteRule in either .htaccess files or in
sections, it is important to have some understanding of
how the rules are processed. The simplified form of this is that once
the rules have been processed, the rewritten request is handed back to
the URL parsing engine to do what it may with it. It is possible that
as the rewritten request is handled, the .htaccess file or
section may be encountered again, and thus the ruleset may be run
again from the start. Most commonly this will happen if one of the
rules causes a redirect - either internal or external - causing the
request process to start over.
(emph mine)
So what I think happens is that your last rule hits, and redirects. It doesn't call the bottom line. But then, the request is handled like any other request, your regexp DOESN"T hit, and in this run the bottom line DOES come into play.
This is also why it works when you disable the bottom rule: the second time around there is nothing to do, so nothing happens.

.htaccess QSA flag overridden by $_GET

This is my rewrite rule
RewriteRule ^(.+)$ index.php?uri=$1 [QSA,L]
When I go to this url
http://localhost/ub/ab?uri=cd
$_GET['uri'] returns cd, instead ab.
How to get $_GET['uri'] returns ab? Or is this the real behavior of QSA?
thanks,
EDIT
the .htaccess file is in ub directory, ab directory is never exist.
Don't use $_GET to to retrieve the uri parameter - .htaccess rules don't override the URI that's presented to your script, so you can retrieve the "ab" portion of the URI by parsing $_SERVER['REQUEST_URI'].
I've seen several PHP applications - such as Joomla! and Magento - whose .htaccess files simply redirect everything to "index.php", and leave it to the PHP routing code to translate the URI into a page to display.
Have your RewriteRule like this to ignore/overwrite prior Query String:
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.+)$ index.php?uri=$1 [L]
In your case $_GET['uri'] is realy cd due to ?uri=cd. ab is a part of URL, not a part of GET query.
I think this happen because you've defined uri= GET query parameter in both RewriteRule ^(.+)$ index.php?uri=$1 [QSA,L] and in address you access (http://localhost/ub/ab?uri=cd). It's overridden by second one.
Look:
You access http://localhost/ub/ab?uri=cd (query string is ?uri=cd)
mod_rewrite send it to index.php and appends URL to it in uri GET parameter ( > index.php?uri=ub/ab)
Then mod_rewrite (due to QSA) Appends Query String, so we get index.php?uri=ub/ab&uri=cd
So uri= placed twice in final request and is overridden by second one.
Just rename one of your GET uri parameters.

.htacces to capture the page name and send it as a get method

I am trying to capture a url such as
http://www.mysite.com/somepage.php?sometext=somevalue
and redirect it to.
http://www.mysite.com/index.php?page=somepage.php&sometext=somevalue
I tried searching for such .htaccess online, but couldn't find it.
Can you please help me?
I'm quite sure this is a duplicate, but I'm having a bit of an issue finding it/them [Edit: I found one, though possibly not the best example].
Anyway, this is a fairly standard problem resolved with fairly standard code:
RewriteRule ^(.*)$ index.php?get=$1 [L,QSA]
The RewriteRule captures the entire request as $1, and passes it to index.php as the page GET parameter.
The [QSA] flag on the end says to take any existing GET parameters (sometext=somevalue in your example), and add them as additional GET parameters on the new request. (The [L] flag just says that this should be the last rule executed.)
Note that this will also redirect requests for things like images or CSS files, so it's good to add the following lines directly before this rule:
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
These lines say "if the request is for a file or directory that actually exists, don't process the rule." That way, requests for real files will be served directly by Apache, rather than being handled (or more likely, mishandled) by your PHP script.
RewriteRule ^(.*).php?sometext=(.*)$ index.php?page=$1.php&sometext=$2 [QSA,L] #rewrite
RewriteRule ^(.*).php?sometext=(.*)$ http://www.mysite.com/index.php?page=$1.php&sometext=$2 [R=301,L] #redirect

Rewrite to index.php best practices

I notice that there are a few common ways to setup RewriteRules for MVC based PHP applications. Most of which contain:
RewriteCond %{DOCUMENT_ROOT}/%{REQUEST_FILENAME} !-f
RewriteCond %{DOCUMENT_ROOT}/%{REQUEST_FILENAME} !-d
Followed by a RewriteRule:
RewriteRule ^(.*)$ /index.php?$1 [L,QSA]
or
RewriteRule .* /index.php/$0 [PT,L]
I realize that L = LAST, QSA = query string appended, PT = pass through but as I don't have the real world experience of using these yet, could anyone inform me which flags and URI they would go with and why?
The latter rule contains a slash before the $0, I'm assuming because this forces it so the PATH CGI variable is populated, as often times I don't see it populated. Does the PT actually serve somewhat of the same purpose as the QSA, indirectly? Or how else would one use query strings? Basically, what are the pros and cons of these?
And just to confirm, if I wanted to add say an ErrorDocument directive would the L flag matter? Let's say a request to '/non-existing-link/' is made, my application cannot pick it up from the defined routes I have, nor is there an existing directory as such, would the L have any effect if I placed the ErrorDocument below the RewriteRule? Should I place it before the entire snippet? Same with 301s, 302s. And if I were to actually manually invoke 3xx/4xx codes, I would be using the header() function within my application, right? I kind of have a feeling this is quite dirty but is probably the most practical and only way of doing it hence it probably isn't dirty.
When the htaccess is read by the server, it goes line-by-line, trying to find a match. Without the L flag it will check every rule in the htaccess (though I'm not sure what happens if it finds multiple matches here).
If you include the L flag, when it gets to that rule, it will stop processing rules and serve the request. However, the gotcha here is that when it serves the request it will process the htaccess file from the beginning again with the new, rewritten URL. This page explains it well, with an example.
The ErrorDocument rule will be independent from the rewrite rules, so it doesn't matter where it comes (I usually put it at the top so it's obvious and not buried under a bunch of rewrites).
However, note that if a rewrite rule matches a valid file or script, the error document won't fire, even if the data/querystring is bogus. For example if a URL gets written to /index.php?page=NON_EXISTENT_PAGE then the server believes it has found the document. You will need to handle the parameter in the PHP script. Setting 404 headers in the PHP script won't automatically serve up the 404 document (but you can include it from the PHP script).
I have used zend framework suggestion for MVC application.
http://framework.zend.com/manual/en/zend.controller.html
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} -s [OR]
RewriteCond %{REQUEST_FILENAME} -l [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^.*$ - [NC,L]
RewriteRule ^.*$ index.php [NC,L]
The ErrorDocument setting will have no effect. If files are not found by Apache, the request is handled by PHP (as defined by these rewrite rules). Once inside PHP, you have to stay inside. Setting the response code to an error value with header() will not invoke Apache's error handling. You have to make your own code to present a decent error page.

Categories