Michael Trojanek (relativkreativ) — Bootstrapper and creator of things

This article was published on November 3rd 2015 and takes about 4 minutes to read.

Use it with caution — it is probably still valid, but it has not been updated for over a year.

How to set cookies across different top level domains

Setting cookies for a domain different from the one the visitor is currently on is actually not possible. Learn how to do it nonetheless.

Identifying existing customers is the key to improving your visitor's experience when visiting your websites — you would not want to prompt existing customers to join your email list or invite them to buy your latest product when they already did.

While it is quite easy to identify (and remember) an existing customer if you only run a single website, things get trickier if you have multiple websites. You cannot set cookies for a domain different from the one that is currently visited (we are not talking about subdomains here, but toplevel domains like domain1.com and domain2.com) — this is against a cookie's nature.

This does not mean that it is impossible, it just involves a little more work.

Let me show you how I do it:

Identifying an existing customer

For every visitor who joins my email list, I generate a unique token and store it in my main application's database (along some important information like when and where they joined). Let's call this token, well, token.

I append ?token=abc to every link to one of my websites I include in a message to my email list (where abc corresponds to each subscriber's personal token). How you manage that is up to you as it is dependent on the mail service provider you are using.

Once you have generated such a link, all you need to identify an existing customer is a before_action on every action of your application that could possibly be the target of such a link. You probably want to define it on ApplicationController and skip it with skip_before_action eventually:

class ApplicationController < ActionController::Base
  before_action :set_cookie

  private

  def set_cookie
    cookies.signed.permanent[:token] = params[:token] if params[:token].present?
  end
end

That's the easy part. When the requested URL includes a query string with a token, this token is written to a permanent signed cookie.

Now you can use another before_action to set a @current_customer variable for example.

In order to also set this cookie on another website, you have no other choice but to redirect the visitor back and forth.

A method similar to the following has to be called as before_action after the one that sets the cookie:

def spread_cookie
  return if params[:token].blank?

  redirect_to URI.decode(params[:origin]) and return if params[:origin].present?

  destination = case request.host.split(".")[-2]
                  when "domain1"
                    "http://www.domain2.com"
                  when "domain2"
                    "http://www.domain1.com"
                end
  origin = URI.encode(request.original_url.split("?").first)

  redirect_to "#{destination}?token=#{params[:token]}&origin=#{origin}" and return
end

When the origin parameter is not set (which it is not initially), this method redirects to your other website (when visiting domain1.com, it redirects to domain2.com and vice versa) and appends an origin parameter which holds the original URL without its query string.

When the origin parameter is set (which it is on the website where the visitor is redirected to), the method redirects back to origin (the original URL without the query string), thus hiding the token form the browser's address bar.

Caveats

Testing this stuff is quite tedious. I recommend you use pow to run your applications locally — this makes it a lot easier than with applications running on different ports of localhost.

Cookies can be observed easily using Chrome's developer tools.

You'll also want to keep these things in mind:

  • The above code needs to be identical on all participating websites. In order to keep things DRY, you'll want to make it a Rails concern, wrap it in a Ruby gem and include it in everyone of your applications.
  • This approach only works with two websites. If your cookie-roundtrip includes more than 2 websites, you need to refine the redirection logic.
  • It is really the user's browser which needs to be redirected in order for this to work. Do not try to be clever and call the second website in a separate thread with HTTParty (or a similar library) — the cookie would not get set on the user's browser but on your library's instance.
  • Prepare yourself for a timeout when redirecting to a website hosted on a different server — breaking the redirect chain with an error will certainly leave your visitors confused.
  • This sample code includes no error checking — you probably want to check if a user with a given token exists in the database before setting the cookie in the first place.

Get in the loop

Join my email list to get new articles delivered straight to your inbox and discounts on my products.

No spam — guaranteed.

You can unsubscribe at any time.

Got it, thanks a lot!

Please check your emails for the confirmation request I just sent you. Once you clicked the link therein, you will no longer see these signup forms.