My team is working on a multilingual multinational application in Rails (I know this sounds incredibly pushy but bear with me). The application is at susuh.de if you want to have a look at it, but it’s still alpha version.
In practice, this multi-multi-blah-blah means that we want to use both various national domains (like susuh.at, susuh.pl — those links don’t function yet, they are only examples) and various subdomains (like pl.susuh.de or en.susuh.pl). The national domains will filter content only to respective country (so susuh.de will only have content from Germany) and the subdomains will be used to change the default language for the national domain (so if you are an English-speaking person living in Germany, en.susuh.de is your place to go). The subdomain will also be important in multilingual countries like Switzerland.
Trying to implement this scheme we’ve run into problems with cookie domain handling in Rails. And cookie problems lead to session problems for users, like being erroneously logged out or loosing other settings.
Mandatory Cookie-monster reference (you wouldn’t believe how many hits from Google this provides).
It seems that there are two official (normal) ways of handling this — at least that’s what Google says. Either you set your default session options in environment.rb
like this:
ActionController::CgiRequest::DEFAULT_SESSION_OPTIONS[:session_domain] = '.susuh.de'
Or you don’t set it at all.
The former lets you set cookies for any subdomain of susuh.de, like pl.susuh.de, but no other domain. The latter sets cookie independently for each subdomain, causing different cookies for pl.susuh.de, en.susuh.de and susuh.pl. Neither is what we really want.
So tell me what you want, what you really really want?
What we really want is to have the same cookie for any subdomain of susuh.eu and a different one for any subdomain of susuh.pl and so on. Nota bene, I’m not talking about a Single Sign-On problem, this is on much more basic level. We tried experimenting with setting DEFAULT_SESSION_OPTIONS
“on the fly” in before_filter
, but found out that this is too late (basically, this sets cookie domain for next request).
Fortunately, all is not lost. If all else fails, start reading code. And that’s what I did. The “entry point” for Rails is defined in dispatcher.rb
file. Glancing over the code, I noticed something new for me: a before_dispatch
callback. Google agreed that this may be a way to go, giving us link to http://m.onkey.org/2007/10/16/dispatcher-callbacks, among others.
Here’s our solution:
require 'dispatcher' module ActionController class Dispatcher def set_session_domain ApplicationController.session_options.update( :session_domain => "#{@request.host.gsub(/^[^.]*/, '')}" ) end before_dispatch :set_session_domain end end
January 21st, 2008 at 17:00:31
Very cool, thanks. Any advice on supporting multiple languages (globalization) in a single Rails application would be greatly appreciated too. I know that it used to be a PITA to support multiple languages (keep all copy in the database in each language). Has it gotten any better?
January 21st, 2008 at 17:34:58
rdempsey: We use GetText. It stores its translations in special files. It’s not that bad. At least most of the translation work can be done without assistance of programmers.
I think that maintaining several language version of any application is quite a lot work. There are several plugins of this type but since I haven’t tried any other of them (except GetText) I can’t tell if they are much better or worse.
January 31st, 2008 at 19:56:14
We’ve used GetText in the past but hell, it feels do ’97. Try Gibberish. Much simpler, No compilation, no .po, .mo madness.
April 29th, 2008 at 16:42:24
This now fails in Edge Rails with:
uninitialized constant ActionController::Dispatcher::ApplicationController
not sure why
June 11th, 2008 at 04:03:51
Hey guys.
If anybody is having trouble with rails 2.0.2, I’ve come up with a working hack(and also changed some cosmetic stuff)
July 16th, 2008 at 14:39:57
Great post – as well as the internationalization bit, this is helpful for anyone who uses subdomains but deploys to different top-level domains for development, testing, and production.
One important fact (explained in the linked “localhost & cookies” post above) is that this won’t work for localhost – in fact, if you try, your session won’t be saved at all, and you’ll have InvalidAuthenticityToken problems if you use authenticity checking.
Also, I think it’s always better to extend classes rather than reopen them – so, with those two things in mind, here’s a slight refactor (works fine in Rails 2.1):
July 16th, 2008 at 20:29:40
This is an interesting technique, I wish I had seen this earlier.
I tried to do a similar thing by hacking Rails session code so that the session domain could be specified with a Proc in the controller. It worked for sending cookies, but I was having trouble reconstituting them.
For developing on localhost with subdomains, I also ran into the invalid authenticity token problems, which we just worked around by creating an alias in /etc/hosts and accessing the site from there, with the session domain configured appropriately of course.
July 17th, 2008 at 08:32:17
[…] Cookie handling in multi-domain applications in Ruby on Rails « require ‘brain’ Cookie handling in multi-domain applications in Ruby on Rails (tags: cookies development programming rails ror ruby scalability subdomain rubyonrails sessions cookie) […]
October 13th, 2008 at 15:37:51
[…] out throughout your site (regardless of subdomains), you could use a small amendement as in this link. However, that may not be what you need – sometimes you want to keep the worlds separated, so […]
February 16th, 2009 at 00:03:40
Adjusted to work with Rails 2.3. Hopefully this will save someone a 30 minute code dive.
February 16th, 2009 at 00:05:46
^ Minus the
puts "DOMAIN: #{domain}"
line of course.February 16th, 2009 at 10:16:47
Brendan: thanks, much appreciated.
March 23rd, 2009 at 21:23:06
None of the above works with the version of Rails that you use? See http://rubista.info/post/88299744/varios-dominios-e-uma-unica-aplicacao-rails-com for more examples (no, I don’t understand this article either :)
March 23rd, 2009 at 21:47:08
If anyone stumbles upon this article, I’ve updated this code to work with Rails 2.3.2. See: http://gist.github.com/64874
April 1st, 2009 at 00:18:21
thanks for updating your code for 2.3.2 , brendan
April 17th, 2009 at 15:42:55
If your using Rails 2.3 or any other framework that utilizes Rack, this could be useful:
http://codetunes.com/2009/04/17/dynamic-cookie-domains-with-racks-middleware/
February 24th, 2010 at 20:41:23
[…] you want to change your session cookie’s path globally, there’s a documented setting. But what if you want to set some one-off cookie to be from something other than your […]
March 20th, 2010 at 10:38:06
The proposed method with @env and class variables will not work for threaded applications. Currently that may be not a problem, as 99.9% applications on Rails are deployed as single-thread model. But soon that may change, and this code will generate hard to debug errors due to thread unsafety
March 13th, 2012 at 12:12:36
How about doing this? In `config/environments/production.rb`:
config.session_store :cookie_store, { :key => ‘_my_app_session’, :domain => “.myapp.com” }