htaccess issue including period/fullstop (.) - .htaccess

I currently have this .htaccess rule the works fine:
RewriteRule ^instructor/([A-Za-z0-9-]+)$ instructor.php?username=$1 [NC,L]
However, when I attempt to add a period into the mix a lot of the rules on the site break so I am assuming the character isn't escaped correctly:
RewriteRule ^instructor/([A-Za-z0-9-\.]+)$ instructor.php?username=$1 [NC,L]
Anyone point me in the right direction please?
Update
It appears to be something to do with the directory structure.
Another selection of rules that apply to this site are the following:
## Registration
RewriteRule ^instructor/register/?$ instructor-form/index.php [L]
RewriteRule ^instructor/register/stage([1-5]+)$ instructor-form/stage$1.php [L]
These work fine (the directory here is instructor-form/
However, there is also a directory called instructor/ which these rules point to:
RewriteRule ^instructor/dashboard/?$ instructor/index.php [L]
RewriteRule ^instructor/account-details/?$ instructor/account-details.php [L]
RewriteRule ^instructor/change-password/?$ instructor/change-password.php [L]
These are the rules that are affected when adding the . into the first rule. The rules are all in order and work fine without the . in the [A-Za-z0-9-] char block. When added the physical instructor/ folder seems inaccessible.

You wrote so much text in your question but forgot to mention important details: what is actually broken? Because I do not have clear answer for that I will be speculating here based on the information you have provided so far.
RewriteRule ^instructor/([A-Za-z0-9-\.]+)$ instructor.php?username=$1 [NC,L]
The problem with this rule is that it will also rewrite already rewritten php files: instructor/index.php, instructor/account-details.php, instructor/change-password.php etc.
I think you are relaying on [L] flag too much .. or do not really know how mod_rewrite and [L] flag work. And that is why you are having this issue -- your rule with a dot in pattern rewrites already rewritten URLs.
Useful link: RewriteRule Last [L] flag not working?
You need to add some condition (global rule or condition specific to this rule only) to prevent rewriting already rewritten URLs or existing files.
1. Global rule -- place it somewhere on the top before other rules. Keep in mind that this may not work as intended depending on your website structure and rewrite logic (e.g. when you need to actually rewrite requests to already existing files or folders):
# do not do anything for already existing files
RewriteCond %{REQUEST_FILENAME} -f [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule .+ - [L]
2. Condition specific to that rule only:
a) do not rewrite if requested URI is physical file
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^instructor/([A-Za-z0-9-\.]+)$ instructor.php?username=$1 [NC,L]
OR
b) do not rewrite .php files
RewriteCond %{REQUEST_URI} !.+\.php$
RewriteRule ^instructor/([A-Za-z0-9-\.]+)$ instructor.php?username=$1 [NC,L]

Related

Display particular (dynamicaly) URL without parent folder. Leave The Others Alone

I started learning rewrites today
First of all here are examples of my links
Index.php?action=pictures
Index.php?action=pictures&type=kitchen
Index.php?action=pictures&type=ceiling
Index.php?action=services
Index.php?action=services&type=shower
Index.php?action=services&type=windows
In my .htaccess file I have this
RewriteEngine On
RewriteBase /
#Do not Rewrite files or folders
RewriteCond %{REQUEST_FILENAME} -f [NC,OR]
RewriteCond %{REQUEST_FILENAME} -d [NC]
RewriteRule ^(.*?)$ $1 [L]
#Ordinary
RewriteRule ^\.htaccess$ .htaccess [F]
RewriteRule ^(.*)/(.*)/?$ Index.php?action=$1&type=$2 [L]
RewriteRule ^(.*)/?$ Index.php?action=$1 [L]
Rewrites URLs to
localhost/pictures
localhost/pictures/kitchen
localhost/pictures/ceiling
localhost/services
localhost/services/shower
localhost/services/windows
//etc
I like to have ALL my links working as is with an exception of services where I don't need parent folder /services/.
Result:
localhost/pictures
localhost/pictures/kitchen
localhost/pictures/ceiling
localhost/services
localhost/shower
localhost/window
I tried to rewrite .htaccess but either I get only parentfolder to work or only subfolder.
I tried to add this but I do understand that this matches everything...
RewriteRule ^(.*)/?$ Index.php?action=services&type=$1 [L]
I can however hardcode it like this
RewriteRule ^window/?$ Index.php?action=services&type=window [L]
Would like to have something dynamical. If folder services -> show no folder yet still be able to see localhost/services!
Is it possible?
Think of it this way: How would Apache know if a particular string is an action, or a type of service?
Well, you have three options:
We hardcode the types of services. Anything that does not match a type must be an action.
We hardcode the actions. Anything that does not match an action must thus be a type of service.
Apache has no way of knowing: We feed it to a script, who might be able to do some magic to find out what this string is.
In your case hardcoding the actions seems like the best idea, at least when the actions are static.
RewriteRule ^(.*)/(.*)/?$ Index.php?action=$1&type=$2 [L]
RewriteRule ^(pictures|services)/?$ Index.php?action=$1 [L]
RewriteRule ^(.*)/?$ Index.php?action=services&type=$1 [L]

