Is it possible to build a RewriteRule regex with database values? - php

When my client adds a page to the site, the new pagename should be appended to a RewiteRule regex. So with, for instance fwrite(), I would like PHP to change that RewiteRule regex with values retracted from the database. If this could be done, are there any pitfalls in the process?
Edit: handling in a PHP script would be the solution, if there would'nt be more to it...
First domain/index.php?page=pagename is 301 redirected to "domain/pagename"
to warn the visitor this page is permanently moved - (this is the old
PUBLIC location of the URL and should give this 301). Then requests
like "domain/pagename" (the new public location), would be
silently,internally rewritten to domain/index.php?page=pagename where
verification takes place and a 404 is given when not valid. But just
the key, the "page" part of ?page=pagename, is static and can be
verified and will give a 404 directly from within the .htaccess . Now,
requests like domain/index.php?page=crap will first nicely give a 301
like the valid domain/index.php?page=pagename does, and only when
arrived in the index.php can be identified as crap. So there is still
a need to get the pagenames from the database to inside the .htaccess.
This is a sample of the .htacces content to give some background to this problem:
ErrorDocument 404 http://localhost/testsite/404.php
RewriteEngine on
RewriteBase /testsite/
## block craprequests without extension like domain/crap > 404
# The requests domain/pagename that do not go to existing pages, will now be redirected with a 302 to index.php?page=pagename and only then give a 404 through the errorcheck in the code.
# This should be done here, with a RewriteCond regex with database content
RewriteCond %{REQUEST_URI} !404.php$
RewriteRule .* 404.php [R=404,L]
## block-direct-queries ##
RewriteCond %{QUERY_STRING} !marker=1$
RewriteCond %{QUERY_STRING} page=(.*)
RewriteRule ^.*$ %1? [R=301,L]
## strip-extensions ##
RewriteCond %{QUERY_STRING} !.
RewriteCond %{REQUEST_URI} !404.php$
RewriteRule ^([\w+%*\d*\+*\-*]+)\.(php[\s]{0,3}|htm[\s]{0,3}|html[\s]{0,3})$ $1 [R=301,L]
## put-querystring
RewriteRule ^([\w\-_]+)\/?$ index.php?page=$1&marker=1 [L]

I'm sorry to keep repeating this back to you, but there just isn't a need for storing the page name in the .htaccess. This can all be done much more simply in PHP.
The only rewrite rule you need is this:
RewriteCond %{REQUEST_URI} !^/?index\.php$
RewriteRule .* /index.php [L,QSA]
Now, in PHP, you can do something like this:
// The important point here is that $_SERVER['REQUEST_URI'] contains the actual
// path the user typed into their browser, which is what you are interested in
if (strtolower(basename(parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH))) === 'index.php') {
// The user directly requested index.php
if (!empty($_GET['page']) || value_of_page_is_crap()) {
// The user requested a bad page
header("{$_SERVER['SERVER_PROTOCOL']} 404 Not Found");
} else {
// Redirect to correct URL
header("{$_SERVER['SERVER_PROTOCOL']} 301 Moved Permanently");
header("Location: http://{$_SERVER['HTTP_HOST']}/{$_GET['page']}");
}
exit;
}
// The request is allowed to continue
$requestedPage = pathinfo($_SERVER['REQUEST_URI'], PATHINFO_FILENAME);
The .htaccess will route every single request blindly through PHP, where much more precise logic than mod_rewrite's clunky PCRE-based rules can be used.
The PHP script examines the URI the user typed into their address bar in the browser. If they directly requested index.php, it will check whether $_GET['page'] contains a sensible value and if it does, redirect them to the correct URL, if not respond with a 404. If the user did not request index.php directly, the script can continue. I have added an example line to show how you could extract the value of the page they requested, but how you continue on from here is up to you.

