Search code examples
php.htaccessmod-rewriterelative-pathabsolute-path

Absolute URL from base folder with rewrite


I have a project running local on WampServer. It's an MVC-like structure; it rewrites the URL to index.php?url=$1. Full .htaccess:

RewriteEngine On

RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-l
RewriteRule ^(.+)$ index.php?url=$1 [QSA,L]

When I want to send the user to another page using PHP location: <location> it doesn't do this properly because of the rewriting (all-though I am technically always in index.php).

For example if I am on http://localhost/project_name/controller/method/ and this controller's constructor or method tries to send me to:

  • header('location: another_controller/method'); sends me to http://localhost/project_name/controller/method/another_controller/method/
  • header('location: /another_controller/method'); sends me to http://localhost/another_controller/method/

But I want it to send me like this:

  • header('location: /another_controller/method'); sends me to http://localhost/project_name/another_controller/method/

Now the only solution I have found is:

define('BASE_URL','http://localhost/project_name');
header('location: '.BASE_URL.'/another_controller/method/');

But this isn't perfect either because it causes me to have to change this defined constant BASE_URL whenever the domain or folder name changes. I could also create a method in my BaseController that creates absolute URLs, but this method would basically just prepend BASE_URL too.

Note: The same problem doesn't arise with HTML's src and href attributes, which can use relative paths (without project_name folder in path). I don't understand why however. Because if the header location causes the browser to append the the relative-URL to the current location, why doesn't it have the same behavior when looking for .css or .js files.

So... this raises a couple of questions for me:

  1. Would I have this problem if I had virtual hosts?
  2. What is the best way to solve this problem?
  3. Is is best to just have the full absolute URL?
  4. Why do HTML's src and href attributes not share this behavior?

Solution

  • Now the only solution I have found is:

    define('BASE_URL','http://localhost/project_name');
    header('location: '.BASE_URL.'/another_controller/method/');
    

    But this isn't perfect either because it causes me to have to change this defined constant BASE_URL whenever the domain or folder name changes.

    You shouldn't need to change the defined constant. These values can be found dynamically.

    Example:

    if ($_SERVER['DOCUMENT_ROOT'] == dirname($_SERVER['SCRIPT_FILENAME'])) {
        define('BASE_PATH', '/');
    } else {
        define('BASE_PATH', dirname($_SERVER['SCRIPT_NAME']) . '/');
    }
    
    $protocol = isset($_SERVER['HTTPS']) ? 'https://' : 'http://';
    define('BASE_URL', $protocol . $_SERVER['SERVER_NAME'] . BASE_PATH);
    

    Additionally, rather than worrying about whether to specify an absolute URL or a path, you can wrap the redirect into a function which can handle both:

    function redirect($path = null) {
        if (isset($path)) {
            if (filter_var($path, FILTER_VALIDATE_URL)) {
                header('Location: ' . $path);
            } else {
                header('Location: ' . BASE_URL . $path);
            }
        } else {
            header('Location: ' . BASE_URL);
        }
        exit;
    }
    

    Finally, as @HarshSanghani mentioned in the comments, the base path in your .htaccess file should match the base path in your code. So if BASE_PATH (based on the example above) outputs /project_name/, then your .htaccess should accommodate it:

    RewriteBase /project_name/