Link to home
Start Free TrialLog in
Avatar of Vipin Kumar
Vipin KumarFlag for India

asked on

Redirect and Rewrite URL using .htaccess

Hi,

I want to redirect and then rewrite my URL (internal forwarding) so that it doesn't show the php extension as well it doesn't show the ?, = and & in the URL.

This is the example of what I want to achieve, my actual URL passed to header is http://localhost/portal/error.php?error=runtime-error which I want to show in the address bar http://localhost/portal/error.php/error/runtime-error and internally this URL is forwarded to the actual URL http://localhost/portal/error.php?error=runtime-error.

Also, what I want to achieve is here is single redirect and single rewrite rule(for internal forwarding) which applies to all the scripts instead of adding individual redirect and rewrite rules for each script page.

Below is the code that is present in my .htaccess file.
ErrorDocument 404 /portal/error.php
ErrorDocument 500 /portal/error.php

RewriteEngine On
RewriteBase /
RewriteRule ^include/(.*)$ /portal/error.php [L,R=404]

Open in new window


Kindly let me know if any more information is required.

Thanks in advance
Avatar of gr8gonzo
gr8gonzo
Flag of United States of America image

Haven't tested this, but it'd probably be something like:

RewriteRule ^(.*error.php)\?error=(.*)$ $1/error/$2 [R=301,L]
Avatar of Vipin Kumar

ASKER

@gr8gonzo,

That is not working. As far as my knowledge I would be requiring two rewrite rules one for redirecting so that the browser address bar shows the clean URL i.e. http://localhost/portal/error.php/error/runtime-error and other rule to rewrite the clean URL and it becomes  http://localhost/portal/error.php?error=runtime-error
Hmm, perhaps this would work. I'm assuming the "portal" and "runtime-error" parts of the URL are variable.

RewriteCond %{QUERY_STRING} error=([^&]+)
RewriteRule ^(.*/error.php)$ $1/error/%1? [R=302,L]  # test with a 302 to avoid caching until it's confirmed as working
RewriteRule ^(.*/error.php)/error/(.*) $1?error=$2 [R=404,L]

Open in new window

@Terry,

Its not working. The redirection itself is not happening.
Hi Vipin,

Sorry, I have been away from my computer and didn't have a way to really test anything. Try these 2 lines instead of the one I sent you:

RewriteCond %{QUERY_STRING} ^error=(.*)$ [NC]
RewriteRule ^.*/error.php /portal/error.php/error/%1 [L,R=301,QSD]
Vipin, the code you're asking for is reasonably complex and unique. It's a normal part of developing such code to work through multiple suggestions/attempts and track down problems in order to get it working. It's hard for us to do that without access to your server, but you can do it yourself (depending on your level or knowledge), or work with us to do it.

For example, the first part of my suggested code needs to be working before the 2nd part will work (though you could still test the 2nd part by navigating to a URL that matches the pattern it targets), so let's look at the first part:

RewriteEngine On
RewriteBase /
RewriteCond %{QUERY_STRING} error=([^&]+) [NC]
RewriteRule ^(.*/error\.php)$ $1/error/%1? [NC,R=302,L]  # test with a 302 to avoid caching until it's confirmed as working

Open in new window