This will most likely be possible (although writing permissions may be an issue). However wouldn't it be a better way to route all requests from the client through the index.php file and let PHP handle the routing.
This way you will be maximum flexible and you don't have to do "hacky" stuff.
EDIT
All forms of redirect can be done from PHP. E.g. an example of a 301 redirect:
header ('HTTP/1.1 301 Moved Permanently');
header ('Location: http://example.com/new/path'); // note the full address
exit();
Please see the manual for more information about the use of header().

Related

Avoiding 302 redirect when setting up multiple 404 pages

My error page is a PHP file, the entirety of which is
<?php
$path_parts = pathinfo($_SERVER['REQUEST_URI']);
$parts = explode('/', $path_parts['dirname']);
if (in_array("abcde", $parts))
{
header('Location: https://example.com/vwxyz/abcde-error-page.php');
exit();
}
else
{
header('Location: https://example.com/vwxyz/error-404-page.php');
exit();
}
?>
I want users who go to a non-existent URL in the "abcde" directory to see a particular error page, and the URL should be https://example.com/vwxyz/abcde-error-page.php and the status code should be 302. This is working just fine.
I want users who go to any other non-existent URL (in the "vwxyz" directory, or anywhere else on example.com) to see https://example.com/vwxyz/error-404-page.php, but for a 404 status code to be returned, and the URL in their browser should still be whatever they erroneously typed in. Of course, that doesn't work with header('Location ... and I can't use file_get_contents or include because the result empties the shopping cart and logs out the user (if they were logged in).
I feel that there is a simpler way to have multiple 404 pages without editing the .htaccess file (though the argument will probably be made that that is indeed the simpler way). And it is vital that the main error page behave exactly like a normal error page, but that the other (for the "abcde" directory) be a redirect, and that that special error page for the "abcde" directory NOT be in that directory (the user gets redirected out of the directory and then shown the "error"page, which should NOT return a 404 status).
If I understand the question correctly it is pretty simple via a root .htaccess file.
# enable mod_rewrite and route the request
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} ^/abcde/?.*$
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^.*$ /vwxyz/abcde-error-page.php [R=302,L]
# sets a custom 404 page
ErrorDocument 404 /vwxyz/error-404-page.php
First you need to switch the rewrite engine on, then perform 3 checks:
Does the URL begin with /abcde/
Is the URL not a file
Is the URL not a directory
If those things are all true then you want to route (with a 302) to /vwxyz/abcde-error-page.php
For any genuine 404 error you can just use the default ErrorDocument 404 ... syntax.
(OK this was 6 lines in .htaccess - twice as complicated as I guessed)

report error 404 by email with last accessed URL

