A route to serve static assets (like .jpgs, etc?) - ruby-on-rails

I've worked my way through a number of interesting routing problems - turning a request URL into a hash, etc., but just out of curiosity, is there a way to tell the routing system that you want anything that comes under a certain url subpath to be served literally - without going through a controller?
For instance, if I have /home/me/public_html/rails_proj/images/foo.jpg, and .../rails_proj/images/other/bar.jpg, can I insert a route that says "anything under images should just be served as an object of the default mime type?"
Might be interesting.

If you put the "images" directory into the "public" folder of the Rails app (for example: /public/images/) then you shouldn't have any problems with MIME types unless your web server is configured wrongly.
According to your examples, you want the images dir in the root of the app. I don't think there is a way though Rails to make those images visible, but if you really wanted to you could use mod_rewrite to make it work. Once again, it would be up to the web server to make sure the images had the correct MIME type.

Things that are served out of the public directory will not go through Rails - they'll just be handled by your server (probably apache). The only reason why you would need to serve images through the rails system is if you wanted some kind of control on who could access them. Just put everything else in public and access ala:
siteurl.whatever/images/*.jpg

I typically use nginx as a frontend and Apache/Passenger as a backend. Ngingx proxies all Rails requests to Apache but handles all static content itself. Check out the examples on the English nginx wiki. Here is a small excerpt for nginx config:
server {
listen 80;
server_name www.domain.com;
location ~* \.(jpg|jpeg|gif|png|ico|css|bmp|js)$ {
root /path/to/static/assets/dir;
}
location / {
proxy_pass http://127.0.0.1:81;
}
}
So have apache listen on port 81 to handle Rails requests proxied by nginx and let nginx deliver static content. Not only is nginx supposedly faster than Apache at delivering static content, but this also offloads your Rails application for every image, stylesheet, javascript or whatever other static content.

I think the simplest way solve this problem is just using image_path helper method, which provide you the path for the image you want to display in the view.
For example, if you want to refer a logo.png under the /assets/images/logo.png, you can just use image_path('logo.png').

Caveat: if your request URL matches a static resource WEBrick, mongrel or whatever will happily serve it. At any cost, you don't want this in production: if your traffic is high enough your app will be brought to its knees just because its mongrels will be busy serving static content.
So make sure that your web server is properly configured for all kinds of static content as the previous commenters have pointed out.

Related

Serving static content from a Rails app, hosted outside of the Rails app

I have a Rails app that has a combination of dynamic and static pages. For the static pages, I want to make them accessible for editing by my partner, who is not a Rails developer, and therefore wouldn't want to be going into git and making changes directly in the repo.
So, some sort of CMS seems like the right answer.
But for maintenance reasons - mostly related to difficulties upgrading the CMS gem that I've used in the past, I don't want to host a CMS inside of the Rails app anymore. I also want to gain other benefits, like better editing tools and easier access to templates that come with a hosted CMS solution.
I was thinking that I could do something like host the static content on something like Wordpress or Squarespace, then somehow serve that static content from the Rails app. If I used separate subdomains, I think this could be easy, but I'd like to keep all content under one www subdomain.
The Rails app is currently hosted on Heroku, and I use Google Domains for DNS.
I hope it is clear what I'm trying to achieve and why. Does anyone have any suggestions on how this could be accomplished?
This can be done with nginx reverse proxy in front of your app - you can proxy different paths to different apps/hosts like:
server {
server_name your_main_host.com;
# other nginx vhost config..
location /cms_prefix/ {
proxy_set_header Host host_that_cms_expects.com;
proxy_pass http://your_cms_host.url/;
}
location / {
proxy_set_header Host your_main_host.com;
proxy_pass http://your_rails_upstream;
}
}
Haven't deployed this method to heroku, but this should be possible by using nginx buildpack in "solo mode" (actually you want "proxy mode" but with custom config)

CloudFoundry: nginx for serving static content on top of Gunicorn (Docker)

I am currently running a Gunicorn server in a Docker container serving both a Flask application and static content (on Swisscom CloudFoundry).
What is the correct way to set up nginx as a reverse proxy for serving the static content? I assume the Staticfile buildpack is not the way to go?
Could someone point me in the right direction?
If you're using Cloud Foundry, it's super easy to spin up an extra instance or two to scale your app up and handle more load. I would recommend doing this as it's dead simple and should work for really any app type. That and look at client side caching. You can reduce your the load on your server generated by requests for static files by simply having files cached client side.
If you really are serving up a lot of static file and it's not cost efficient to scale up additional instances of your app, you can do the following:
1.) Push your Flask app using the Python buildpack. This will be given the main route for your app.
2.) Push your app files using the Static File buildpack using a separate hostname or context path. For example: static.example.com or www.example.com/static.
By doing this, you will route any requests to the static.example.com route or the www.example.com/static route and path to your static files being hosted by Nginx (courtesy of the Static File buildpack). Requests to your main route or not to the static path, will end up going to your Python app. The platform handles this and makes sure the routes go to the correct app based on the routes you define for each app.
The only downside is that this relies on you having static content separated out, so that you can map a custom route or custom route and path for your static content. That said, I don't think this should be an issue because you're running Flask. If it is an issue, you can always map multiple routes + paths. Depending on how your files are structured, this may require a lot of routes + paths to be mapped.
As I mentioned above, this has the advantage of relying on the platform to route static requests to one app and all other requests to another app. If you were to try and set up Nginx as a proxy, you'd be adding more layers of proxies and more latency to your requests.

Rails 3.2.9 Intercept Asset Requests For Remapping

In Ruby on Rails 3.2.9 is there a way to intercept asset requests and remap the URL that is being requested.
For example, for a request for /assets/javascripts/app.js I would like to intercept the request and strip out javascripts/. I've tried in the application.rb
config.asset_path = proc { |path|
path.slice! 'javascripts/'
}
I'm not aware of any way to intercept requests to assets and I highly doubt there's one or there'll ever be one at all.
Simply because it wouldn't work with compiled assets. What if the assets are on another server with a completely different software stack? For example if someone chooses to host the assets on Amazon S3, how could requests be intercepted at all?
If you really need this feature and you are self-hosting your assets the best way mght be configuring your web server to redirect the request.
Have a look at RewriteEngine for Apache or HttpRewriteModule for nginx. They provide mechanisms for URL rewriting. (I guess most production-grade web servers do have something simlar, too)

Is it possible to use modify nginx config file and use X-Accel-Redirect on Heroku?

Reading this article on nginx website, I'm interested in using X-Accel-Redirect header in the way that Apache or Lighttpd users might use the X-Sendfile header to help with the serving of large files.
Most tutorials I've found require you to modify the nginx config file.
Can I modify the nginx config file on Heroku and if so, how?
Secondly,
I found this X-Accel-Redirect plugin on github which looks like it removes the need to manually alter the nginx config file - it seems to let you add the redirect location in your controller code - does anyone know if this works on heroku? I can't test it out until tonight.
NB - I have emailed both Heroku support and goncalossilva to ask them the same questions but I have no idea when they will get back to me. I will post back with whatever it is they tell me though.
Although Heroku seem to be using Nginx for their reverse-proxy component, the thing about a platform-as-a-service stack like this is that no individual tenant has to (nor even gets to) configure or tune distinct elements of the stack for any given application.
Requests in and out could be routed through any number of different elements to and from your Rails app so it's their platform infrastructure (and not any particular tenant) that manages all of the internal configuration and behavior. You give up the fine-grained control for the other conveniences offered by a PaaS such as this.
If you really need what you've described then I'd suggest you might need to look elsewhere for Rails app hosting. I'd be surprised if their answer would be anything else but no.

Rails: Proxy Pass?

I've got a web-app that I want to migrate to Rails, which is currently just plain HTML with an Apache proxy to another server, running a custom database/webserver that serves the site's dynamic content.
For the moment, I want to do a staged move, since the content on the proxied server I won't be able to update until I update the static (HTML) server.
So... how would I configure a Rails route to proxy all requests to /dynamic/* to this other server? Or, how would I translate the Apache rule below to Rails?
Apache Proxy Rule:
ProxyPassMatch ^((?i)/dynamic/)(.*)$ http://xxx.xxx.com:8080/$2
ProxyPassReverse /dynamic/ http://xxx.xxx.com:8080/
Rails routes are used to route some urls to one controller and one action.
You can't make a route to a distant URL or proxying like this.
And that wouldn't be a good idea as it would force us to load all rails (activerecord and everything) when it wouldn't be necessary at all. That's the opposite of scalability.
Using an Apache Proxy rule remains the most appropriate here.

Resources