Search code examples
apache.htaccessmod-rewriteurl-rewriting

htaccess rewrites causing side affects


I have the following in my .htaccess file.

RewriteEngine On
RewriteRule ^([^/]+)?$ /member/profile.php?user=$1 [L]
RewriteRule  ^assets(/.*)?$  /member/assets$1 [L]
RewriteRule  ^images(/.*)?$  /member/images$1 [L]
RewriteRule  ^php(/.*)?$  /member/php$1 [L]

The desired effect is:

https://example.com/username -> https://example.com/member/profile.php?user=$1

This works, however, the issue is there are 2 undesired outcomes happening from this.

First: https://example.com and https://example.com/ return 404 errors but https://example.com/index.php works just fine.

Second: https://example.com/username/ ends up forwarding to https://example/member/php/?user=username and returning a 404 error.

I have also attempted

DirectoryIndex index.htm index.html index.php

But this seems to have no effect on the issue

My actual desired end result would look more like:

https://example.com -> https://example.com/index.php
https://example.com/ -> https://example.com/index.php
https://example.com/username -> https://example.com/member/profile.php?user=$1
https://example.com/username/ -> https://example.com/member/profile.php?user=$1

Solution

  • RewriteRule ^([^/]+)?$ /member/profile.php?user=$1 [L]
    

    First: https://example.com and https://example.com/ return 404 errors but https://domain.name/index.php works just fine.

    The first rule will catch the request (since it allows an empty URL-path) and will rewrite the request to /member/profile.php?user=. So, presumably it is your script that is triggering the 404?

    In fact, it looks like you are missing a slash before ? to match an optional trailing slash (ie. /username or /username/), rather than making the entire pattern optional! ie. ^([^/]+)/?$

    You would also need the NS (nosubreq) flag to prevent the subrequest by mod_dir for the DirectoryIndex (ie. index.php) also being caught by this rule. However, this rule is arguably matching too much, as it will also catch direct requests for index.php (and any other files you might have in the root). So, maybe you need to be more restrictive in what characters are allowed in usernames? For example, at a minimum, exclude dots (as well as slashes) with ^([^/.]+)/?$? Or allow only letters and numbers (and underscores), eg. ^(\w+)/?$. (\w is a shorthand character class that represents [0-9a-zA-Z_].)

    Note that the first rule will also match assets, images and php - so these are valid usernames. Is that intentional? You could reverse the rules so this does not happen, but you would need to ensure that there are no usernames that match these strings.

    NB: https://example.com and https://example.com/ are exactly the same request. (The browser effectively appends the slash after the hostname to make a valid HTTP request. See the following question on the Webmasters stack: Is trailing slash automagically added on click of home page URL in browser?)

    Second: https://example.com/username/ ends up forwarding to https://example.com/member/php/?user=username and returning a 404 error.

    I can't see how that would happen with the directives as posted. None of your rules would match /username/ (with a trailing slash), unless the username is "assets", "images" or "php" - but that still wouldn't result in the stated rewrite? However, /username/ would result in a 404 because nothing actually happens to rewrite the URL!

    Your rules should perhaps be written like this instead:

    RewriteEngine On
    RewriteRule ^(\w+)/?$ member/profile.php?user=$1 [L]
    RewriteRule ^assets(/.*) member/assets$1 [L]
    RewriteRule ^images(/.*) member/images$1 [L]
    RewriteRule ^php(/.*) member/php$1 [L]
    

    The capturing subpattern in rules 2, 3 and 4 is not optional, so I've removed the trailing ?$.

    I've also removed the slash prefix on the substitution string, to make it a relative file-path.

    Which could also be further "simplified" to:

    RewriteEngine On
    RewriteBase /member
    RewriteRule ^(\w+)/?$ profile.php?user=$1 [L]
    RewriteRule ^((assets|images|php)/.*) $1 [L]