Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

Rails applications is not working in sub-path #8941

Closed
sobrinho opened this Issue · 19 comments

7 participants

@sobrinho

Hi,

I'm trying to deploy my rails application work in a sub-path but it is not working.

Some places says to use RAILS_RELATIVE_URL_ROOT environment variable but it seems to be ignored.

I started the server:

λ RAILS_RELATIVE_URL_ROOT='/relative' bundle exec rails server puma -p 9292

And checked the routes with curl:

λ curl -s localhost:9292/relative/people | grep 'No route matches' || echo 'ok!'
<p><pre>No route matches [GET] &quot;/relative/people&quot;</pre></p>

λ curl -s localhost:9292/people | grep 'No route matches' || echo 'ok!'
ok!

Then I found other posts saying to scope the entire routes.rb file like that:

Myapp::Application.routes.draw do
  scope ENV['RAILS_RELATIVE_URL_ROOT'] do
    ...
  end
end

Then the routes starts to work:

λ curl -s localhost:9292/people | grep 'No route matches' || echo 'ok!'
<p><pre>No route matches [GET] &quot;/people&quot;</pre></p>

λ curl -s localhost:9292/relative/people | grep 'No route matches' || echo 'ok!'
ok!

But the devise redirect goes wrong (maybe it's devise/warden fault, I didn't checked yet):

curl -s localhost:9292/relative/people 
<html><body>You are being <a href="http://localhost:9292/relative/relative/users/sign_in">redirected</a>.</body></html>

At this point I can access on browser to workaround this redirect issue but the assets do not work anymore since it is:

<link href="/relative/assets/application.css" media="screen" rel="stylesheet" type="text/css" />

And the sprockets route is mounted on /assets instead of /relative/assets:

λ curl -s localhost:9292/relative/assets/application.css | grep 'No route matches' || echo 'ok!'
<p><pre>No route matches [GET] &quot;/relative/assets/application.css&quot;</pre></p>

λ curl -s localhost:9292/assets/application.css | grep 'No route matches' || echo 'ok!'
ok!

Another posts says to change the config.ru like that:

map ENV['RAILS_RELATIVE_URL_ROOT'] do
  run Myapp::Application
end

But the redirect is still wrong:

curl -s localhost:9292/relative/relative/people 
<html><body>You are being <a href="http://localhost:9292/relative/relative/users/sign_in">redirected</a>.</body></html>

I can workaround that route issue again on browser and authenticate and the assets is served.

But at this point, calling image_path inside a css+erb stylesheet returns a wrong value:

image_path 'logo.png'

Returns:

/assets/logo.png

Since the sprockets is serving on /relative/assets, it won't work.

But calling image_path on rails views returns the right value:

/relative/assets/logo.png

At this point, I removed the scope from routes and kept the map on config.ru.

Then devise redirect worked:

λ curl -s localhost:9292/relative/people 
<html><body>You are being <a href="http://localhost:9292/relative/users/sign_in">redirected</a>.</body></html>

The stylesheet route was generated right:

<link href="/relative/assets/application.css" media="screen" rel="stylesheet" type="text/css">

But the image_path inside the stylesheet is still using /assets/logo.png instead of /relative/assets/logo.png.

I found another place saying to use config.assets.prefix like that:

config.assets.prefix = '/relative'

But at this point, the sprockets route changed from /relative/assets/application.css to /relative/relative/assets/application.css and the image_path is still using /assets/logo.png.

That do not make any sense for me because RAILS_RELATIVE_URL_ROOT is set, the run Myapp::Application is inside a map using the same value and config.assets.prefix have this same value.

#2977 and #3365 seems to fix this issue so I think it's a regression.

@alup

For sure adding a prefix to router base url affects lot of stuff. So it is very possible many things to be broken. Maybe, you could by pass this problem by using a proxy (e.g. nginx) with a rewrite rule mapping urls beginning with /relative to /,

@sobrinho

In fact, I'm making a reverse proxy with nginx that will handle a couple applications.

example.com -> reverse proxy

example.com/ -> one application
example.com/app1 -> another application
example.com/app2 -> another application
example.com/app3 -> another application

The assets must be under the sub-path because the root path is a completely different application.

Note that each application has your own server, the reverse proxy does not contain anything beyond the nginx itself.

@alup

Take a look at http://wiki.nginx.org/HttpRewriteModule. Anyway, this is not a Rails issue. You could seek for some help to the nginx irc channel.

@sobrinho

@alup that's not a related to nginx, I'm running on development environment using puma ;)

@alup

Ok, so the problem here is that:
1. rails router does not respect RAILS_RELATIVE_URL_ROOT for each rule.
2. rails engines should be aware of the relative url.
3. asset path should also be aware of the relative url.

I am not the expert to talk about it. With a quick search I can see that rails uses an instance variable relative_url_root to keep track of the value of the env var. So, you can search through the project to determine if this is used as it should :p

@sobrinho

For some bizarre reason, the development environment is working now.

Anyway, the production environment on my local machine (running puma) still have the problem and I think if found the root cause.

Using config.assets.initialize_on_precompile = false, the assets aren't compiled using the relative url path:

λ rm -rf public/assets

λ RAILS_RELATIVE_URL_ROOT='/relative' rake assets:precompile
/Users/sobrinho/.rbenv/versions/1.9.3-p327/bin/ruby /Users/sobrinho/Developer/nohup/relative/vendor/bundle/ruby/1.9.1/bin/rake assets:precompile:all RAILS_ENV=production RAILS_GROUPS=assets

