1

What I want to do is letting the user enter a searchstring in the commandline and redirect him to the appropriate page. e.g.: the user types: www.mydomain.com/something and I direct him to www.mydomain.com/page.php?id=27408

What I have is a command in the .htaccess : ErrorDocument 404 /index_404.php so the request is sent to my php-script searching my database for the correct id and leading him to the requested page. – And this works fine.

Unfortunately there is a lot of traffic as every 404 error (e.g. some picture not found) is sent to that php-page and disturbs me. I had a solution with redirect 301 – but the server of my provider is not letting me generate a .htaccess by php-script.

So my question: Is there a way to check the request if it matches only letters and redirect only these requests. Something like:

ErrorDocument 404 (where request in [a..z]) /index_404.php

And letting the rest handle by Default? If so please send a decent example.

Here the basic code from my php-script:

if (isset($_SERVER["REQUEST_URI"])) {
    $in = $_SERVER["REQUEST_URI"];
    $in = str_replace("/","",$in); //  
    dbconnect();
    $sql = 'select * from menu where REPLACE(menu," ","") = "'. $in;    
    $result = @mysql_query($sql);
    if ($data = @mysql_fetch_array($result)) {
        $out = 'page.php?id='.$data['id'];                                  
    }
}

if (isset($out)) {
    header("Location: ".$out);
} else {
    header("Location: "."index.php");
}
Michael Berkowski
  • 267,341
  • 46
  • 444
  • 390
ratmalwer
  • 700
  • 5
  • 14
  • You're kind of abusing `ErrorDocument` here and approaching it an an unusual way. Can you post a little of the `index_404.php` so we can see how you're doing the redirection? (by REQUEST_URI or whatever) This would be better and more conventionally handled with `RewriteRule`... – Michael Berkowski Apr 26 '14 at 02:25
  • I agree. But im not familiar to the RewriteRule so I posted this Question. As I mentioned I look for a solution without the need of manipulating the htaccess when the cms is creating new menu-items. (a code-sample is added above). – ratmalwer Apr 26 '14 at 13:02
  • Ok, thanks for posting the code. I fleshed it out into an answer below. – Michael Berkowski Apr 26 '14 at 18:10

1 Answers1

2

As you've found, ErrorDocument can be used when you require every mimetype without exceptions to be sent there, but that's not the case for you.

Let's restructure your PHP code a bit so that you may break the dependency on ErrorDocument and use mod_rewrite more appropriately in your .htaccess. Instead of reading from REQUEST_URI, a more conventional way would be to build the PHP script to expect a parameter in $_GET.

The Rewrites:

First, setup the rewrites. You want to match letters only and send those to the PHP script. (This still uses index_404.php but you might want to rename it since it isn't handling 404's anymore). Requests like /abcd or abcd/efg will be passed to the PHP script. Real existing files like /main.css will not be sent to PHP; the file will be correctly served. Requests for non-existing files like /bad-image.png won't match the RewriteRule and will therefore be treated as a regular 404.

RewriteEngine On
# If the request isn't for an actual existing file
# (so images, CSS aren't interrupted)
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
# Send letters-only (and slashes) URI's to PHP into the query string param in=
# /? also allows a trailing slash optionally
RewriteRule ^([A-Za-z\/]+)/?$ index_404.php?in=$1 [L]

Note: I used [A-Za-z\/]+ to allow upper and lowercase, and also / since you had code to remove the / in your PHP. This allows URIs like /abc or /abc/def to be sent to PHP. If you only want to allow /abc and not /abc/def, use [A-Za-z]+ instead. If you don't want to permit uppercase, just remove the A-Z.

Now the PHP handler...

The above uses the query string to send to PHP. Modify your PHP to read $_GET['in'] instead.

if (isset($_GET['in'])) {
  dbconnect();

  // Looks like you permit slashes as input but remove them here...
  $in = str_replace("/","",$_GET['in']);

  // You must escape this even though the Apache 
  // rewrite only permits a-z/
  $in = mysql_real_escape_string($in);

  // Get rid of those @ error suppressors
  $sql = "select * from menu where REPLACE(menu,' ','') = '$in'";

  // Do your query
  $result = mysql_query($sql);
  if ($data = mysql_fetch_array($result)) {
      $out = 'page.php?id='.$data['id'];                                  
  }
}
if (isset($out)) {
    header("Location: " . $out);
} else {
    header("Location: index.php");
}

Note that the mysql_*() extension is deprecated in PHP 5.5. It will eventually be removed from the language, and new code should not be written with it. Instead, now is the time to start learning to use PDO or MySQLi instead. Both support prepared statements via prepare()/execute() which is the recommended way to secure your code against SQL injection.

Community
  • 1
  • 1
Michael Berkowski
  • 267,341
  • 46
  • 444
  • 390
  • It works perfect even with the german umlaute: -> look at the code `RewriteRule ^([A-Za-z\xC3\xA4\xB6\xBC\x84\x96\x9C\/]+)/?$ /index_404.php?in=$1 [L]`
    Your well commented example helped me a lot, so as the many good tipps. Soon I will read more about PDO and get rid of the @ (funny its still there…, somehow lazy from me). The mysql_real_escape_string function was removed from me in this example to keep it brief ( I know about sql-injection) but I have a look at the prepare-execute. Always hoping making better code …
    – ratmalwer Apr 27 '14 at 21:53