mod_rewrite with GET requests - apache

I have mod_rewrite working on most of my site. Right now I have a search that normally would point to
search.php?keyword=KEYWORD
And I'm trying to rewrite that to
search/?keyword=KEYWORD
Just to make it a little bit cleaner. So here's my mod_rewrite. (There are other rules I'm just posting the one that isn't working.)
RewriteRule ^search/?keyword=([^/\.]+)/?$ search.php?search=$1
When I type a search in the address bar way I want it to be, I get a page telling me its a "broken link" (I'm guessing that that's Chrome's equivalent of a 404 error). So what am I doing wrong? I think that the problem is the '=' or the '?' sign in the rule (the first part) because when I take the ?keyword= part out, it works. Does that make sense?
EDIT: This is my full .htaccess code:
RewriteEngine on
RewriteRule ^$ index.php
RewriteRule ^thoughts$ archives.php
RewriteRule ^thoughts/$ archives.php
RewriteRule ^about$ about.php
RewriteRule ^about/$ about.php
RewriteRule ^search/\?keyword=([^/]+)$ search.php?search=$1
RewriteRule ^tags/([^/]+)$ tags.php?tag=$1
RewriteRule ^thoughts/([^/]+)$ post.php?title=$1 [L]
Still getting an error page...

If you just want to transform:
search.php?keyword=KEYWORD
into:
search/?keyword=KEYWORD
all you need to do is:
RewriteRule ^search/$ search.php [QSA]
The QSA flag means "query string append", and passes to search.php whatever you request via GET:
search/?keyword=KEYWORDD
search/?name=value&name2=value2
You may also want to check out Apache MultiViews, which sends every /foo request to any foo.* file it finds in the / directory, although they are considered bad.

RewriteRule ^search/\?keyword=([^/.]+)/?$ search.php?search=$1
The question mark character has special meaning in a regex. You need to escape it.
Additionally, the dot has no special meaning when inside a character class; you need not escape it (you're requiring that keyword contain no forward slashes and dots).

Related

mod_rewrite: hide real urls but keep available as different files

Possible this question has already been answered but I didn't find any answer after hours of searching.
I need to put the site under "maintenance mode" and redirect/rewrite all requests to site_down.html, but at the same time I need the site to be available if I enter the address like files are in a subfolder.
ex:
if I type http://example.com/login.php I need site_down.html to be displayed.
but if I specify http://example.com/test/login.php I need real login.php do be displayed.
I need this to be done with rewrite, so copying everything to another directory isn't a solution.
I tried a couple dozens of combinations, but I'm still unable to achieve what I need
This is one version of my .htaccess file ():
DirectoryIndex site_down.html
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteRule ^test\/(.*)$ $1 [S=1]
RewriteRule ^(.*\.php)$ site_down.html
RewriteRule .* - [L]
</IfModule>
This code should rewrite all requests with "test/*" to "parent folder" and skip next rewrite rule and then terminate rewriting at RewriteRule .* - [L]. If there is no "test/" in url - all request should be rewritten to site_down.html
What am I doing wrong?
Could you suggest any valid solutions, please?
Thank you.
Essentially, you are searching for 2 rules. One rule will translate a virtual subdirectory to the working files. The other rule will translate the url to the working files to a splash page. We just have to make sure that if the first rule matches, the second rule doesn't match. We can do this by making sure " /test/" (including that leading space) was not in THE_REQUEST (or the string that the client sent to the server to request a page; something in the form of GET /test/mypage.php?apes=bananas HTTP/1.1). THE_REQUEST doesn't change on a rewrite, which makes it perfect for that. Skipping a rule like you did usually doesn't have the effect you expect, because mod_rewrite makes multiple passes through .htaccess until the resulting url doesn't change anymore, or it hits a limit and throws an error. The first time it will skip the rule, but the second time it will not do that.
RewriteCond %{THE_REQUEST} !\ /test/
RewriteRule \.php site_down.html [L]
RewriteRule ^test/(.*)$ $1 [L]

How to add "everything else" rule to mod_rewrite

How can I make mod_rewrite redirect to a certain page or probably just throw 404 if no other rules have been satisfied? Here's what I have in my .htaccess file:
RewriteEngine on
RewriteRule ^\. / [F,QSA,L]
RewriteRule ^3rdparty(/.*)$ / [F,QSA,L]
RewriteCond %{REQUEST_FILENAME} -f
RewriteRule ^((images|upload)/.+|style.css)$ $1 [L]
RewriteRule ^$ special [QSA]
RewriteRule ^(special|ready|building|feedback)/?$ $1.php [QSA,L]
RewriteRule ^(ready|building)/(\d+)/?$ show_property.php?type=$1&property_id=$2 [QSA,L]
RewriteRule . error.php?code=404 [QSA,L]
This is supposed, among other things, to send user to error.php if he tries to access anything that was not explicitly specified here (by the way, what is the proper way to throw 404?). However, instead it sends user from every page to error.php. If I remove the last rule, everything else works.
What am I doing wrong?
What is happening is that when you are doing a rewrite, you then send the user to the new URL, where these rewrite rules are then evaluated again. Eventually no other redirectoin rules will be triggered and it will get to the final rule and always redirect to the error.php page.
So you need to put some rewrite conditions in place to make this not happen.
The rewrite engine loops, so you need to pasthrough successful rewrites before finally rewriting to error.php. Maybe something like:
RewriteCond %{REQUEST_URI} !^/$
RewriteCond %{REQUEST_URI} !^/(special|ready|building|feedback|show_property)\.php
RewriteCond %{REQUEST_URI} !^/((images|upload)/.+|style.css)$
RewriteRule ^ error.php?code=404 [QSA,L,R=404]
Each condition makes sure the URI isn't one of the ones your other rules have rewritten to.
The R=404 will redirect to the error.php page as a "404 Not Found".
Unfortunatelly, it didn't work - it allows access to all files on the server (presumably because all conditions need to be satisfied). I tried an alternate solution:
Something else must be slipping through, eventhough when I tested your rules plus these at the end in a blank htaccess file, it seems to work. Something else you can try which is a little less nice but since you don't actually redirect the browser anywhere, it would be hidden from clients.
You have a QSA flag at the end of all your rules, you could add a unique param to the query string after you've applied a rule, then just check against that. Example:
RewriteCond %{REQUEST_FILENAME} -f
RewriteRule ^((images|upload)/.+|style.css)$ $1?_ok [L,QSA]
then at the end:
RewriteCond %{QUERY_STRING} !_ok
RewriteRule ^ error.php?code=404&_ok [QSA,L,R=404]
In theory if none of the rules are matched (and the requested URL does not exist), it's already a 404. So I think the simplest solution is to use an ErrorDocument, then rewrite it:
RewriteEngine On
ErrorDocument 404 /404.php
RewriteRule ^404.php$ error.php?code=404 [L]
# All your other rules here...
You can do the same for any other HTTP error code.
The problem here is that after the mod_rewrite finishes rewriting the URL, it is resubmitted to the mod_rewrite for another pass. So, the [L] flag only makes the rule last for the current pass. As much better explained in this question, mod_rewrite starting from Apache version 2.3.9, now supports another flag - [END], that makes the current mod_rewrite pass the last one. For Apache 2.2 a number of solutions are offered, but since one of them was a bit clumsy and another didn't work, my current solution is to add another two rules that allow a specific set of files to be accessed while sending 404 for everything else:
RewriteRule ^((images|upload)/.+|style.css|(special|ready|building|feedback|property).php)$ - [QSA,L]
RewriteRule .* - [QSA,L,R=404]
I think your last rule should be
RewriteRule ^(.*)$ error.php?code=404&query=$1 [QSA,L]
You could leave out the parenthesis and the $1 parameter, but maybe it's useful to know, what the user tried to achieve.
Hope, this does the trick!

How does .htaccess work?

I'm trying to make my website display the other pages as a www.example.com/pageone/ link instead of www.example.com/pageone.html.
Problem is, i'm reading up on ways to do that using .htaccess and its getting me confused because i don't understand the commands.
I understand that the first step, however, is to write this in the .htaccess file:
RewriteEngine On
After this step, i have absolutely no idea whats !-d nor {REQUEST_FILENAME} nor ^(.*) and all that programming text. Is there a documentation that i can refer to?
Or can anyone provide me a simple explanation on how to configure the .htaccess file to understand that if i want to go to
www.example.com/pageone.html
, all i need to type into the URL is
www.example.com/pageone/
and PHP files, etc as well?
First of all, there's the Official Documentation. To solve your specific problem, I would go about this way:
RewriteEngine on #Turn on rewrite
RewriteCond %{REQUEST_FILENAME} !-f #If requested is not a filename...
RewriteCond %{REQUEST_FILENAME} !-d #And not a directory
RewriteRule ^([^/]+)/?$ /$1.html [L] #Preform this redirect
The RewriteConds only apply to the next following rule. If you were to have multiple rules, you'd need to write the conditions for each one.
Now, the Apache server matches the requested path (everything after www.example.com/), to see if it matches any of the rules you've specified. In which case, there is only one:
^([^/]+)$
This regular expression matches any number of characters, which are not slash /, followed by an optional trailing slash. If the match was found, it will rewrite the request to the second parameter: /$1.html, $1 means "Whatever was matched between the brackets", which in our case is all of the non-slash characters.
The [L] flag, tells the rewriting engine to stop looking for rules if this rule was matched.
So to conclude, www.example.com/whatever/ will be rewritten sliently at the server to www.example.com/whatever.html
RewriteEngine on
RewriteBase /
RewriteRule ^([^/]+)$ /$1.html
That should be all you need for this rewrite. It basically says "Anything that is not a forward slash will be assigned to the variable $1. So /foo would point to /foo.html
For official documentation you can look here Apache httpd mod_rewrite.
On Google you can search with keywords such as url rewriting tutorial.
The weird characters are called regular expressions. It's not an easy part to learn but there is a lot of tutorial about them.
PS: this is not a straight answer but some stuff to let you go further and understand how url rewriting works.

.htaccess mod_rewrite linking to wrong page

I have in my .htaccess the following code:
RewriteEngine On
RewriteRule ^/?([^/\.]+)/?$ $1.php [L]
RewriteRule ^/?([^/\.]+).php$ $1/ [R,L]
RewriteRule ^/?([^/\.]+)/?$ $1.php [L] is working fine. What this is doing is taking a url like http://www.example.com/whatever and making it read the page as http://www.example.com/whatever.php.
However, what I'd like to be able to do is take a url like http://www.example.com/whatever.php and automatically send it to http://www.example.com/whatever, hence the second line of the code. However, this isn't working. What its doing now, is as soon as it comes across a link ending in .php, the url becomes http://localhost/C:/Sites/page/whatever/, and pulling a 403: Forbidden page.
All I want to know is what I can to so that http://www.example.com/whatever.php will be read as http://www.example.com/whatever, and that if http://www.example.com/whatever.php is entered into the URL bar, it will automatically redirect to http://www.example.com/whatever.
Does that make any sense?
EDIT
Ok, so it appears I wasn't all too clear.. basically, I want /whatever/ to read as whatever.php while the URL still stays as /whatever/, right? However, if the URL was /whatever.php, I want it to actually redirect the users URL to /whatever/, and then once again read it as whatever.php. Is this possible?
If you're rules are inside an .htaccess file, you can omit the leading slash when you match against a URI:
RewriteRule ^([^/\.]+)/?$ /$1.php [L]
Also note that a leading slash is included in the target (/$1.php), this makes sure /whatever/ gets rewritten to /whatever.php. When you redirect, if you are missing this leading slash, apache prepends the document root to it. Thus /whatever.php gets redirected to the document root C:/Sites/page/whatever/. Even if you include the leading slash, this will never work because you're going to cause a redirect loop:
Enter "http://www.example.com/whatever.php" in your address bar
apache redirects you to "http://www.example.com/whatever/"
apache gets the URI whatever/ and applies the first rule and the URI gets rewritten to /whatever.php
The URI gets put through the rewrite engine again
the URI /whatever.php matches the second rule and redirects the browser to "http://www.example.com/whatever/"
repeat steps 3-5
You need to add a condition that the actual request is for /whatever.php:
RewriteCond %{THE_REQUEST} ^(GET|POST|HEAD)\ /([^/\.]+)\.php
RewriteRule ^ /%2/ [R,L]
So altogether, you'll have:
RewriteEngine On
RewriteRule ^([^/\.]+)/?$ /$1.php [L]
RewriteCond %{THE_REQUEST} ^(GET|POST|HEAD)\ /([^/\.]+)\.php
RewriteRule ^ /%2/ [R,L]
You're making a relative path substitution in a per-directory context (.htaccess is a per-directory context). This requires RewriteBase. Per-directory rewrites are done in a later stage of processing, when URLs have been mapped to paths. But the rewrite must produce a URL, which is processed again. I think without the RewriteBase to supply the URL prefix, you end up with a filesystem prefix instead of the URL. That may be why you're getting the C:/Sites thing. Try RewriteBase. But after a correct RewriteBase to specify the correct URL prefix to be tacked in front to the relative rewritten part, I'm afraid you will have the rewrite loop, because you're rewriting whatever.php to whatever; and whatever to whatever.php.
Reference: http://httpd.apache.org/docs/current/rewrite/tech.html

htaccess rewrite drive me nuts

I want to use a rather simple rewrite, something like this:
RewriteRule monitor.html index.php/\?first_category_id=B008 [NC,L]
But it doesn't work as expected, goes to like index.php/monitor.html (which kicks in symfony's routing and returns a 404 error but this is a different story)
However if i include full url like:
RewriteRule monitor.html http://example.com/index.php/\?first_category_id=B008 [NC,L]
it responses the correct content, but this looks like a full redirect, the rewrited url is revealed in the browser. And thats not transparent nor easily deployable.
What am i missing here?
the rest of the htaccess file if it matters:
RewriteCond %{REQUEST_URI} \..+$
RewriteRule .* - [L]
RewriteRule ^(.*)$ index.php [QSA,L]
Your rule is outputting a relative path and you're in a per-directory context. You need RewriteBase. In a per-directory context, rewriting is being done on expanded filesystem paths, not on the original URL's. But the results of the expansion are converted to a URL again! RewriteBase supplies the prefix needed to do that. Without it, the URL is naively made out of the same filesystem prefix that was stripped prior to the substitution and you end up with for instance http://example.com/var/www/docroot/blah... which is nonsense. Either RewriteBase or put out an absolute, beginning with a slash.
Also, you should anchor the match:
RewriteRule ^monitor.html$ ...
Otherwise the rule will potentially match somewhere in the middle of the path and just that matching part will be replaced with the substitution! You don't want to match and translate amonitor.htmly/foobar, right, and convert just the monitor.html part to a the index.php stuff.
You should not escape the question mark in the substitution. It's not a regexp! Just index.php/?etc not index.php/\?etc (Could that backslash be what is screwing up, causing `index.php/monitor.html'?)