Rails asset_host, cloudfront and heroku - ruby-on-rails

I'm running a rails 4.0 app on heroku and for the life of me I can't get my asset urls using the host I've set in asset_host.
I believe my cloudfront setup is good because I can substitute in my cloudfront url for any of my asset urls and the file is picked up from heroku and cached on cloudfront.
So https://xxxxxxxxxxxx.cloudfront.net/assets/application-xxxxxxxx.js is caching https://myapp.com/assets/application-xxxxxxxxx.js correctly.
The issue seems to be my assets helper, such as javascript_include_tag, are never using the asset_host setting in staging.rb.
All I see when I load my page is all my js and css files being served from https://myapp.com/assets/
My staging setup looks like this:
# Full error reports are disabled and caching is turned on
config.consider_all_requests_local = false
config.action_controller.perform_caching = true
# Disable Rails's static asset server (Apache or nginx will already do this)
config.serve_static_assets = true
# Compress JavaScripts and CSS
config.assets.compress = true
# Don't fallback to assets pipeline if a precompiled asset is missed
config.assets.compile = true
# Generate digests for assets URLs
config.assets.digest = true
#config.assets.digest = false
#config.assets.initialize_on_precompile = true
config.static_cache_control = "public, max-age=31536000"
# user Amazon Cloudfront for asset hosting
config.action_controller.asset_host = "https://xxxxxxxxxxxx.cloudfront.net"
Is there a magic combination of config settings that has somehow eluded me?

Ok, finally figured out what was going on - my app was using the rails_api gem, which removes a whole bunch of middleware from the standard rails stack.
Changing my application controller to class ApplicationController < ActionController::Base added back in the required middleware, and asset_host started working immediately. I'll investigate further to determine which middleware it is later and decide whether I want to go back to rails_api.
One more issue I then discovered was that rack-mini-profiler rewrites cache headers to always revalidate, thus negating most of the benefit of the cdn. I've now disabled it in staging and prod and my app is running a lot more snappily!

Related

Rails Application - Using Cloudfront for asset delivery with Heroku

The situation
I couldn't get my vendor assets to precompile in heroku without specifying each individual file to precompile in config/initializers/assets so resorted to setting
config.assets.compile = true
Note: I didn't require vendor assets in application.js because I'm calling them on a per page basis when they are needed.
Anyhow I setup a Cloudfront account and now everything is working as it does in development. But on deploy to Heroku, there is a warning and a link that leads to a StackOverflow post, warning against setting config.assets.compile to true.
Compile Set to True in Production If you have enabled your application
to config.assets.compile = true in production, your application might
be very slow. This was best described in a stack overflow post:
When you have compile on, this is what happens: Every request for a
file in /assets is passed to Sprockets. On the first request for each
and every asset it is compiled and cached in whatever Rails is using
for cache (usually the filesystem). On subsequent requests Sprockets
receives the request and has to look up the fingerprinted filename,
check that the file (image) or files(css and js) that make up the
asset were not modified, and then if there is a cached version serve
that.
This setting is also known to cause other run-time instabilities and
is generally not recommended. Instead we recommend either precompiling
all of your assets on deploy (which is the default) or if that is not
possible compiling assets locally.
My question is, Since I'm now using Cloudfront, does that cover me from what they are warning about, slowness etc.?
Thanks in advance for any advice :)
Yes, you're likely covered. The request for an asset will hit CloudFront first. After the first request, it will be cached and shouldn't hit your server for compilation. Every time your assets change, however, they'll have to recompile, which is of course very slow.

Serving only image assets from CloudFront with Rails

I recently switched to using CloudFront as a CDN to serve my assets using the simple
config.action_controller.asset_host = "url of your cloudfront distribution" in my config file.
All works well, CF pulls in assets that it doesn't have just fine, serves them just fine, is faster than using the asset pipeline.
However, for a variety of reasons, some of our JS breaks when served from CF and not our own server. So I am looking for a way to use CF just for image (or image/css) assets, and still serve the compiled application.js file directly from our own server.
Any ideas?
Rails allows you to set config.action_controller.asset_host to be a proc. That way you can have as much control as you like over the selection of the asset host. For example:
config.action_controller.asset_host = Proc.new { |source|
if source.ends_with?('.jpg')
"http://cdn.example.com"
else
nil
end
}
See the api docs for more details.

Rails Engine asset images are not compiled