λ ack logo public/assets/application-*.css
  background: url(/assets/logo-be21df5126551bf7d0514d35dc7ba211.png) center center no-repeat;

Using config.assets.initialize_on_precompile = true, the assets are compiled using the relative url path:

λ rm -rf public/assets

λ RAILS_RELATIVE_URL_ROOT='/relative' rake assets:precompile
/Users/sobrinho/.rbenv/versions/1.9.3-p327/bin/ruby /Users/sobrinho/Developer/nohup/relative/vendor/bundle/ruby/1.9.1/bin/rake assets:precompile:all RAILS_ENV=production RAILS_GROUPS=assets

λ ack logo public/assets/application-*.css
  background: url(/relative/assets/logo-be21df5126551bf7d0514d35dc7ba211.png) center center no-repeat;

Note that config.assets.initialize_on_precompile = false is required to application compile the assets on heroku, see https://devcenter.heroku.com/articles/rails3x-asset-pipeline-cedar

@schneems
Collaborator

@sobrinho your environment variables aren't present during asset compilation on Heroku, so even if you set RAILS_RELATIVE_URL_ROOT, it won't do anything (on Heroku) unless you have user-env-compile turned on (not recommended).

@sobrinho

@schneems I'm using the user-env-compile labs on my applications and it's working fine.

Any reason to not be recommended?

@steveklabnik
Collaborator

Since this is a heroku issue, I'm giving it a close, but let's still figure out how to help @sobrinho solve this.

@schneems
Collaborator

It makes your app less secure if you're using a 3rd party buildpack, but the biggest reason is so that your build is more deterministic.Take a look at http://www.12factor.net/build-release-run your app should build the same way regardless how how it is configured.

I work with our support department quite a bit and I would say more often than not, the results of tickets from people who have user-env-compile turned on is because user-env-compile is being used (i.e. it worked in staging but deploy is broken on production, etc.).

So assets should precompile the same regardless of what environment variables are set, but when your app is running it is okay if they behave differently based on your config. It should be the routers job to figure out what paths match to what rails endpoint, not the asset precompilation. While not technically a bug, I would still consider the implementation of this feature in assets to be broken.

@sobrinho

Note that I'm using the same sub-path on development, staging and production environments.

In this case, the assets must be equal if compiled on any environment.

Since the assets on development compiles this:

background: url(<%= image_path("logo.png") %>);

To this:

background: url(/tributos/logo.png);

I expect my production environment to compile this:

background: url(<%= image_path("logo.png") %>);

To this:

background: url(/tributos/logo-hash.png);

But instead, it is compiling to:

background: url(/logo-hash.png);

I can use the action pack config to set the prefix and avoid the environment access:

config.relative_url_root = '/tributos'

But I still have to boot my application to sprockets recognize this config and to do that, I need to be able to connect to the database which will need to access environment variables.

I solved my case booting the application and using the user-env-compile on Heroku but I still classify this as bug since is not expected to the prefix be ignored when the application is not booted.

I took at least 4 hours to figure out the problem and I think other developers will have the same problem.

If we are not going to fix this, we need to at least document that config.assets.initialize_on_precompile = false will ignore the RAILS_RELATIVE_URL_ROOT and explain why.

@sobrinho

Would be great to have some easy option like:

config.relative_url_root = '/tributos'

And then, the application and sprockets recognize that without changing anything else.

The current receipt to deploy the application under a sub-path on Heroku is:

  1. Remove the mandatory config.assets.initialize_on_precompile = false
  2. Create the RAILS_RELATIVE_URL_ROOT environment variable
  3. Change the config.ru to use this variable and run the application under it
  4. Enable user-env-compile
  5. Deploy

This after to figure out that compilation do not consider the RAILS_RELATIVE_URL_ROOT when application is using config.assets.initialize_on_precompile = false.

Since I know the receipt and can workaround the problem on my applications, I'm not caring too much.

But this will cause headaches for others developers.

@schneems
Collaborator

Ideally precompiling assets shouldn't require a database connection, which would solve this issue as well. That would be 100% worth looking into, if it hasn't been fixed already. I'll put it on my todo.

@rafaelfranca

I think, precompiling doesn't try to connect to database in Rails 4.

@rafaelfranca

see e5946e0

@AvnerCohen AvnerCohen referenced this issue from a commit in AvnerCohen/rails
@AvnerCohen AvnerCohen Fix for #9058 and #8941, environment setup of RAILS_RELATIVE_URL_ROOT…
… was actually never used
9fc7c1e
@AvnerCohen AvnerCohen referenced this issue from a commit
Commit has since been removed from the repository and is no longer available.
@AvnerCohen AvnerCohen referenced this issue from a commit
Commit has since been removed from the repository and is no longer available.
@mde mde referenced this issue in geddy/geddy
Open

Running Geddy on subdomain. #592

@joevandyk

Stellenticket/rails@7f1e45e fixes the problem for me. Seeing this problem on places other than heroku.

@joevandyk

Ah, looks like there's a pull request for it already: #17724

@habermann24

Just wanted to add this to the mix.
What i've been doing in applications needing a sub-path is the following in config.ru:

# This file is used by Rack-based servers to start the application.

require ::File.expand_path('../config/environment', __FILE__)
map ENV['RAILS_RELATIVE_URL_ROOT'] || '/' do
  run Rails.application
end

Not sure if that's a bad idea, but seems to work well!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Something went wrong with that request. Please try again.