I'm making a page 404 with button for user report that site have a problem. The idea is when user click on button, I receive a email with information of the last page accessed...
The problem is that I'm using the php variable '$_SERVER['HTTP_REFERER']' and this ever return a null value. I found this question (In what cases will HTTP_REFERER be empty) and I came to the conclusion that this variable is not the solution.
This is my .htacess
RewriteEngine on
ErrorDocument 404 http://example.com/erro.php
#ErrorDocument 404 /erro.php #this doesnt work... redirect doesnt work
On page erro.php I have code with function email that are working without problem, but I need some manner to take last accessed page that generate error.
On page erro.php I'm trying use:
echo $_SERVER['HTTP_REFERER']; // return null value
echo $_SERVER['REQUEST_URI']; // return page http://example.com/erro.php
I try use alternative with jQuery (https://stackoverflow.com/a/2415645/2761794):
On page erro.php
$(document).ready(function() {
var referrer = document.referrer;
alert(referrer); // return null value
});
Some suggestion to take last accessed URL on page erro.php for send by email?
As you are willing to work with mod_rewrite anyway and have PHP there is a slightly different approach.
First you could detect for a request that is for a file or directory that is not there and pass that to the PHP script with a rewrite.
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
Rewriterule ^(.*) /erro.php?error_path=$1 [R=301,L]
Depending on your exact setup you may need to tweak that last line perhaps to
Rewriterule ^(.*) http://example.com/erro.php?error_path=$1 [R=301,L]
I find that mod_rewrite is somewhat akin to voodoo and can sometimes need a little "try it out and see what works".
Then in your erro.php file:
<?php
$badfile = $_GET['error_path']; // the URL that 404'd
http_response_code(404); // send the 404 header code
// ... your other code
The end result for the visitor should be almost the same but you would have access to the data you need.
Much of the rewrite directive came from this question: htaccess errordocument 404 and pass url to path
For more on setting response codes in php: http://php.net/manual/en/function.http-response-code.php
mod_rewrite cheat sheet: http://www.cheatography.com/davechild/cheat-sheets/mod-rewrite/
The official doc on mod_rewrite: http://httpd.apache.org/docs/2.0/mod/mod_rewrite.html

Giving parameters to index.php with RewriteRule

I had a redirector, which gave everything after / in the url to the hidden index.php as a parameter (which then redirects the user with js' window.location later to an another domain, the host does not support external redirections by .htaccess), but I lost the code. Each file (index.php, .htaccess) is in the /storage folder.
The .htaccess was something like this, but I can't figure it out:
RewriteRule ^(.*) /?$1 [R,L]
This one is making an infinite loop of redirections.
It worked as entering http://storage.mysite.com/file.png would open http://storage.mysite.com/?file.png.
I tried to avoid the use of directly calling index.php in the .htaccess, as it redirected with this:
<?php
echo "
<script>
window.location='http://otherdomain.com/12345678".$_SERVER['REQUEST_URI']."'
</script>
"; // note there's no slash after the number, the REQUEST_URI had it
?>
What would be the proper way to do this?
Sounds like this is what you need:
RewriteCond %{REQUEST_URI} !^/index.php
RewriteRule ^(.*)$ index.php
In PHP, $_SERVER['REQUEST_URI'] will still be the original requested URI /file.png.
Getting rid of the R flag will fix the looping. Additionally, there is no need to add the request URI as a GET parameter as explained bellow.
The R flag means that the new address /?file.png is sent to the browser and the browser then makes a new request for that URI.
Removing the R flag will mean that Apache serves the new file index.php without telling the browser.
This means that although index.php is being parsed, the request URI is still /file.png.
RewriteCond %{REQUEST_URI} !^/index.php means if the request is for /index.php, then it is not rewritten.
I have tested this and it works. If you have any problems please comment.
* means "0 or more". If you only want to redirect if there is at least one then you should use + instead.

.htaccess 301 redirect not working

I have a basic CMS in PHP/MySQL where content managers can create pages to the system for public viewing. Each page is then available at an url such as http://www.example.com/pages.php?pid=123
Now, I want to redirect requests to http://www.example.com/pages.php?pid=123 to http://www.example.com/pages.php?pid=456.
I've already removed the pid=123 page from the db but because of the cms code the site still returns a 202 when some one tries to access the page. I thought I could use a 301 redirect in .htaccess to make the redirect work, i.e.:
redirect 301 pages.php?pid=123 http://www.example.com/pages.php?pid=456
but this doesn't work, Apache still return 202 when trying to fetch the pid=123 page. Also, I've tried using mod_rewrite, but it doesn't work:
RewriteRule ^pages.php?pid=123$ pages.php?pid=456 [R=301,L]
Any ideas what could be wrong and how I can fix the 301 redirect?
Both the Redirect and RewriteRule directive work just on the URL path. In mod_alias (Redirect directive) you can not test the query and in mod_rewrite (RewriteRule directive) you need an additional RewriteCond directive:
RewriteCond %{QUERY_STRING} (^|&)pid=123(&|$)
RewriteRule ^pages\.php$ /pages.php?pid=456 [R=301,L]
But it would certainly be better if your CMS can handle such redirects since it’s your CMS that knows best what URLs are valid and what are not.
You can perform the redirect in PHP (which probably knows more about what to redirect where) using header().
Please note that ? is a special character used by regular expressions, so your regex matches pages.phppid=123 and pages.phppid=123.
Even then, I don't think the query string (including the ?pid=123 part) is used in the URL handled by RewriteRule, so you would need to use something like:
RewriteCond %{QUERY_STRING} ^pid=123$
RewriteRule ^pages.php$ pages.php?pid=456 [R=301,L]
This shouldn't work as is, but it should give you some ideas.

How to stop direct execution of a php page using htaccess rules?

In my .htaccess file I have defined the following rule to make my register page URL as http://example.com/register/
RewriteRule register/ /register.php
The above rule is perfectly fine but I can access my register page from http://example.com/register/ as well as from http://example.com/register.php.
I don't want that user will be able to access the URL from http://example.com/register.php URL, is there any RULE which I can define in .htaccess to stop execution of register.php URL or simply redirect any direct register.php request to /register/
If you are doing this to avoid getting multiple links to the same content, you can simply don't use "register.php" anywhere on your page. I think no search engine will "guess" for a certain file type and if there are no security concerns you are on the safe side, because in my opinion no user will link to this file either. However if you want to be certain just reroute all your functionality through an index.php via one line in your .htaccess which should be placed inside your www-root directory:
RewriteEngine on
RewriteRule ^(.*?)$ index.php?file=$1
In your index.php you can then simply choose which function/file to invoke by breaking down and checking the $_GET["file"] parameter. To make 100% certain no one can access your register.php file directly just move it (and all your others) to a separate directory and include a .htaccess file with the following line:
DENY from all
There are a couple of other options to prevent direct access. Just define() a variable somewhere in your index.php and at the top of your register.php just put
defined('access') or die('Intruder alert!');
at the top. Another way could be to be honest and simply tell search engines that your content has been moved and that they no longer should use the old link:
header("Status: 301"); /* Content moved permanently */
header("Location: http://yourserver/Register/");
exit;
Update
Just one more thing that crossed my mind, you can also check $_SERVER["REQUEST_URI"], whether the user attached any ".php" and act accordingly by either denying access completely or just redirecting to the new location.
It is true that you cannot use location directive, but you can actually paste .htaccess file into any directory.
Just if you put this into it, say:
Options -Indexes
order allow,deny
deny from all
you can copy paste this file into any (root) directory you want to protect from external execution.
To check the initial requested URL path, you need to use the request line. So try this rule:
RewriteCond %{THE_REQUEST} ^GET\ /[^?\s]+\.php[/?\s]
RewriteRule (.+)\.php$ /$1 [L,R=301]
And then again your rule (in a slightly modified way):
RewriteRule ^register/$ register.php
If you want to completely block /register.php by using mod_rewrite, use a variant of SleepyCod's answer:
RewriteCond %{REQUEST_FILENAME} register\.php [NC]
RewriteCond %{IS_SUBREQ} false
RewriteRule .* - [F,L]
Explanation:
[NC]: Makes the condition case-insensitive, just in case you're on a windows box.
Condition 1: The requested filename is 'register.php', and
Condition 2: The request is no subrequest (this is important, since every new round through RewriteRules actually creates subrequests).
Rule: essentially do nothing
Flags: [F]: Send an 403 Forbidden header, [L]: This is the last rule to apply, skip all following rewrite rules
Rewriting correctly is an art by itself. I suggest you carefully read http://httpd.apache.org/docs/2.2/rewrite/.
Cheers,
Try this.
RewriteCond %{REQUEST_FILENAME} ^register\.php$ [NC]
RewriteRule ^/register register.php
Or this
Redirect register.php /register
Ignoring the user-experience part, you can implement the new rel=canonical link to sort out the search engines.
Although, for this case you should probably just use a 301 redirect from /register.php to /register/
In register.php
if ( stristr( $_SERVER['REQUEST_URI'], '.php' ) )
{
header ('HTTP/1.1 301 Moved Permanently');
header ('Location: /register');
}

Categories