I've added the [NC] flag as done by gr8gonzo to allow mixed case. I also have added a backslash to the literal . character we're wanting to match, otherwise it gets treated as a wildcard (that wouldn't have stopped it working though).

I believe you also still need to use the rewriteengine on and rewritebase directives, in case you hadn't included those.

Can you confirm my assumptions that "portal" and "runtime-error" parts of the URL are variable?

To track down problems, you can simplify the pattern and see if the redirect works. eg this adjusted version should target any url with a query parameter of error=something:

RewriteEngine On
RewriteBase /
RewriteCond %{QUERY_STRING} error=([^&]+) [NC]
RewriteRule ^(.*) $1/error/%1? [NC,R=302,L]  # test with a 302 to avoid caching until it's confirmed as working

Open in new window

@Terry,

I completely agree with you that my request is quite unique, so it may take a bit of time to get an exact solution. It was just that my project is stuck on this and cant proceed ahead until I resolve this.

For your question - Can you confirm my assumptions that "portal" and "runtime-error" parts of the URL are variable? The "portal" is not variable as it is the root directory which contains all my scripts of my project, so you can be sure that "http://localhost/portal/" will always be in the URL on the other hand "runtime-error" is variable as different pages will be displayed on different values of "error=".

Let me give you a brief idea of my code based on which I am testing the htaccess. This is just a small part of my code, but majority of my project will be designed on this principal.

I have a "index.php" which includes a main configuration file (this file is the main file which contains information regarding the starting the session, setting server timezone establishing DB connection etc. This file will be included in all the script files that I create for my project). Below is the code that I use to check the whether the main file exits or not.

So as per my code below, when I access http://localhost/portal/ it checks for main file and currently it doesnot exist so it redirects my page to http://localhost/portal/error.php?error=runtime-error

if(!file_exists('bootstrap.php')){
		header('Location:error.php?error=runtime-error');
		die();
}

Open in new window


I tried you adjusted code, it is displaying the URL in the browser http://localhost/error.php/error/runtime-error which is not the correct, as it is missing the "portal" after the localhost.

I also tried to access the clean URL i.e. http://localhost/portal/error.php/error/runtime-error it gets to the error.php script but doesn't understand the value of the GET variable "error" and doesn't display the appropriate page.

Kindly let me know if any more information is required.

Thanks in advance.
Tested and working for me. To prevent an infinite loop, an additional query parameter is added by the second rule, and checked for non-existence by the first.

RewriteEngine On
RewriteBase /

RewriteCond %{QUERY_STRING} error=([^&]+) [NC]
RewriteCond %{QUERY_STRING} !redirect=done [NC]
RewriteRule ^(portal/error\.php)$ $1/error/%1? [NC,R=302,L]  # test with a 302 to avoid caching until it's confirmed as working

RewriteRule ^portal/error\.php/error/(.*) /portal/error.php?error=$1&redirect=done [NC,L]

Open in new window

@Terry,

I tried it, it is not working, i.e. now it is not even redirecting like it was doing earlier. Also if I access the URL http://localhost/portal/error.php/error/runtime-error it is still not able to understand the value of GET variable "error".

Thanks.
1. My script gives you the URL you're asking for.

2. If you end up at localhost/potral/error.php/error/runtime-error, then you are not going to get the parameters in your $_GET array because it's no longer a valid query string. You have to write code to parse out the URL at that point. There is no way to avoid that.
@gr8gonzo, your point 2 is incorrect, and my testing proved so when I ran it. A rewrite rule without a R flag loads a different URL (and in this case with different query params) to the one requested, but without doing a redirect.

With my code, that means the URL localhost/potral/error.php/error/runtime-error actually loads localhost/potral/error.php?error=runtime-error&redirect=done but doesn't show that in the browser as the user hasn't been redirected.
@Terry,

Any solution you have for this query of mine.
Sorry, didn't see your earlier post. Strange, it worked exactly at my end.

To narrow down the problem, I'd suggest commenting out the 2nd rewriterule, and simplifying the pattern for the first rule to try to get a match. eg target all .php requests (only try this if it won't affect anything else for the duration of the test), then try to access http://localhost/portal/error.php?error=runtime-error (it should redirect to http://localhost/portal/error.php/error/runtime-error which will fail to load the page)

RewriteEngine On
RewriteBase /

RewriteCond %{QUERY_STRING} error=([^&]+) [NC]
RewriteCond %{QUERY_STRING} !redirect=done [NC]
RewriteRule (.*\.php)$ $1/error/%1? [NC,R=302,L]  # test with a 302 to avoid caching until it's confirmed as working

#RewriteRule ^portal/error\.php/error/(.*) /portal/error.php?error=$1&redirect=done [NC,L]

Open in new window


If it still doesn't redirect, try commenting out the 2 rewritecond lines too and try again. We need to narrow down the problem.
@Terry,

I have tried the code, it works fine and it is redirecting. The error.php script is executing but the script is not able to get the $_GET variable "error" value which is "runtime-error" in current scenario.

Thanks
Are you able to edit the php script?

If you temporarily insert this as the first line of the file it should cause the script to output what's passed as query parameters and then exit:
<?php die("<pre>".print_r($_GET, true)); ?>

That may help us understand what's happening with the parameters.
@Terry,

When I add the <?php die("<pre>".print_r($_GET, true)); ?> line, i get this as output "Array()" that means $_GET variable value is empty.

Thanks
You did copy and paste the code, right?

The round brackets on this line:
RewriteCond %{QUERY_STRING} error=([^&]+) [NC]

Open in new window

capture the value of the "error" parameter and are then put back into the URL as %1 in the rewriterule. Can you please double check both those things are in place correctly?
@Terry,

I confirm I copied the code correctly and I even placed the php code on the top of the error.php script so that it executes first.

I also confirm that the RewriteCond is same as mentioned by you above in the htaccess file.

Thanks
Can you provide an actual value of one of the error messages you're using? That way I can re-run my test with what's essentially real data. If there's any unexpected handling of special characters, maybe that could explain the behaviour.
@Terry,

I m using the below code as of now to check if $_GET variable value is being passed..

if($_GET['error']=='runtime-error'){
	echo '4565';
}else{
	echo '123';
}

Open in new window


Thanks
Can you provide an actual value of one of the error messages you're using?
Do you have anything else in your .htaccess that could be losing the query parameter?

What happens if you call the error.php directly like this:
localhost/portal/error.php?error=houdini-disappearing-query-param&redirect=done
Because I've manually added the redirect=done parameter to that URL, it should prevent the first rewriterule from redirecting, and doesn't match the second rule. That means it should just pass the query parameter directly to error.php.

If you were using my code at the top of error.php:
<?php die("<pre>".print_r($_GET, true)); ?>

