14

What I want to ask is if there is a way to find out if a web-server instance has URL Rewriting enabled. I need this in order to be able to instantiate the correct type of URL handler.

Theoretically you know in advance if you have it enabled or not and can use something to configure it. I would like, however, to be able to detect this setting automatically at runtime.

The URL rewrite rule would be something very simple like:

^/(.*)$ => /bootstrap.php

This guarantees that the relevant string is present in the REQUEST_URI, but doesn't pollute the _GET array.

Where did my research took me so far:

  1. Apache.
    In my opinion Apache has a very quirky approach, since it sets the REDIRECT_SCRIPT_URI header for rewrote URLs, but not for the ones that are not rewrote.
    E.g.

    http://host/ana/are/mere
    would be re-wrote to index.php so the aforementioned header would be present, but
    http://host/
    wouldn't be re-wrote.
  2. Lighttpd.
    Lighttpd with fast-cgi behaves OK, setting the REDIRECT_URI header if URL Rewrite is enabled for the current host. This is reliable.

  3. Cherokee.
    Well, for Cherokee there is no method that I found out, as it uses (in my opinion) a more complicated method for obtaining URL rewriting. (I.e., it's called internal redirect – and the fcgi process doesn't know that the request was redirected)

Also I haven't tested other http servers, as nginx, so if someone has some input on this matter I would love to hear it.

Charles
  • 50,943
  • 13
  • 104
  • 142
Marius Or.
  • 374
  • 4
  • 10

5 Answers5

14

Not the most elegant solution, but you could create a directory, insert a .htaccess and a small php file and try to open it with curl/file_get_contents() from your actual code:

.htaccess

RewriteEngine on
RewriteRule ^(.*?)$ index.php?myparam=$1

index.php

<?php
//open with file_get_contents("http://yoursite/directory/test")
if($_GET['myparam']){die("active");}
?>

Although this might be acceptable during an installation, for performance reasons this shouldn't be used for every request on your site! Save the information somewhere (sqlite/textfile).

Update

Apache specific, but apache_get_modules()/phpinfo() in combination with array_search/strpos is maybe helpful to you.

merkuro
  • 6,161
  • 2
  • 27
  • 29
9

It's already touched upon below, but I believe the following recipe is a rather waterproof solution to this problem:

  1. Set up the redirection

  2. Request a page through its rewritten url

  3. If the request returns the page in question, you have redirection set up correctly, if you get HTTP 404 response, then it's not working.

The idea is basically that this works with just about any redirection method. It has already been mentioned, but bears reiterating, such tricks add quite a bit of overhead and are better performed only once (installation or from the settings panel) and then saved in the settings.


Some implementation details, choices to make and a little on how I came to this solution:

I remembered Drupal did such a check during the installing process, so I looked up how they did it. They had the javascript on the install page do an ajax request (synchronously, to prevent concurrency issues with the database). This requires the user installing the software to have javascript turned on, but I don't think that's an unreasonable requirement.

However, I do think using php to request the page might be a cleaner solution. Alongside not bothering with a javascript requirement, it also needs less data to be sent back and forth and just doesn't require the logic of the action to be spread over multiple files. I don't know if there are other (dis)advantage for either method, but this should get you going and let you explore the alternative choices yourself.

There is another choice to be made: whether to test in a test environment or on the normal site. The thing Drupal does is just have the redirection always turned on (such as in the apache case, have the .htaccess file that does redirects just be part of the Drupal download) but only write the fancy urls if the redirection is turned on in the settings. This has the disadvantage that it takes more work to detect which type of redirection is used, but it's still possible (you can for example add a GET variable showing the redirection engine either on a specific test page or even on every page, or you can redirect to a page that sets $redirectionEngine and then includes the real index). Though I don't have much experience with redirection other than with mod_rewrite on apache, I believe this should work with just about every redirection engine.

The other option here is to use a test environment. Basically the idea is to either create a folder and set up redirection for it, or remove the need for file system write access and instead have a folder (or a folder for each redirection engine). This has some disadvantages: you still need write access to set up the redirection for the main site (though maybe not for all redirection engine, I don't really know how you all set them up properly - but for apache you will need write access if you are going to turn on redirection), it might be easier for a bot to detect what software and what version of it you are using through accessing the tests (unless you remove the test folders after testing) and you need to be able to rewrite for only a part of the site (which makes sense for any redirection engine to be a possibility, but I'm not blindly going to assume this functionality). However, this does come with the advantage of it being easier to find out which rewrite engine is being used or basically any other aspect of the redirection. There might also be other advantages I don't know of, so I just give the options and let you pick your method yourself.

