Category: Programming

I had a Zend project in which we had to use the Apache module mod_rewrite to “toggle” our secure connection depending on what pages the user landed on. The requirements were something like:

User visits any administration or account pages, and SSL is off, switch it on

User visits a page that is not an administration or account page, and SSL is on switch it off

As it was a Zend MVC project, we also had a rewrite rule that routed everything to /index.php in the document root.

This is Zend’s recommended .htaccess rule for routing:

1

2

3

4

5

6

RewriteEngineOn

RewriteCond%{REQUEST_FILENAME}-s[OR]

RewriteCond%{REQUEST_FILENAME}-l[OR]

RewriteCond%{REQUEST_FILENAME}-d

RewriteRule^.*$-[NC,L]

RewriteRule^.*$index.php[NC,L]

For the uninitiated, the block says: If the request is a valid file that exists, or it is a directory, don’t rewrite the URL, otherwise, rewrite the whole string to read “index.php.” The framework will generally just read the query string server variable and go from there.

So on to my additions for toggling SSL.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

#SSL Redirection

RewriteCond%{HTTPS}on

RewriteCond%{REQUEST_URI}!/(account|admin)[NC]

RewriteCond%{HTTP_HOST}^secure\.domain\.com$[NC]

RewriteRule^(.*)$http://domain.com/$1 [R,L]

RewriteCond%{HTTPS}off

RewriteCond%{REQUEST_URI}/(account|admin)[NC]

RewriteCond%{HTTP_HOST}^domain\.com$[NC]

RewriteRule^(.*)$https://secure.domain.com/$1 [R,L]

# Zend routing

RewriteCond%{REQUEST_FILENAME}-s[OR]

RewriteCond%{REQUEST_FILENAME}-l[OR]

RewriteCond%{REQUEST_FILENAME}-d

RewriteRule^.*$-[NC,L]

RewriteRule^.*$index.php[NC,L]

The above code checks the secure protocol, and then checks the requested path (in this case, /admin or /account). In finding that, it toggles the SSL accordingly.

Here’s the problem: because the two toggling rules only work if triggered as a redirect, the server’s request URL is rewritten twice, meaning that any “toggling” will result in a flat redirect to /index.php, breaking the application.

After hours of testing, rewriting, and Google-fu, I came up with this fix:

1

RewriteRule^.*$index.php[NC,L]

becomes:

1

RewriteRule^(.*)$index.php/$1[NC,L]

This means that the request is reattached to the end of the query all the time. This is hidden from the user (i.e. no ugly index.php/admin/login/ or what-have-you), and is successfully passed onto Zend to trigger the appropriate dispatcher. This works for any number of redirects.