I'm not obtaining the desired URLRewrite

Viewed 94

I am experiencing an odd problem with URLs. I'm using .htaccess and header(location) to set the web address.

I think that I'm "resetting" the web address at the bottom with header (location), but instead it looks like the address that I'm specifying gets appended to the current address rather than replacing it all.

My code is:

$route = ltrim(strtok($_SERVER['REQUEST_URI'], '?'), '/');
if($route == strtolower($route)) {

    if($route === 'contact/edit') {
            include __DIR__ . '/../myscripts/MainScript.php';
            $driver = new MainDriver();
            $page = $driver->edit();
    }elseif($route === 'contact/add' ) {
            include __DIR__ . '/../myscripts/MainScript.php';
            $driver = new MainDriver();
            $page = $driver->add();
    }else {
            include __DIR__ . '/../myscripts/MainScript.php';
            $driver = new MainDriver();
            $page = $driver->home();
    }
}else {
    http_response_code(301);
    header('location: index.php?route=' . strtolower($route));
}

.htaccess looks like this:

RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^.*$ /index.php [NC,L,QSA]
RewriteRule ^/contact/edit(new|existing)$ /index.php?route=/contact/edit$1 [NC,L,QSA]

localhost opens just fine. But, if I try to go to localhost/contact/editNew, instead of taking me to localhost/contact/editnew I am taken to localhost/contact/index.php?contact/editnew and the welcome page index.php gets loaded!

I think the last rewrite rule is what's going to have to be modified, but I don't know for certain. That's why I'm looking for help here. I wrote that rule based on a mod_rewrite page on apache.org that I read.

Let me know if you need more details to understand this issue.

(EDIT#2)

I have edited the header call to

header('location: ' . strtolower($route);

And I still obtain the same result, which confirms that the problem is caused by the URLRewrite rule that I'm learning now. Do you folks know how to fix this?

1 Answers

There's a few issues with the code you posted...

http_response_code(301);
header('location: index.php?route=' . strtolower($route));

You are passing a relative URL in the Location header. The browser is going to resolve this relative to the current URL. If the requested URL is /contact/editNew then the above will result in a redirect to /contact/index.php?route=<route>.

The URL you set in the Location should at least be root-relative (starting with a slash), or absolute (preferable).

This is also a 301 (permanent) redirect so will be persistently cached by the browser. (Test with 302 (temporary) redirects to avoid potential caching issues.)

You are redirecting to a query string route=<route>, which you are also trying to do in one of your RewriteRule directives. However, your PHP script is only configured to parse the URL-path - the query string is stripped from the request to calculate the route:

$route = ltrim(strtok($_SERVER['REQUEST_URI'], '?'), '/');

This retrieves the part of the URL before the query string and discards the query string. It also trims the / from the start of the URL-path, making it relative. So, again, if you later used header('location: ' . strtolower($route)); - you would have the same problem as mentioned above.

RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^.*$ /index.php [NC,L,QSA]
RewriteRule ^/contact/edit(new|existing)$ /index.php?route=/contact/edit$1 [NC,L,QSA]

The second rule does not appear to serve any purpose? With the slash prefix on the RewriteRule pattern and this appearing after the rewrite to /index.php it's not going to do anything anyway.

But neither does it need to do anything, at least not according to your PHP script. Your PHP script looks at the URL-path on the requested URL. It's not looking at the rewritten URL or query string.

Minor points, but the NC and QSA flags on the first RewriteRule are superfluous, and the regex could be simplified to just a dot. And the slash prefix on the substitution string is not required. For example:

RewriteRule . index.php [L]

If you simply, want to convert all URLs to lowercase then you could do this in .htaccess, before your existing directives:

# Convert URL to lowercase.
RewriteCond expr "tolower(%{REQUEST_URI}) =~ /(.*)/"
RewriteRule [A-Z] %1 [R=301,L]

UPDATE: Maybe you're wanting to pass the requested URL-path as a query string to your script? However, as noted, your PHP is currently using the URL-path, not the query string.

For example, your .htaccess would be like the following instead:

RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule (.*) index.php?route=$1 [QSA,L]

Your PHP script will now receive the route as part of the query string. Which you can examine using the $_SERVER['QUERY_STRING'] or $_GET['route'] superglobals in your PHP script. Or/and, you can still examine the requested URL-path, which you are currently doing, to provide two methods to access your pages.

Note, with the above rewrite, the route URL-parameter will not be present on requests to the home page, since that is handled by the DirectoryIndex (mod_dir).

Related