With some options left to the user, I believe this should help you set up the system in the manner that you like.

Jasper
  • 11,590
  • 6
  • 38
  • 55
  • Nice solution. If this was implemented correctly it would be independent of the web server that was being used. – Luke Mills Dec 28 '12 at 05:33
6

PHP has server-specific functions for Apache, IIS and NSAPI servers. I only have Apache but as merkuro suggested this works as expected:

<?php
  if (in_array('mod_rewrite',@apache_get_modules()))
    echo 'mod_rewrite enabled';
  else
    echo 'mod_rewrite not enabled';
?>

As PHP server-specific functions don't cover all the servers you'd like to test in this probably isn't the best solution.

I'd recommend merkuro's first answer - implementing then testing it in script. I believe it's the only way to get a good result.

Hope that helps!

Al.
  • 2,872
  • 2
  • 22
  • 34
2

You can programmatically check for the existence of mod_rewrite if the server is Apache by using the apache_get_modules() function in PHP:

$modules = apache_get_modules();
echo in_array('mod_rewrite', $modules) ? 'mod_rewrite detected' : 'mod_rewrite not detected';

This could be used as the first step, but it is not a full proof method by any means. Just because mod_rewrite is loaded does not mean it is available for your environment. This also doesn't help if you are on a server that is not Apache.

There are not many consistent methods that will work across all platform combinations. But since the result is consistent, you can test for that. Setup a special redirect, and have a script use PHP's cURL or file_get_contents() to check a test URL. If the redirect was successful, you will get the expected content, and you can test easily for this.

This is a basic .htaccess I setup to redirect ajax to ajax.php:

RewriteEngine On
RewriteRule ajax ajax.php [L]

The following PHP script will attempt to get the contents of ajax. The real script name is ajax.php. If the redirect fails, then it will not get the expected contents.

error_reporting(E_ALL | E_STRICT);

$url = 'http://'.$_SERVER['HTTP_HOST'].dirname($_SERVER['REQUEST_URI']).'/ajax';
$result = json_decode(@file_get_contents($url));

echo ($result === "foobar") ? 'mod_rewrite test was successful' : 'mod_rewrite test failed'; 

Lastly, here is the final piece of the script, ajax.php. This returns an the expected response when the redirect is successful:

echo json_encode('foobar');

I have setup a live example of this test, and I have also made available the full sources.

Jordan Mack
  • 8,223
  • 7
  • 30
  • 29
1

As all the awnser already mention, actually testing it is the only way to be sure it works. But instead of actually redirecting to an actual page and waiting for it to load, I would just check the header. In my opinion this is quickly enough to be even used at runtime at a regular site. If it realy needs to be high performance, then ofcourse caching it is better.

Just put something like the following in your .htaccess file

RewriteEngine on
RewriteRule ^/redir/My/Super/Special/Hidden/Url/To/Test/$   /redir/longload.php  [L,R=307]

And then you can use the following php code to check if mod_rewrite is enabled.

<?php
function HasModRewrite() {
  $s = empty($_SERVER["HTTPS"]) ? '' : ($_SERVER["HTTPS"] == "on") ? "s" : "";
  $sp = strtolower($_SERVER["SERVER_PROTOCOL"]);
  $protocol = substr($sp, 0, strpos($sp, "/")) . $s;
  $port = ($_SERVER["SERVER_PORT"] == "80") ? "" : (":".$_SERVER["SERVER_PORT"]);


  $options['http'] = array(
    'method' => "HEAD", 
    'follow_location' => 0,
    'ignore_errors' => 1,
    'timeout' => 0.2
  );

  $context = stream_context_create($options);

  $body = file_get_contents($protocol . "://" . $_SERVER['SERVER_NAME'] . $port .'/redir/My/Super/Special/Hidden/Url/To/Test/', NULL, $context);
  if (!empty($http_response_header))
  {
    return substr_count($http_response_header[0], ' 307')>0;
  }

  return false;      
}


$st = microtime();
$x = HasModRewrite();
$t = microtime()-$st;

echo 'Loaded in: '.$t.'<hr>';

var_dump($x);
?>

output:

Loaded in: 0.002657
---------------------
bool(true) 
Hugo Delsing
  • 13,803
  • 5
  • 45
  • 72