url rewrite how to send everything else to index.php condition

there,
This sould be a simple task for anyone who knows, but I am new to Apache rewrites, so please bear with me.
I wrote 2 rewrite conditions and they work. I need to write a third - so that everything else would go to index.php file. The problem is - if I add the third rule, it is always applied despite first 2.
RewriteEngine On
RewriteRule ^new/?$ new.php [NC,L]
RewriteRule ^thanks(.*)$ thankyou.php [NC,L]
RewriteRule ^(.*)$ index.php
Thanks for help.
I believe the answer lies in the following paragraph about the L flag used with the RewriteRule directive:
If you are using RewriteRule in either .htaccess files or in
sections, it is important to have some understanding of
how the rules are processed. The simplified form of this is that once
the rules have been processed, the rewritten request is handed back to
the URL parsing engine to do what it may with it. It is possible that
as the rewritten request is handled, the .htaccess file or
section may be encountered again, and thus the ruleset may be run
again from the start. Most commonly this will happen if one of the
rules causes a redirect - either internal or external - causing the
request process to start over.
I think what happens is that after the rewrite is executed, somehow control is given back to the URL parsing engine and the rules are run again.
You can prevent this behaviour by adding a few rewrite conditions to the last rule:
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteRule new/?$ new.php [NC,L]
RewriteRule thanks(.*)$ thankyou.php [NC,L]
# Only rewrite to index.php if the current request is not for an existing file or directory
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . index.php [L]
</IfModule>

How do you add a second possible variable to a clean url?

I have created a htaccess rewrite code for URLs so when a user goes to myurl.com/testing/ it shows them index.php?page=testing however I would like to have a second or maybe third page so it could look like myurl.com/testing/2832/9283 and would show users index.php?page=testing&var1=2832&var2=9283.
This is the code I currently have:
RewriteRule ^([^\/]+)/([^\/]*)/$ index.php?page=$1&var1=$2
RewriteRule ^([^\/]+)/([^\/]*)$ index.php?page=$1&var1=$2
This works but I want to make the variables optional. If I do not have a second variable (i.e. just myurl.com/testing/) then it says it cant find the file.
# 3-level deep parameters
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^([^/]+)(/([^/]+))?(/([^/]+))?(/)?$ /index.php?page=$1&var1=$3&var2=$5 [QSA,L]
This rule will not touch already existing files and folders.
This rule will rewrite:
/help/tracking/123456/ => /index.php?page=help&var1=tracking&var2=123456
/help/tracking => /index.php?page=help&var1=tracking&var2=
/help => /index.php?page=help&var1=&var2=
You were having page=index.php because your rule rewrites already rewritten URLs (A lot of people forgetting, that when rewrite happens, it goes to next iteration and starting to test all rules again). This rule has conditions (extra checks) to ignore already existing files and folders.
Why not just set multiple RewriteRules for each case?
RewriteRule ^([^/]+)/?$ index.php?page=$1
RewriteRule ^([^/]+)/([^/]+)/?$ index.php?page=$1&var1=$2
RewriteRule ^([^/]+)/([^/]+)/([^/]+)/?$ index.php?page=$1&var1=$2&var2=$3

Rewrite htaccess old oscommerce links

I am trying to rewrite all the old oscommerce links to a new website. But I am having trouble with part of the URL I need to rewrite.
The link looks like this:
http://www.domain.com/product_info.php?cPath=3_72&products_id=129&osCsid=6j3iabkldjcmgi3s1344lk1285
This rewrite works for the above link:
RewriteCond %{REQUEST_URI} ^/product_info\.php$
RewriteCond %{QUERY_STRING} ^cPath=3_72&products_id=129&osCsid=([A-Za-z0-9-_]+)$
RewriteRule ^(.*)$ http://www.domain.com/apple/air.html? [R=301,L]
But will not work for:
http://www.domain.com/product_info.php?cPath=3_72&products_id=129
My problem is that I want the rewrite to work no matter if the &osCsid=6j3iabkldjcmgi3s1344lk1285 part is included or not.
I think you can achieve this by not specifying the closing delimiter ($)
Give this a try:
RewriteCond %{REQUEST_URI} ^/product_info\.php$
RewriteCond %{QUERY_STRING} ^cPath=3_72&products_id=129
RewriteRule ^(.*)$ http://www.domain.com/apple/air.html? [R=301,L]
By not putting the $ at the end of the regex string you are basically saying: match any string that starts with ..., no matter what comes after
Hope this helps :)
This should do the job just fine:
RewriteCond %{QUERY_STRING} ^cPath=3_72&products_id=129
RewriteRule ^product_info\.php$ http://www.domain.com/apple/air.html? [R=301,L]
There is no need for separate condition RewriteCond %{REQUEST_URI} ^/product_info\.php$ -- this part can be (actually, SHOULD BE, for better performance) moved to RewriteRule.
This is enough ^cPath=3_72&products_id=129 -- it tells "When query strings STARTS with ...". No need to include optional/non-important parameters osCsid=([A-Za-z0-9-_]+).
This rule is to be placed in .htaccess file in website root folder. If placed elsewhere some small tweaking may be required.