Open in new window

then the output should be this (which is what I get):
Array
(
    [error] => houdini-disappearing-query-param
    [redirect] => done
)

Open in new window

When I remove the redirect=done and just load:
http://localhost/portal/error.php?error=houdini-disappearing-query-param
then I get redirected to http://localhost/portal/error.php/error/houdini-disappearing-query-param and get the same output as before.
@Terry,

When i load localhost/portal/error.php?error=houdini-disappearing-query-param&redirect=done i am getting the expected output i.e.
Array
(
    [error] => houdini-disappearing-query-param
    [redirect] => done
)

Open in new window


When I load http://localhost/portal/error.php?error=houdini-disappearing-query-param or http://localhost:5489/portal/error.php?error=runtime-error i get redirected and the result is as below
Array
(
)

Open in new window


Below is the complete code in my .htaccess file
ErrorDocument 404 /portal/error1.php
ErrorDocument 500 /portal/error.php

RewriteEngine On
RewriteBase /portal/

RewriteCond %{QUERY_STRING} error=([^&]+) [NC]
RewriteCond %{QUERY_STRING} !redirect=done [NC]
RewriteRule (.*\.php)$ $1/error/%1? [NC,R=302,L]  # test with a 302 to avoid caching until it's confirmed as working

#RewriteRule ^portal/error\.php/error/(.*) /portal/error.php?error=$1&redirect=done [NC,L]

RewriteRule ^include/(.*)$ error.php [L,R=404]

Open in new window

There's a few differences from the code I successfully tested (ie this: https://www.experts-exchange.com/questions/28969579/Redirect-and-Rewrite-URL-using-htaccess.html?anchor=a41804503#a41799547 ).

Firstly, you've set the RewriteBase to a different value and adjusted the first RewriteRule. Do you need that to be a different value?

Secondly, the 2nd RewriteRule (necessary to be able to work) is commented out in your code, and hasn't been adjusted to work with the different RewriteBase value.

Code tested as working:
RewriteEngine On
RewriteBase /

RewriteCond %{QUERY_STRING} error=([^&]+) [NC]
RewriteCond %{QUERY_STRING} !redirect=done [NC]
RewriteRule ^(portal/error\.php)$ $1/error/%1? [NC,R=302,L]  # test with a 302 to avoid caching until it's confirmed as working

RewriteRule ^portal/error\.php/error/(.*) /portal/error.php?error=$1&redirect=done [NC,L]

Open in new window

@Terry,

I have pasted the above exactly in my .htaccess file replacing all the old code. Now the redirection is not happening, it is showing URL http://localhost:5489/portal/error.php?error=runtime-error in the address bar but the output is as expected.

Array
(
    [error] => runtime-error
)

Open in new window

SOLUTION
Avatar of Terry Woods
Terry Woods
Flag of New Zealand image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
@Terry,

I tried with the code you provided it is still not working. The URL doesn't redirect. Is it because in the main file i am passing the header as "header('Location:error.php?error=runtime-error');". Even I am not understanding why it is working at your end and not at my end.

Thanks
Not sure about whether the header might affect it; I'll get back to you if I can figure it out. You could try removing the / from ".*/error\.php" in my latest suggestion and see if it makes a difference.
@Terry,

We have some progress here.

I removed the / from both the Rewrite rules as per your last suggestion, I am getting the redirection as well as the output "Array( error => runtime-error    [redirect] => done)", but there is still one glitch the address bar is showing http://localhost:5489/portal/error.php/error/runtime-error?error=runtime-error i.e. there is a "?error=runtime-error" at the end.

Thanks
ASKER CERTIFIED SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
It's unlikely to be a problem, but bear in mind that with the more relaxed pattern the RewriteRule using .*error\.php it can also match a different URL like:
portal/someotherapp/adifferenterror.php?anothererror=blah
@Terry,

This works just fine.

What I wanted to know, is that do I need to create a separate rule for every script i.e. for example I have "admin.php" or  "users.php" or we can create a generic rule in htaccess which will work fine for all scripts.

Also, will this script work if there are multiple $_GET variables in URL for any of the scripts.

Thanks
Would you want admin.php to redirect to
admin.php/error/error-message
?

What other query parameters are you expecting, and is there a pattern for how they're named?
@Terry,

no I don't want admin.php to redirect to admin.php/error/error-message. It can redirect to anything for example admin.php?xyy=abc123&uij=ham etc.

as far as I think there will be no pattern in the query parameters they can be named anything.

thanks
You're probably best to create that as a separate question, provide some examples of what you want before and after.
@Terry,

I have opened a new question for the same can you please look into it.

https://www.experts-exchange.com/questions/28971545/Generic-Rule-to-Redirect-and-Rewrite-URL-using-htaccess.html

Thanks and unfortunately I cannot give more than 500 points for this question or else I would  have as you put a lot of effort to help me provide a solution. Looking for your assistance in future as well.
Thanks for all your help