Forum Moderators: phranque
Second, let it be known that I am a mod_rewrite hack, just learning, and I thought I had the basics understood, until... I tried to redirect people trying to login to my site (https) to a secure login (https). Here are the rules I thought would work, but are getting me nowhere. (Jim/jdMorgan, I have read many of your awesome posts but can't seem to put 2 and 27 together):
RewriteEngine on
Options +FollowSymLinks
RewriteBase /
#redirect them to https.
#rule_source_is:http://joseph.randomnetworks.com/archives/2004/07/22/redirect-to-ssl-using-apaches-htaccess/
RewriteCond %{HTTPS} off
RewriteCond %{REQUEST_URI} ^login\.php$
RewriteRule (.*) [%{HTTP_HOST}%{REQUEST_URI}...]
I also tried:
%{REQUEST_FILENAME} login.php
and various other shotgun attempts. In the end, I would like to be able to list out a handful of files that redirect/rewrite into https. Something like:
RewriteCond %{REQUEST_URI} ^(login.php¦edit_billing.php)$
so all my sensitive pages would be ssl. Additionally, as a bonus, I would love to do the inverse for my non-ssl pages... like if someone travels from a https page to, lets say, index.php, I would like it to revert to http again. I would prefer to not have to use seperate directories for the files, but am definitely willing to append some encoding like "_secure.php" to file names to make the rules work. Looking forward to being humbled...
You can indeed make a list of pages to be redirected from HTTP to HTTPS. Then it is a simple matter to negate the regular-expressions pattern you used to do that for use in redirecting non-HTTPS pages back to HTTP. Taking your example, this would be
^(login\.php¦edit_billing\.php)$
and
!^(login\.php¦edit_billing\.php)$
You may want to try using
RewriteCond %{SERVER_PORT} ^443$
and
RewriteCond %{SERVER_PORT} !^443$
instead of
RewriteCond %{HTTPS} on [NC]
and
RewriteCond %{HTTPS} off [NC]
# Redirect secure pages to HTTPS if requested with HTTP
RewriteCond %{HTTPS} !^443$
RewriteRule ^(login\.php¦edit_billing\.php)$ https://www.example.com/$1 [R=301,L]
#
# Redirect non-secure pages to HTTP if requested by HTTPS
RewriteCond %{HTTPS} ^443$
RewriteCond %{REQUEST_URI} !^/(login\.php¦edit_billing\.php)$
RewriteRule (.*) http://www.example.com/$1 [R=301,L]
Replace all broken pipe "¦" characters above with solid pipe characters before use; Posting on this board modifies the pipe characters.
Jim
# Redirect secure pages to HTTPS if requested with HTTP
RewriteCond %{SERVER_PORT}!^443$
RewriteRule ^(login\.php¦edit_billing\.php)$ [example.com...] [R=301,L]
#
# Redirect non-secure pages to HTTP if requested by HTTPS
RewriteCond %{SERVER_PORT} ^443$
RewriteCond %{REQUEST_URI}!^/(login\.php¦edit_billing\.php)$
RewriteRule (.*) http://www.example.com/$1 [R=301,L]
The %{SERVER_PORT} was the trick... and how you knew to suggest that alternate is beyond me.
Thanks again Jim for your expertise, time, and promptness... you just took a major weight off my shoulders for a looming deadline. I'll be sure to learn from your code...
<link rel="stylesheet" type="text/css" href="mystylesheet.css" media="screen" />
and that sheet contains this relative background image in the body:
body {
background: url("images/image.gif") repeat-x 0 0;
}
The image loads fine, but for some reason only loads in http: ... I haven't fully looked into this yet and will post something once I find out about it, but until then, feel free to toss in your suggestions.
Anyway, didn't Einstein say that he would spend 99% of his time definining the problem, and 1% fixing it? I've defined the problem, sort of. But now I need to get into mod_rewrite to write a rule (or an exception in an existing rule) that says "hey, when referencing the external style sheet, don't apply the rules above" or something like that. Sorry. I talk a lot... and I am definitely no Einstein...
[webmasterworld.com...]
has been an invaluable guide in helping me tweak the code. Here is what I have, and it is working great (note, I prefixed each piece with comments as if I was coding in PHP to help me understand what is going on):
RewriteEngine on
Options +FollowSymLinks
#RewriteBase /
#
# Redirect secure pages to HTTPS if requested with HTTP
# IF uri does NOT contain a .css file AND...
# IF server port is NOT 443 AND...
# IF the uri contains login.php OR edit_billing.php OR etc...
# THEN make it secure with https
RewriteCond %{REQUEST_URI}!^/mystyle\.css$
RewriteCond %{SERVER_PORT}!^443$
RewriteCond %{REQUEST_URI} ^/(login\.php¦edit_billing\.php)$
RewriteRule (.*) https://www.mysite.com/$1 [R=301,L]
#
# Redirect non-secure pages to HTTP if requested by HTTPS
# IF uri does NOT contain a .css file AND...
# IF port is 443 AND...
# IF the uri does NOT contain login.php OR edit_billing.php OR etc...
# THEN make it insecure
RewriteCond %{REQUEST_URI}!^/mystyle\.css$
RewriteCond %{SERVER_PORT} ^443$
RewriteCond %{REQUEST_URI}!^/(login\.php¦edit_billing\.php)$
RewriteRule (.*) http://www.mysite.com/$1 [R=301,L]
Some questions/challenges (the perfectionist in me) I'm facing:
1) i tried to test the passed url (e.g. [mysite.com...] and not modify it using:
RewriteCond %{REQUEST_URI}!^/*\.css$
but that didn't work. Not sure why. Learning as we speak.
2) Jim, why did you mention before that you have to have a leading / when testing REQUEST_URI?
3) Is there a way to see all the variables I have available to test, and to see what their actual value is b4 testing them? That would make things a lot easier...
4) Jim, in your original solution, you had a rewriterule that said:
RewriteRule ^(login\.php¦edit_billing\.php)$ [example.com...] [R=301,L]
I'm assuming that is shorthand of cond/rule in one statment. Any advice/tips on that, because that was the thing that really confused me in the beginning (hence, I broke it up in my statements...) is that bad for performance or anything?
2) Because the following two snippets are equivalent and correct:
RewriteRule [b]^f[/b]oo\.php$ /bar.php [L]
#
RewriteCond %{REQUEST_URI} [b]^/f[/b]oo\.php$
RewriteRule . /bar.php [L]
Note that in .htaccess (per-directory context), the URL-path 'seen' by RewriteRule does not start with "/", whereas the value in %{REQUEST_URI} always does.
3) The list of variables accessible to mod_rewrite is in the RewriteCond documentation. One way to see them is to use a test rule like:
RewriteRule ^foo\.php$ http://www.example.com/bar.php?Req_URI=%{REQUEST_URI}&Host=%{HTTP_HOST} [R,L]
4) Because RewriteRule can test a version of REQUEST_URI, it is far faster and simpler to use that facility when possible. Also, RewriteConds are not processed unless the RewriteRule pattern matches, so it's best to make that pattern as selective as possible. See the rule processing diagram in the mod_rewrite docs.
Continue to follow Albert's directive: "Make everything as simple as possible, but no simpler." And avoid introducing "cosmological constants" at any cost... :)
Jim
[edited by: jdMorgan at 12:19 am (utc) on Jan. 25, 2007]
New question (a little off topic maybe, but maybe not):
The code that jdMorgan wrote above and I modified works wonderfully in Firefox. You flow from http pages to https pages without a hitch. Internet Explorer (IE) 6.x, however is a different story.
For testing, I went to Tools/Internet Options/Advanced and pressed "Reset Default Settings." This is the way I'm betting most people view my pages. Anyway, what happens is that when they hit a secure page you are asked to "click" through a series of message boxes. The first one is normal: "You are about about view information over a secure connection..." fine... click. No biggie. But then, after the main body of the page loads, the images begin loading in (some of which are located in a *.css file, others wich are realtive urls in the page), and IE pops up a box FOR EACH image that says "You are about to leave a secure connection." Click... <less trust>... click... <less trust>... (note that mod_rewrite IS in fact rewriting thing securely. It just doesn't look that way to the user...
And worst of all, after clicking all the boxes, I don't see the padlock bottom right of the browser. Obviously all this is bad for buyer's confidence. So.... any ideas as to this behavior and how to fix it?
"Dear IE, You make my life harder than it needs to be. Can you stop being so mean to me?"
-christian
However, in IE 6.0 things are a bit different. Everytime an image loads it brings with it the message of "you are about to leave a secure connection..."
This makes no sense to me. Why would IE render the security of the page different? Mod_rewrite is still doing it's thing properly (as evidenced in Firefox). Yet IE is saying the page is insecure (no padlock ever appears) even though. And IE doesn't have a menu that allows you to check through what is causing the insecurity, although I can assume (due to the warning messages) it is not rendering the images as https....
I can sticky you the website address if you want to see it for yourself (if you have access to IE 6.0). Let me know.
Also, be sure to completely flush your browser caches before testing any changes to .htaccess or server config files.
In MSIE: Tools->Internet Options->General->Browsing History->Delete->Temporary Internet Files->Delete Files.
In Firefox: Tools->Options->Privacy->Private Data->Clear Now
If your browser has old content or responses cached for any or all of those image URLs, then you'll be seeing those old responses until those cache entries expire or until those entries are overwritten with newer cache entries.
Jim