I have made a .htaccess file to rewrite /username to /profile.php?=username
this is my htaccess file
Options All -Indexes
RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^([a-zA-Z0-9_-] )$ /profile.php?username=$1 [QSA,L]
the issue is that the other files in the same level are skipped unless if i added their .php
extention which is awful.
Can i prevent /username rewrite if that file exist and also do another rewrite in order to have URL without file extention ?
CodePudding user response:
You can keep these 2 rules in the given order:
Options All -Indexes -MultiViews
RewriteEngine On
## To internally rewrite /dir/file to /dir/file.php
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME}.php -f
RewriteRule ^(. ?)/?$ $1.php [L]
## for user profile
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^([\w-] )/?$ profile.php?username=$1 [QSA,L]
It is important to turn off -MultiViews
(content negotiation service) of Apache for this.
CodePudding user response:
@MrWhite nothing for now, i just want to stop rewrite when file exist
The rules would work together. If you check that the target .php
file exists before rewriting the request (on your extensionless URLs) - as you should be - then you don't need to apply the same filesystem check on your existing rule that rewrites the request to profile.php
.
For example:
# Append ".php" if request file without extension and target file exists
RewriteCond %{DOCUMENT_ROOT}/$1.php -f
RewriteRule ^([^.] [^/])$ $1.php [L]
# Rewrite user profiles (directory check is optional)
#RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^([a-zA-Z0-9_-] )$ profile.php?username=$1 [QSA,L]
I'm assuming your URLs do not contain dots (this naturally avoids having to make an exception for requests that end in a file extension). The regex ^([^.] [^/])$
matches URLs that do not contain a dot and do not end in a slash (ie. directories end in a slash).
Filesystem checks are relatively expensive, so are best kept to a minimum (or avoided altogether if possible). In the rule that appends the ".php" extension, there is no need to check that the request does not map to a directory before checking that the request does map to a file when .php
is appended, these checks are mutually inclusive. (But if a directory did exist then the file wouldn't be accessible - catch 22.)
Likewise, there is no need to check that the user-profile URL does not map to a file, unless you also have files that don't have a file extension (very unlikely and best avoided anyway if you do).
Even the directory check on the user-profile URL is debatable. This is only required if you need to be able to access subdirectories off the root directory directly.
With this limited set of rules, it doesn't really matter whether MutliViews is enabled or not. (Although best practice would dictate that MultiViews should be disabled here, to avoid future conflicts.) The effect of having MultiViews enabled will just mean the first rule that appends the .php
file extension is bypassed (not required). But having MultiViews enabled essentially enables extensionless URLs on everything.
Consider restructuring your user-profile URLs
HOWEVER, there is a fundamental "problem" with your user-profile URL structure - namely that they do "conflict" with actual file requests. The actual file requests will naturally take priority, but this means that you can't have usernames that happen to match files in the root directory - since the user profile will not be accessible. This check would need to be enforced when creating/updating user accounts.
It would be better to avoid this ambiguity to begin with and allow all usernames (that could also match root files) by creating a "unique" URL. eg. /user/<username>
. This also completely avoids having to perform the directory check. For example:
# Rewrite user profiles (directory check is not required)
RewriteRule ^user/([a-zA-Z0-9_-] )$ profile.php?username=$1 [QSA,L]