I have a 2.2.2 ROR website package that I sell to my clients. After
deploying a few instances, I realized that the only difference
between the accounts was the client’s URL and the database. It seemed
wasteful to have many copies of the same software on my web server,
not to mention a separate mongrel cluster for each client, most of
which were running at very low load levels.
So I had the idea of combining all these separate websites into one
‘shared’ system. Having no idea how to do that, I had to wing it.
Here’s what I’m using:
- Apache as the front end server, running mod_proxy
- A single mongrel cluster (4 mongrels)
- A single instance of my Rails application
I had each Apache virtual host configuration insert a unique
client_id header in all requests, like so:
RequestHeader set X_CLIENT_ID ‘7563TY7732UUW9’ # a unique id for
each domain
The Rails app then reads this header and knows which client database
to use.
Thus, a request comes in for the url example1.com, Apache adds the
unique X_CLIENT_ID header for that URL, the request gets forwarded
through Mongrel, Rails reads the client_id, connects to the correct
database, and then renders the page as it would if this were not a
multi-domain system.
This all works very well except for one serious problem: mongrel
seems to mess up cookie handling in a multi-domain situation.
The problem is illustrated in the log snippet at the bottom of this
email:
The 1st request is from User 1, already logged in and working on
example1.com.
The 2nd request is from User 2, for the example2.com home page.
The 3rd request is from User 1, for another page in the application.
The HTTP request included the original cookie, but note that the
cookie value, the session id that Rails sees, has changed!
The 4th request shows User 1 being redirected to the login page,
because to Rails it looks like User 1 is a new, un-authenticated
user, who does not have the rights to access that page.
If no one ever accesses example2.com, then the users of example1.com
have no problem. That is, their cookies are not changed. However, as
soon as anyone hits example2.com, anyone subsequently hitting
example1.com will have their session_id cookie changed. The same is
true in the other direction… hits on example1 also change the
cookies of users on example2.
I can solve the problem by not sharing mongrels. That is, by keeping
everything else the same, but dedicating one or more mongrel
instances to each URL. As soon as mongrel stops handling multiple
URLs, the cookie problem vanishes.
However, this gets me back to having one or more mongrels for each
domain, which seems wasteful. A single mongrel cluster of 4 or 6 can
easily handle the load, if it weren’t for this cookie problem.
Can anyone explain this problem to me? I don’t actually understand
why mongrel doesn’t just pass the cookie in the request straight
through to Rails. Why does it change it?
And can anyone suggest a work-around?
Or perhaps I’ve got the wrong end of the stick, and it’s not
Mongrel’s fault? Or maybe I should take an entirely different
approach to this multi-domain problem? As I said, this is straight
out of my head. I have not been able to google any other approach to
what I’m trying to do here.
Any help, much appreciated. Again, this is a fully upgraded Rails
2.2.2 system.
– John
**1st Request from USER 1 for example1.com**
Processing Admin::CmsController#index (for 75.127.142.66 at
2009-01-27 13:15:27) [GET]
Session ID: 00b9cfb6fd397e5c9934ea58eaef648d
>>> Request for client 90873721, EXAMPLE1.COM
Rendering template within layouts/admin/standard
Rendering admin/cms/list
Completed in 114ms (View: 14, DB: 81) | 200 OK [https://
example1.com/admin/cms]
**2nd Request from User 2 for example2.com**
Processing CmsController#cms_show (for 64.1.215.163 at
2009-01-27 13:16:15) [GET]
Session ID: 4fed1c59001f7484a63fb6280376825a
Parameters: {“alias”=>“home.html”}
>>> Request for client 48218343, EXAMPLE2.COM
### alias: home.html
Rendering template within layouts/two-column
Rendering cms/cms_show
Completed in 23ms (View: 13, DB: 3) | 200 OK [http://
example2.com/]
**3rd Request from User 1 for example1.com -- note session ID
changes!!!**
Processing Admin::CmsController#index (for 75.127.142.66 at
2009-01-27 13:16:18) [GET]
Session ID: 85c178aa70ed2bef6a767e844bf6c6d6
>>> Request for client 90873721, EXAMPLE1.COM
####### ‘admin/cms’, ‘index’
Redirected to actionsignincontroller/admin/user
Filter chain halted as [:check_authentication]
rendered_or_redirected.
Completed in 4ms | 302 Found [https://example1.com/admin/cms]
**4th request -- redirected from 3rd request**
Processing Admin::UserController#signin (for 75.127.142.66
at 2009-01-27 13:16:18) [GET]
Session ID: 85c178aa70ed2bef6a767e844bf6c6d6
>>> Request for client 90873721, EXAMPLE1.COM
Rendering template within layouts/admin/standard
Rendering admin/user/signin
Completed in 10ms (View: 6, DB: 0) | 200 OK [https://
example1.com/admin/user/signin]