45

Does PHP's built in server not make use of .htaccess? Makes sense, I suppose, as it isn't relying upon Apache(?). Anyway, is it possible to tell the server to make use of these files - can it handle URL rewrites? I have some projects in frameworks that rely upon these files.

APPLICATION_ENV=development php -S localhost:8000 -t public/

6
  • 3
    php -S is not apache, why should it to read any apache config files?
    – vp_arth
    Commented Dec 9, 2014 at 14:38
  • 1
    you can to say to server your front controller by php -S ... public/index.php
    – vp_arth
    Commented Dec 9, 2014 at 14:40
  • Yeh but .htaccess files are commonly used in applications so I wondered if at all PHP server did handle them. I guess not. Pointing to a front controller is fine, but when I want to rewrite something like /news/view/205 url currently I can't.
    – Martyn
    Commented Dec 9, 2014 at 14:46
  • I don't understand your question that clearly. If you want you don't have to use .htaccess but you can in apache httpd.conf; there you can set url rewrite rules in between your virtualhost config directive. as a result; if you want to use rules on certain directory you can define it there. if you were to do that you'd need to reload the httpd.conf file when you do that.
    – unixmiah
    Commented Dec 9, 2014 at 15:09
  • I have no idea.
    – unixmiah
    Commented Jan 8, 2017 at 18:02

3 Answers 3

54

Here's the router that I use for the builtin php webserver that serves assets from the filesystem if they exist and otherwise performs a rewrite to an index.php file.

Run using:

php -S localhost:8080 router.php

router.php:

<?php

chdir(__DIR__);
$filePath = realpath(ltrim($_SERVER["REQUEST_URI"], '/'));
if ($filePath && is_dir($filePath)){
    // attempt to find an index file
    foreach (['index.php', 'index.html'] as $indexFile){
        if ($filePath = realpath($filePath . DIRECTORY_SEPARATOR . $indexFile)){
            break;
        }
    }
}
if ($filePath && is_file($filePath)) {
    // 1. check that file is not outside of this directory for security
    // 2. check for circular reference to router.php
    // 3. don't serve dotfiles
    if (strpos($filePath, __DIR__ . DIRECTORY_SEPARATOR) === 0 &&
        $filePath != __DIR__ . DIRECTORY_SEPARATOR . 'router.php' &&
        substr(basename($filePath), 0, 1) != '.'
    ) {
        if (strtolower(substr($filePath, -4)) == '.php') {
            // php file; serve through interpreter
            include $filePath;
        } else {
            // asset file; serve from filesystem
            return false;
        }
    } else {
        // disallowed file
        header("HTTP/1.1 404 Not Found");
        echo "404 Not Found";
    }
} else {
    // rewrite to our index file
    include __DIR__ . DIRECTORY_SEPARATOR . 'index.php';
}
2
  • 3
    I use this instead of making a whole new Apache virtual host when testing things out. Very useful snippet. Commented Aug 30, 2018 at 12:53
  • (Note that you could use $_SERVER['REQUEST_URI'] in index.php to get the actual request URL.) Commented Sep 6, 2020 at 1:34
22

It is not possible to handle .htaccess using PHP's built-in webserver (it is not relying on apache, it is implemented entirely in PHP's core). However, you can use router script (described here: http://php.net/manual/en/features.commandline.webserver.php).

E.g. php -S localhost -S localhost:8080 router.php

3
  • then you need to use .htaccess
    – unixmiah
    Commented Dec 9, 2014 at 15:12
  • 4
    You can't use .htaccess with PHP's built-in server. It is not supported. If you want to use .htaccess and not other way of configuring your webserver, then you either have to use Apache or parse .htaccess yourself in PHP (that probably would be a difficult thing)
    – Agares
    Commented Dec 9, 2014 at 15:45
  • 1
    ahh that makes sense. yes I agree.
    – unixmiah
    Commented Dec 9, 2014 at 16:36
2

We're currently working with legacy projects and I came accross with the same problem. Based on @Caleb's answer, we managed to add a few more controls:

  1. Route the request to an old htaccess router (url.php on the example below);
  2. Work with query string;
  3. Change the current directory to work with PHP includes;
  4. Plus: naming to server.php to match Laravel's PHP Builtin router.

Just type in the cmd: php -S localhost:8888 server.php

chdir(__DIR__);
$queryString = $_SERVER['QUERY_STRING'];
$filePath = realpath(ltrim(($queryString ? $_SERVER["SCRIPT_NAME"] : $_SERVER["REQUEST_URI"]), '/'));
if ($filePath && is_dir($filePath)){
    // attempt to find an index file
    foreach (['index.php', 'index.html'] as $indexFile){
        if ($filePath = realpath($filePath . DIRECTORY_SEPARATOR . $indexFile)){
            break;
        }
    }
}
if ($filePath && is_file($filePath)) {
    // 1. check that file is not outside (behind) of this directory for security
    // 2. check for circular reference to server.php
    // 3. don't serve dotfiles
    if (strpos($filePath, __DIR__ . DIRECTORY_SEPARATOR) === 0
        && $filePath != __DIR__ . DIRECTORY_SEPARATOR . 'server.php' 
        && substr(basename($filePath), 0, 1) != '.'
    ) {
        if (strtolower(substr($filePath, -4)) == '.php') {
            // change directory for php includes
            chdir(dirname($filePath));

            // php file; serve through interpreter
            include $filePath;
        } else {
            // asset file; serve from filesystem
            return false;
        }
    } else {
        // disallowed file
        header("HTTP/1.1 404 Not Found");
        echo "404 Not Found";
    }
} else {
    // rewrite to our router file
    // this portion should be customized to your needs
    $_REQUEST['valor'] = ltrim($_SERVER['REQUEST_URI'], '/');
    include __DIR__ . DIRECTORY_SEPARATOR . 'url.php';
}

Not the answer you're looking for? Browse other questions tagged or ask your own question.