How do I get the [L] flag of RewriteRule (.htaccess) really working?

To newcomers: While trying to comprehensively describe my problem and phrase my questions I produced huge ammount of text. If you don't want to read the whole thing, my observations about (read "proof of") [L] flag not working the misconception, from which it all sprung, is located in Additional observations section. Why I misunderstood apparent behaviour is described in my Answer as well as solution to given problem.
Setup
I have following code in my .htaccess file:
# disallow directory indexing
Options -Indexes
# turn mod_rewrite on
Options +FollowSymlinks
RewriteEngine on
# allow access to robots file
RewriteRule ^robots.txt$ robots.txt [NC,L]
# mangle core request handler address
RewriteRule ^core/(\?.+)?$ core/handleCoreRequest.php$1 [NC,L]
# mangle web file adresses (move them to application root folder)
# application root folder serves as application GUI address
RewriteRule ^$ web/index.html [L]
# allow access to images
RewriteRule ^(images/.+\.(ico|png|bmp|jpg|gif))$ web/$1 [NC,L]
# allow access to stylesheets
RewriteRule ^(css/.+\.css)$ web/$1 [NC,L]
# allow access to javascript
RewriteRule ^(js/.+\.js)$ web/$1 [NC,L]
# allow access to library scripts, styles and images
RewriteRule ^(lib/js/.+\.js)$ web/$1 [NC,L]
RewriteRule ^(lib/css/.+\.css)$ web/$1 [NC,L]
RewriteRule ^(lib/(.+/)?images/.+\.(ico|png|bmp|jpg|gif))$ web/$1 [NC,L]
# redirect all other requests to application address
# RewriteRule ^(.*)$ /foo/ [R]
My web application (and its .htaccess file) is located in foo subfolder of DOCUMENT_ROOT (accessed from browser as http://localhost/foo/). It has PHP core part located in foo/core and JavaScript GUI part located in foo/web. As can be seen from the code above, I want to allow access only to single core script that handles all requests from GUI and to 'safe' web files and redirect all other requests to base application address (last commented directive).
Problem
Behaviour
It works until I try the last part by uncommenting the last redirecting directive. If I comment some more lines, the appropriate page parts stop working, etc.
However, when I uncomment last line, which should be performed only when matching of all previous rules fails (at least that's what I understand), page goes into redirection cycle (Firefox throws error page with something like "This page isn't redirecting properly"), because it's redirecting to http://localhost/foo/ again and again and again, forever.
Questions
What I don't understand is this processing of this rule:
RewriteRule ^$ web/index.html [L],
specifically the [L] flag. The flag apparently doesn't work for me. When the last line is commented, it correctly redirects, but when I uncomment it, it is always processed, even though rewriting should stop on [L] flag. Anyone got any ideas?
Also, on a sidenote, I'd be thrilled to know why my following attempt at fixing it doesn't work either:
RewriteEngine on
RewriteRule ^core/(\?.+)?$ core/handleCoreRequest.php$1 [NC,L]
RewriteRule ^(.*)$ web/$1 [L]
RewriteRule ^.*$ /foo/ [L]
This actually doesn't work at all. Even if I remove the last line, it still doesn't redirect anything correctly. How does the redirecting work in the first example, if it doesn't work in the second?
It would also be of great benefit to me, if anybody knew any way to actually debug these directives. I spend hours on this without even the slightest clue what could possibly be wrong.
Additional observations
After trying the advice given by bbadour (not that I haven't tried it before, but now that I had a second opinion, I gave it another shot) and it didn't work, I've come up with the following observation. By rewriting last line to this:
RewriteRule ^(.*)$ /foo/?uri=$1 [R,L]
or this
RewriteRule ^(.*)$ /foo/?uri=%{REQUEST_URI} [R,L]
and using Firebug's Net panel, I found out more evidence, that the [L] flag is clearly not working as expected in the previously mentioned RewriteRule ^$ web/index.html [L] rule (let's call it THE RULE from now on). In first case I get [...]uri=web/index.html, in second case [...]uri=/foo/web/index.html. That means that THE RULE gets executed (rewrites ^$ to web/index.html), but the rewriting doesn't stop there. Any more ideas, please?
After hours of searching and testing, I finally found the real problem and solution. Hopefully this will help somebody else too, when they come across the same problem.
Cause of observed behavior
.htaccess file is processed after every redirect (even without [R] flag),
which means that after the RewriteRule ^$ web/index.html [L] is processed, mod_rewrite correctly stops rewriting, goes to the end of the file, redirects correctly to /foo/web/index.html, and then the server starts processing .htaccess file for the new location, which is the same file. Now only the last rewrite rule matches and redirects back to /foo/ (this time with [R], so the redirect can be observed in browser) ... and the .htaccess file is processed again, and again, and again...
Once more for clarity: Because only the hard redirects can be observed, it seems like the [L] flag is ignored, but it is not so. Instead, the .htaccess is processed two times redirecting back and forth between /foo/ and /foo/web/index.html.
Solution
Disallow direct access to subfolder
To virtually move subdirectory to application root directory, additional complex conditional rewrites must be used. Variable THE_REQUEST is useful for distinguishing between hard and soft redirects:
RewriteCond %{THE_REQUEST} ^GET\ /foo/web/
RewriteRule ^web/(.*) /foo/$1 [L,R]
For this rewrite rule to be matched, two conditions must apply. First, on second line, the "local URI" must start with web/ (which corresponds with absolute web URI /foo/web/). Second, on first line, the real request URI must start with /foo/web/ too. Together this means, that the rule only matches when the file inside the web/ subfolder is requested directly from the browser, in which case we want to do a hard redirect.
Redirect to allowed content from root to subfolder (soft)
RewriteCond $1 !^web/
RewriteCond $1 ^(.+\.(html|css|js|ico|png|bmp|jpg|gif))?$
RewriteRule ^(.*)$ web/$1 [L,NC]
We want to redirect to allowed content only if we haven't done it already, hence the first condition. Second condition specifies mask for allowed content. Anything matching this mask will be softly redirected, possibly returning 404 error if the content doesn't exist.
Hide all content not in subfolder or not allowed
RewriteRule !^web/ /foo/ [L,R]
This will do a hard redirect to application root for all URIs not beginning with web/ (and remember, only requests that can begin with web/ at this point are internal redirects for allowed content.
Real example
My code shown in my "question" after using solution tips mentioned above gradually transformed into the following:
# disallow directory indexing
Options -Indexes
# turn mod_rewrite on
Options +FollowSymlinks
RewriteEngine on
# allow access to robots file
RewriteRule ^robots.txt$ - [NC,L]
# mangle core request handler address
# disallow direct access to core request handler
RewriteCond %{THE_REQUEST} !^(GET|POST)\ /asm/core/handleCoreRequest.php
RewriteRule ^core/handleCoreRequest.php$ - [L]
# allow access to request handler under alias
RewriteRule ^core/$ core/handleCoreRequest.php [NC,QSA,L]
# mangle GUI files adressing (move to application root folder)
# disallow direct access to GUI subfolder
RewriteCond %{THE_REQUEST} ^GET\ /foo/web/
RewriteRule ^web/(.*) /foo/$1 [L,R]
# allow access only to correct filetypes in appropriate locations
RewriteCond $1 ^$ [OR]
RewriteCond $1 ^(images/.+\.(ico|png|bmp|jpg|gif))$ [OR]
RewriteCond $1 ^(css/.+\.css)$ [OR]
RewriteCond $1 ^(js/.+\.js)$ [OR]
RewriteCond $1 ^(lib/js/.+\.js)$ [OR]
RewriteCond $1 ^(lib/css/.+\.css)$ [OR]
RewriteCond $1 ^(lib/(.+/)?images/.+\.(ico|png|bmp|jpg|gif))$
RewriteRule ^(.*)$ web/$1 [L,NC]
# hide all files not in GUI subfolder that are not whitelisted above
RewriteRule !^web/ /foo/ [L,R]
What I don't like about this approach is that the application root folder must be hardcoded in .htaccess file (as far as I know), so the file must be generated on application install, not simply copied.
To debug, try simplifying your regex, and the url you ask for (a part of the full url you wanna match), and see if it's working, now step by step, add more bits to the regex adn the testing url, till you find where things are stopping to work properly.
Try using:
RewriteRule ^(.*)$ /foo/ [R,L]
If it still loops, put a RewriteCond in front of it to skip the rule if it is already /foo/

Resources