Multiple domain names mapping to different rails app URLs

Well in my case I need to do two things.

  1. redirect www.foo.com => www.foo.com/properties/1/
    DONE

  2. rewrite the resulting url www.foo.com/properties/1/ => www.foo.com
    NOT DONE YET

I’m clear point 1 should be done in Apache config, as it is.
Are you saying point (2) should be done in Apache or Rails (routes.rb).

Thanks Luis, I’ll refer to them and see if I can find a solution totally
via apache/modrewrite; I suspect I can! I need to get a handle on the
syntax of those rewrite commands, the command chain and regexes, should
be ok. I’ll post back to this thread with the solution when I crack it.

You need to wear many hats in this game at times, not every problem
seems to be solvable in the RoR app itself (or perhaps it is but the
pragmatic approach for a quick win is to ssh to the server and get
funky with with apache config).

You can get www.foo.com/ to display the content of properties/1 by
setting your route for “root” in your routes.rb file. In Rails 2, this
would be

map.root :controller => :properties, :action => :view, :id => 1

Now if you are trying to make property1.foo.com point there as well,
that could be done in Apache.

What it sounded like you wanted was to point www.foo.com to property/1
and www.bar.com to property/2. Am I mistaken at that point?

You can do redirects that fundamentally rewrite the URL from the
server’s perspective, without changing what the user sees in their
browser’s location bar. So you could avoid the whole routes.rb hack
I’ve outlined above by rewriting requests for [www.]foo.com to baz.com/
property/1 [NS,QSA] (those flags keep the URL whatever was written in
the browser. Then the user could enter www.foo.com/edit and the
request would be www.baz.com/property/1/edit at your server.

Walter

Please quote when replying.

bingo bob wrote in post #955467:

Well in my case I need to do two things.

  1. redirect www.foo.com => www.foo.com/properties/1/
    DONE

  2. rewrite the resulting url www.foo.com/properties/1/ => www.foo.com
    NOT DONE YET

I’m clear point 1 should be done in Apache config, as it is.

The way you are describing it here, that should simply be Rails
map.root.

Are you saying point (2) should be done in Apache or Rails (routes.rb).

I don’t understand your description well enough, but I suspect you’re
badly overcomplicating things.

I’ve never tried to do multiple subdomains on one Rails app, but off the
top of my head, here’s what I’d try first:

You might be able to do without the Apache rewrite and simply dispatch
on request.host , but I don’t know if that would work.

Best,

Marnen Laibow-Koser
http://www.marnen.org
[email protected]

Did you try using the flags Walter suggested, [NS,QSA]? Can you post
your Apache rewrite rules?

Hopefully we’re not going too off topic with Apache :slight_smile:

Could well be overcomplicating things!

Walter’s point here is sounds closest I think - I’ll try it.

You can do redirects that fundamentally rewrite the URL from the
server’s perspective, without changing what the user sees in their
browser’s location bar. So you could avoid the whole routes.rb hack
I’ve outlined above by rewriting requests for [www.]foo.com to baz.com/
property/1 [NS,QSA] (those flags keep the URL whatever was written in
the browser. Then the user could enter www.foo.com/edit and the
request would be www.baz.com/property/1/edit at your server.

As it stands apache modrewrite in place but not finished - working
though, :-)!

http://www.chaletcordee.co.uk/
as you will witness if you visit this is rewritten to this in the users
browser…
http://www.chaletcordee.co.uk/properties/1/
Fine, content works but I’d like this to be
http://www.chaletcordee.co.uk/
in the users browser

in a similar manner

http://www.apartmentnicole.co.uk/
goes to
http://www.apartmentnicole.co.uk/properties/2/
I’d like this to be
http://www.apartmentnicole.co.uk/
in the users browser

if you click on the photos page after visiting
http://www.chaletcordee.co.uk/
you get here…
http://www.chaletcordee.co.uk/properties/1/photos
I’d like this to be
http://www.chaletcordee.co.uk/photos
in the users browser, etc

phew - is anyone still reading - I doubt it ! :-).

Luis S. wrote in post #955491:

Did you try using the flags Walter suggested, [NS,QSA]? Can you post
your Apache rewrite rules?

Hopefully we’re not going too off topic with Apache :slight_smile:

Sorry for delay, currently running with this

