Klunky, but Easy
Moving on to the client side, you can't make an HTTPS AJAX call from a page loaded using HTTP. So, how to you safely send a password to the server?
The no-brainer approach is easy make everything HTTPS, including the main page. A slightly better approach is to use a login button to get to an HTTPS login page from an HTTP main page. The HTTPS login page forwards to other HTTPS pages when you login successfully. Even that simple approach has its problems, though.
In the everything-HTTPS approach, you have to make sure that every page is really fetched using HTTPS, even if the user asks for the page by typing http:// in the URL. You do that in Apache with the mod_rewrite extension. You can find full documentation for how to use mod_rewrite at http://httpd.apache.org/docs/current/rewrite/
If mod_rewrite is installed, but not compiled into to Apache itself, check that the following line is not commented out in your httpd.conf file: LoadModule rewrite_module libexec/apache2/mod_rewrite.so (with the path on the right pointing at wherever the .so file is actually found in your file system). Once you've done that, you'll need to edit the .htaccess file in the root directory of your website to hold the following directive.
RewriteEngine on Options +FollowSymLinks Order allow, deny Allow all from all RewriteCond %{SERVER_PORT} !^443$ RewriteRule ^.*$ https://%{SERVER_NAME}%{REQUEST_URI} [L,R,QSA]
You can do the same thing in HTTPd.conf by putting the aforementioned code snippet into a
<Directory "/path/to/your/web/site/*/"> # should match DocumentRoot. Applies to all subdirectories too .... </Directory>
element.
Looking at the contents of the element: The RewriteEngine On does the obvious. The Options +FollowSymlinks turns on support for symbolic links in case you move a file somewhere and put a symbolic link to that new location at the old location. The
Order allow, deny Allow all from all
tells Apache to let everybody access to the whole web-site directory. Next, RewriteCond %{SERVER_PORT} !^443$ tells mod_rewrite that it has to look at all requests that aren't going to port 443. HTTP requests go to port 80, HTTPS request go to port 443, so we can map all non-HTTPS requests. This way, we won't waste time rewriting requests that come in on HTTPS to begin with. Finally, we need to specify how to change the URL:
RewriteRule ^.*$ https://%{SERVER_NAME}%{REQUEST_URI} [L,R,QSA] This rule says: "Map everything (&.*$) to the indicated HTTPS URL, which has the same server name and URI as the original request." The arguments at the end of the line mean:
R | To a real browser-level redirect that changes the URL bar in the browser to show the new address. Doing this will assure that the HTTPS URL is bookmarked rather than the HTTP version. |
L | Stop looking at redirect rules right here (in case we add more of them, later) |
QSA | Append the query string (the ? and everything that follows it in the original URL) to the new URL |
Cache Issues
The only other significant issue in the brute-force deliver-everything-in-HTTPS approach is caching. Many browsers do not cache files that are read using the HTTPS protocol, and the list of browser versions that do cache is constantly changing. Generally (although not always), you can control caching behavior by modifying the HTTP request header in the servlet that delivers your web page. Use the HTTPServletResponse class's setHeader(headerName,value) method for this purpose. (If you're not delivering your page from a servlet, you can ask Apache to customize the response header using mod_headers. Find documentation at http://httpd.apache.org/docs/2.0/mod/mod_headers.html)
A typical response header will contain something like this:
HTTP/1.1 200 OK ... Expires: Fri, 30 Oct 1998 14:19:41 GMT ...
This basic header just suggests that the file should be cleared from the cache on a specific date. The browser (and all the proxies that the page goes through on the way to the browser) may or may not pay attention to that expiration date; the may or may not cache the file at all for that matter (this is a problem in the case of an image delivered by HTTPS, where not caching is is not a great choice).
So, our first improvement is to add Cache-Control: max-age=3600, must-revalidate, which says that the page will be cached, even if it's delivered by HTTPS, but for only 3600 seconds (1 hour). After the specified hour elapses, the page must be reloaded by the server.
Other useful arguments to the Cache-control directive are:
public | Always cache it. |
must-revalidate | The proxy-sever/browser must pay attention to cache rules like max-age. |
no-cache | The server must revalidate (with the originating server) before using the cached version, but is not required to reload the file if it's valid. |
no-store | The file is never cached. |
This discussion is just the tip of the iceberg. For example, there are additional directives that can make the proxy server behave differently from the browser. There's a good article on the subject here.
As a final comment, you'll occasionally read that the Pragma: no-cache directive in the response header will prevent a page from being cached, but that approach is completely unreliable, so don't use it. There's simply no guarantee that anybody (server or browser) will pay attention to any Pragma.


