For many reasons, including SEO, public-facing websites should only be
available at a single domain. Many hosting providers (especially PaaS-like
platforms such as Heroku and Fly.io) make apps available at a default URL. For
example, Heroku apps are available at my-app-name.herokuapp.com
. If those
default URLs make it out into the wild, search engines might index them,
resulting in indexed duplicated content. Historically, search engines have
punished websites that serve duplicated content.
At Super Good, we host many Solidus apps on Heroku. Due to the nature of eCommerce, SEO is critically important to us, so we need to avoid this issue.
The ideal solution if you find yourself in this situation (or just want to
avoid it) is to make your production app serve permanent redirects to the
correct domain. If someone visits
https://my-app-name.herokuapp.com/some/page
, then they should be served a 301
redirect (“Moved Permanently”) to https://www.my-app-name.com/some/page
, or
whatever the desired domain happens to be.
Firstly, if you’re using a web server like NGINX as a reverse proxy in front of your application, you should use that to do the redirect and avoid load on your web application. The following options are for situations where you can’t do that because of the particulars of your hosting solution.
One option is rack-host-redirect. It’s designed for this use case, but at the time of this writing hasn’t been touched in six years. I was hesitant to add a seemingly unmaintained dependency to my app, but it’s still worth considering. There is very little code in it.
An option that doesn’t require adding a new dependency is to use built-in Rails
routing features. This is a similar to aproach to what you’d
do if you wanted to redirect your
www
traffic to a naked domain or vice versa.
Create a catch-all route that’s constrained by domain. Let’s say your app has
two Heroku deployments, my-app-staging
and my-app-production
. You want the
app to only be accessible at staging.my-app.com
and www.my-app.com
, and not
my-app-staging.herokuapp.com
and my-app-production.herokuapp.com
. You could
do something like this:
if Rails.env.production? || Rails.env.staging?
match(
"(*any)",
to: redirect(
domain: "my-app.com",
subdomain: Rails.env.production? ? "www" : "staging"
),
via: :all,
constraints: {domain: "herokuapp.com"}
)
end
The result will be that in your production and staging environments, any
requests to the herokuapp.com
domain (regardless of sub-domain) will be
served 301 redirects to correct domain.