<VirtualHost *:80>

ServerName chaletcordee.co.uk
ServerAlias www.chaletcordee.co.uk

DocumentRoot /home/rupert/public_html/bookings/public

Options +FollowSymLinks
RewriteEngine on
RewriteRule ^/$ /properties/1/$1 [R]

<VirtualHost *:80>

ServerName apartmentnicole.co.uk
ServerAlias www.apartmentnicole.co.uk

DocumentRoot /home/rupert/public_html/bookings/public

Options +FollowSymLinks
RewriteEngine on
RewriteRule ^/$ /properties/2/$1 [R]

Will try [NS,QSA] but from what walter said I think I might need the
rewriterule to be different as well, more than just adding those flags?

Been trying rewrite stuff all week but can’t get to where I want to be.
If anyone has an answer or another way - v keen to hear.

I did wonder about this…http://github.com/shuber/proxy or my routes.rb
file - or more apache tinkering…

this didn’t get me there either (lots of repeated stuff here)

any tips - I’m not certain that I can achieve this with modrewrite.

Do try it, that’s the secret sauce that lets the URL remain masked.
The R flag you have there currently is doing a full external redirect
to a subfolder, but I don’t think you need that.

Walter

I have built something along these lines and here is how it might work
for you.

Have a domain model that maps a domain name to a property_id(decide
whether you are going to use www or no-www (http://no-www.org/) or
both if you want but it it is better to choose one or the other for
SEO and analytics.

so in your application_controller.rb put in some code that effectively
does a lookup on the domain model to find your property.

before_filter :domain_lookup

private

def domain_lookup
@domain = Domain.find_by_host(request.host)
raise DomainNotFound unless @domain # should always find it as
you defined it right?
raise DomainUnavailable unless @domain.live? # the on button for
the domain
raise DomainMaintenance if @domain.maintenance? # if you need to
pull it down
@host = @domain.host + request.port_string # I need this when
building some redirects as my setup is more complicated, SSL etc.
end

Now you have @domain.property available in your application wherever
you need it assuming you set up a belongs_to association. Now you can
refer to something like @domain.property.photos in your application.
I use @domain.css to pick up which of my CSS’s I want to use with that
domain. You can have as much as you like in there such as maintenance
flags etc.

Next decide what your root path is going to be and set map.root
accordingly. You may find there is no need to go through the property
controller at all for the end-user since the whole domain is about a
single property and you define that outside in your seed data or admin
console.

Now for the virtual host config in Apache.

<VirtualHost :80>
# define any of you domains as the servername, it
doesn’t matter which one you choose to be the main one (perhaps your
admin domain?)
ServerName property1.com
# list all of the other domains that run against the
same rails application remembering to pick up the www’s too and
wildcards work too!
ServerAlias
skichalets
.com www.skichalets*.com
ahouse.com www.ahouse.com
myshed.com www.myshed.com
www.property1.com

            # then I rip off the www. using this rule

RewriteEngine On
RewriteCond %{HTTP_HOST} ^www\.(.*)$ [NC]
RewriteRule ^(.*)$ http://%1 [R=301,L]

RailsEnv production
      RailsBaseURI /

    DocumentRoot /srv/www/vhosts/yourapp/current/public

Warning if you are using SSL, you can only have one SSL host per IP
address. Basically the host name is not available to apache until it
has been decrypted by the host certificate, by which time it is too
late! I work around this by passing the property_id to the ssl
redirects and then use this to look up the domain. Just be careful
about rebuilding and building sessions.

So the logical flow goes like this:

user puts a URL into browser
apache picks up the host name and checks to see if it is the list of
hosts
rips the www off the host name if you want it to
send the request to be routes.rb in /srv/www/vhosts/yourapp/current/
config
your application_controlller.rb looks up the domain from request.host

HTH

O.

or to put it another way…

what does your routes.rb look like to get the root urls right for a
property (in my case properties anyway). also, totally happy to bypass
properties controller for the public views, just figured I’d done it the
rails way with a properties controller and separate actions for book /
prices / full_details etc.

Right - once more unto the breach and all that! Thanks for this, I see
what you mean. I’ve part implemented, the bit I don’t quite get relates
to finding the relevant property and what I put in the routes file to
hook it up (hooking up the default root to a domain/property).

The bit you describe here…

“Next decide what your root path is going to be and set map.root
accordingly. You may find there is no need to go through the property
controller at all for the end-user since the whole domain is about a
single property and you define that outside in your seed data or admin
console.”

My complicated (perhaps over) routes.rb is like this at present.

http://pastie.org/1230080

Here’s what I have just done in the last hour or so all good as far as
it goes I think !

migrated a new Domain model
property belongs to domain
domain has one property
(all hooked up and playing nicely)

App controller…

before_filter :domain_lookup

private
def domain_lookup
@domain = Domain.find_by_host(request.host)
# raises errors when necessary
end

bit of my header file for testing…

Request host is :
<%= request.host %>

Set domain in app controller is : <%= @domain.host %>

Property that belongs to this domain is : <%= @domain.property.name %>


as you can se this test works…the find works nicely…

http://chaletcordee.co.uk/properties/1
http://apartmentnicole.co.uk/properties/2/

apache side…just as you suggested, understood nice and simple (I like
the www striping! no-www is interesting)

<VirtualHost *:80>

ServerName chaletcordee.co.uk
ServerAlias www.chaletcordee.co.uk apartmentnicole.co.uk
www.apartmentnicole.co.uk

then I rip off the www. using this rule

RewriteEngine On
RewriteCond %{HTTP_HOST} ^www.(.)$ [NC]
RewriteRule ^(.
)$ http://%1 [R=301,L]

RailsEnv production
RailsBaseURI /

DocumentRoot /home/rupert/public_html/bookings/public

Stuck on next step!

Owain wrote in post #956838:

I have built something along these lines and here is how it might work
for you.

Have a domain model that maps a domain name to a property_id(decide
whether you are going to use www or no-www (http://no-www.org/) or
both if you want but it it is better to choose one or the other for
SEO and analytics.

so in your application_controller.rb put in some code that effectively
does a lookup on the domain model to find your property.

before_filter :domain_lookup

private

def domain_lookup
@domain = Domain.find_by_host(request.host)
raise DomainNotFound unless @domain # should always find it as
you defined it right?
raise DomainUnavailable unless @domain.live? # the on button for
the domain
raise DomainMaintenance if @domain.maintenance? # if you need to
pull it down
@host = @domain.host + request.port_string # I need this when
building some redirects as my setup is more complicated, SSL etc.
end

Now you have @domain.property available in your application wherever
you need it assuming you set up a belongs_to association. Now you can
refer to something like @domain.property.photos in your application.
I use @domain.css to pick up which of my CSS’s I want to use with that
domain. You can have as much as you like in there such as maintenance
flags etc.

Next decide what your root path is going to be and set map.root
accordingly. You may find there is no need to go through the property
controller at all for the end-user since the whole domain is about a
single property and you define that outside in your seed data or admin
console.

Now for the virtual host config in Apache.

<VirtualHost :80>
# define any of you domains as the servername, it
doesn’t matter which one you choose to be the main one (perhaps your
admin domain?)
ServerName property1.com
# list all of the other domains that run against the
same rails application remembering to pick up the www’s too and
wildcards work too!
ServerAlias
skichalets
.com www.skichalets*.com
ahouse.com www.ahouse.com
myshed.com www.myshed.com
www.property1.com

            # then I rip off the www. using this rule

RewriteEngine On
RewriteCond %{HTTP_HOST} ^www\.(.*)$ [NC]
RewriteRule ^(.*)$ http://%1 [R=301,L]

RailsEnv production
      RailsBaseURI /

    DocumentRoot /srv/www/vhosts/yourapp/current/public

Warning if you are using SSL, you can only have one SSL host per IP
address. Basically the host name is not available to apache until it
has been decrypted by the host certificate, by which time it is too
late! I work around this by passing the property_id to the ssl
redirects and then use this to look up the domain. Just be careful
about rebuilding and building sessions.

So the logical flow goes like this:

user puts a URL into browser
apache picks up the host name and checks to see if it is the list of
hosts
rips the www off the host name if you want it to
send the request to be routes.rb in /srv/www/vhosts/yourapp/current/
config
your application_controlller.rb looks up the domain from request.host

HTH

O.

Now I’m firmly back in the rails world here, any kind soul give me a
pointer as to how I might finish this one. I figure I need to modify the
routes file and perhaps conditionally modify the rails app home root
based on domain - what else?

Multiple apps is definitely not what I wish to do here.
It’s a single app.

I think you should run different rail apps on different ports and use
Nginx
to proxy to them. That should be simple enough.


Thanks & Regards,
Dhruva S…

A quick look and the following points:

  • you are not scoping your property_id to the domain. If you go to
    http://chaletcordee.co.uk/properties/1 you can change to properties 2
    in the domain controller and you pick up the other property. Your
    application controller should set an instance variable for the request
    @property which you should always use as the parent of all subsequent
    request in a before filter. I think you said you implemented this.

App controller…
before_filter :domain_lookup
private
def domain_lookup
@domain = Domain.find_by_host(request.host)
# raises errors when necessary
end

  • apache is doing some redirects here. You shouldn’t need any other
    than the no-www stuff. Take them out. Let rails do the routing. You
    only really need mod_rewrite if you are doing URL rewriting which I
    don’t think you need.

  • your map.root invokes your welcome controller. This should work if
    the welcome controller index action shows the details of the property
    you have sitting in your instance variable @property

Sounds like you are almost there.

O.

Oh well if that is the case then you can probably check the current
domain /
request.url in a before_filter inside application_controller and work
accordingly thereafter, that should be simple enough too…


Thanks & Regards,
Dhruva S…

Inline - I won’t have a chance to test this out until a little later but
yes - thanks for this, think I’m getting it.

Owain wrote in post #957124:

A quick look and the following points:

  • you are not scoping your property_id to the domain. If you go to
    http://chaletcordee.co.uk/properties/1 you can change to properties 2
    in the domain controller and you pick up the other property. Your
    application controller should set an instance variable for the request
    @property which you should always use as the parent of all subsequent
    request in a before filter. I think you said you implemented this.

App controller…
before_filter :domain_lookup
private
def domain_lookup
@domain = Domain.find_by_host(request.host)
# raises errors when necessary
end

So if I’ve understood you correctly, I’d do something like
this…something like I mean - just to see if this is on the right
lines?

App controller…
before_filter :domain_lookup
private
def domain_lookup
@domain = Domain.find_by_host(request.host)
# raises errors when necessary

New step find the property and set the instance variable,

do something else sensible if there’s a problem

if @domain
# guess the line below is the key
# set a property instance via the domain name
@property = @domain.property
else
# do stuff if there’s no domain…
end

properties controller

at present I find properties by ID - but with this new method I wouldn’t
need to do that, it’s already done in the app controller. should be
easy.

routes

this is where I possibly need most advice.
http://pastie.org/1230080

at present these lines…

map.photos ‘properties/:id/photos’, :controller => ‘properties’, :action
=> ‘photos’
map.contacts ‘properties/:id/contacts’, :controller => ‘properties’,
:action => ‘contacts’

get me to the various pages, but I do not want the /properties/id bit in
the URL, how do I redo this routing without /properties/id showing…

is it simply?

map.contacts ‘/contacts’, :controller => ‘properties’, :action =>
‘contacts’

  • apache is doing some redirects here. You shouldn’t need any other
    than the no-www stuff. Take them out. Let rails do the routing. You
    only really need mod_rewrite if you are doing URL rewriting which I
    don’t think you need.

OK - I thought I just had the no-www stuff in there - but ok.

  • your map.root invokes your welcome controller. This should work if
    the welcome controller index action shows the details of the property
    you have sitting in your instance variable @property

I’ll have to think of a sensible place to point map.root, one this is a
front page for the app which isn’t a particular property at all - or I
might send it to one of the properties - haven’t registered a domain
name for it yet.

Sounds like you are almost there.

I think so ! :-).

O.

This might be a good time to mention that with the rails 3 redesign,
you can specify much more powerful constraints from within the router.
Katz’s website has a nice article on it:

The part I found most interesting was this:

Basecamp::Application.routes do
constraints(:subdomain => “support”) do
match “/foo/bar”, :to => “foo#bar”
end
end

As it turns out, you can swap :hostname (or :host) into the place
of :subdomain and setup different routes for different domains.
Using this in combination with some sort of hostname checking in a
before_filter is all I needed for my project. No need for any URL
rewriting in nginx.

Hope that helps.