I have a strange behavior with my engine gem https://github.com/antpaw/bhf on the production environment. The bhf/application.js and bhf/application.css is compiled the way you would expect it and also linked the right way in the template. But none of the assets/bhf/image files are compiled and can be found in shared/assets/bhf/ on production server, unless i change
config.serve_static_assets = false
to true in production.rb?
How bad is it to use this setting? And is there a way for my engine to work out-of-the-box?
Per your gemspec https://github.com/antpaw/bhf/blob/master/bhf.gemspec
It has a dependency on rails v4 s.add_dependency(%q<rails>, [">= 4.0.0"])
I suspect its related to asset digest. Where the assets are getting compiled as expected with digest but are referred from respective CSS without digest.
Its a possible issue with sprockets-rails discussed here issue#49
non-stupid-digest-assets - not so good but preferred solution
It will copy non-digested assets to /public
Are you using Webrick in production? If so, you will need to set config.serve_static_assets = true since it's no good at serving static assets. Other Ruby 'app servers' aren't also ideal for serving static assets so you'll need to have Rails do that for the meantime. It's not an ideal set-up though since page caching won't work and all requests will hit your app.
Once you use a proper server for serving static assets like Nginx or Apache, you will need to set it to config.serve_static_assets = false so that Rails will leave it to Nginx/Apache to handle serving static assets. That way, not all requests will have to hit your Rails app since caching will work.
Since you're building a Rails engine, you don't need to worry about that since that is the responsibility of the one who is deploying the Rails app. You won't have control over their config.

Is this Rails cloudfront custom origin setup optimal?

I've configured cloudfront to use Heroku as it's custom origin. Inside the production.rb file, I made the following setting:
middleware.insert_after('Rack::Cache', Rack::Deflater)
config.static_cache_control = "public, max-age=86400"
config.action_controller.asset_host = ENV["ASSET_HOST"]
Also, I modified the stylesheets, so that everywhere there was any asset used ( image/font ), I made use of asset_path. Therefore, I'm counting on the digest provided by the asset pipeline to expire my assets.
How's this cloudfront setup? Could anything be improved upon, related to the loading of assets?

Rails 3.1, asset pipeline: no route matches

This question is similar to Why do I get “no route matches” for requests to the asset pipeline?.
I have a rails 3.0 application that I upgraded to 3.1 and converted to use the new asset pipeline (thanks to RailsCasts #282 and #279).
In production mode, I'm seeing the application-<digest>.js and application-<digest>.css. Great! And if I look at the source of those files, I see they are compressed. Yee-haw! So that means the asset pipeline is working, right?
However, if I add ?debug_assets=1 to the URL so that I may view individual files, some of them are producing ActionController::RoutingError (No route matches [GET] "/assets/<filename>-<digest>.js"), and same goes for some CSS files. But not all, just some, and I can't figure out what makes some files do this and others not.
I've cleared out tmp/cache/* and restarted Passenger. I've bumped config.assets.version. I've restarted memcached. None of these seem to resolve it. But what's odd is this only comes up when I'm using ?debug_assets=1 in the URL; without it, I see just one JS and CSS file, all compressed and minified.
I don't use precompiled assets, by the way. But just for grins, I performed a rake assets:precompiled, and whaddya know? The ?debug_assets=1 now shows all JS and CSS files, and none of them are 404'd.
So I guess the question you might have is, "Why not just use precompiled assets and not worry about missing assets from lazy load?" Good point. Answer: I just like to make sure I understand what I am doing, what's happening, and that I'm doing things correctly.
application.rb:
config.assets.enabled = true
config.assets.version = '1.2'
production.rb:
config.assets.compress = true
config.assets.compile = true
config.assets.digest = true
config.assets.js_compressor = :uglifier
config.assets.css_compressor = :scss
development.rb:
config.assets.compress = false
# I keep this off during development because I want
# to make sure the compression isn't breaking my JS
config.assets.debug = false
If you precompile your assets and set compile to false, debug is disabled because you've told Rails to not use Sprockets at all, but assumes that the files can be served by nginx based on mappings in the asset pipeline manifest.
When compile is true (like you have) then requests for these assets (and the debug request) are sent back to Sprockets to be processed if the files are missing (which without being precompiled is the case).
I would have assumed Sprockets would serve the individual files for each digested name. This behavior sounds buggy to me, although I don't think it is intended to use debug in production anyway.

Resources