Add URL Prefix to requests using nginx or rails - ruby-on-rails

I'm trying to understand the best way to redirect all traffic for https://app.company.com/ to https://app.company.com/app
app.company.com is a rails 4.2 server which uses nginx to serve static assets and unicorn to handle the rails requests.
Should this redirect happen in nginx or rails?
Back story:
A few years ago I supported some rails apps that were installed on the intranet using URL Prefixing, like:
https://company.com/app1/
https://company.com/app2/
https://company.com/app3/
company.com ran an nginx server which routed traffic to each app server based on the url prefix.
Each app server runs nginx to serve static assets and unicorn as the rails server.
Recently a decision was made to hand management of the company.com server over to the parent company. As a result, a decision was made to route to each rails server by DNS using subdomains in place of nginx rules.
For now apps will be accessed using:
https://app1.company.com/app1/
https://app2.company.com/app2/
https://app3.company.com/app3/
Individual app servers are largely unchanged. They still run nginx and unicorn.
My main issue is understanding the best way to push traffic for the root to the url prefix
https://app1.company.com/ --> https://app1.company.com/app1/
The root route without the url prefix never reached the rails servers before.
UPDATE
Here is my nginx config file with original enhancement proposed by Vashed's answer.
I'm now curious if there is a refinement to this which would allow my config file to be independent of the server name like it was before.
upstream unicorn {
server unix:/tmp/unicorn.app1.sock fail_timeout=0;
}
server {
listen 80 deferred;
# ADDED THIS LOCATION BLOCK PER VASFED'S ANSWER
location = / {
rewrite ^ https://app1.company.com/app1;
}
location /app1/assets {
alias /var/www/application/current/public/app1/assets;
}
# Serve file uploads from nginx
location /app1/system {
alias /var/www/application/current/public/app1/system;
}
try_files $uri $uri/ #unicorn;
location #unicorn {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect off;
proxy_pass http://unicorn;
}
client_max_body_size 1G;
keepalive_timeout 10;
}

You can issue a redirect using nginx:
server{
server_name app1.company.com;
listen 443 ssl;
# other setup
location = / {
rewrite ^ https://app1.company.com/app1/;
}
location / {
# as before
}
}

Related

How to pass a Rails route through an ember-CLI app

I have a Rails app that has some controllers and views, but it also acts as the API back-end for an Ember-CLI app I have created. My problem is that I need to access a route that is defined in the Rails routes.rb but I believe the Ember-CLI app is preventing this route from being loaded in a web browser.
The Rails route looks like the following:
refile_app /attachments #<Refile::App app_file="/Users/myUserName/.rvm/gems/ruby-2.1.5/bundler/gems/refile-373d42114839/lib/refile/app.rb">
Another Rails route that isn't loading in the web browser is:
signup GET /signup(.:format) users#new
I need to be able to download an attachment, but I am unable to do so on the production box.
In my local dev environment I'm starting Rails with the following command:
rails s --binding 127.0.0.1
and I'm starting the ember-CLI app with the following command:
ember server --proxy http://127.0.0.1:300
In production, my nginx.conf looks like the following:
upstream puma_Kegcop {
server unix:///home/user/apps/Kegcop/shared/tmp/sockets/Kegcop-puma.sock;
}
server {
listen 80;
server_name kegcop.chrisrjones.com;
root /opt/ember/kegcop-web-frontend/dist;
access_log /home/user/apps/Kegcop/current/log/nginx.access.log;
error_log /home/user/apps/Kegcop/current/log/nginx.error.log;
# Proxy to our API
location /api {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
proxy_redirect off;
proxy_pass http://puma_Kegcop;
}
# UI, Ember app, Static Build
location / {
try_files $uri $uri/ /index.html?/$request_uri;
}
}

Nginx with 1 domain, 2 apps. Ror + nodejs

Hey.
After a few hours of research I couldn't find any easy how-to solutions for my problem.
I have written earlier a RoR app that's deployed on my server and running just fine. Now I'm trying to deploy also a Nodejs app to run alongside my RoR app. The two apps don't have anything to do with each other - they are to be used separately.
I only have one domain to use and I'm trying to use Nginx.
The RoR app is running on Unicorn + Nginx already.
My first question is what is the correct way to deploy two separate apps alongside on the same server?
Should they listen to different ports? Other to port :80 and other to :81 for example?
Or should I alternatively use sub folders? Going for exampleDomain/app1 and exampleDomain/app2?
I also read about an option of creating sub domains, but does this work when running my apps in production?
My RoR app is deployed following these instructions:
https://www.digitalocean.com/community/tutorials/how-to-deploy-a-rails-app-with-unicorn-and-nginx-on-ubuntu-14-04
(as a note I'm using Digital Ocean's virtual server)
Currently my Nginx file looks the following:
upstream app {
# Path to Unicorn SOCK file, as defined previously
server unix:/home/deploy/appname/shared/sockets/unicorn.sock fail_timeout=0;
}
server {
listen 80;
server_name localhost;
root /home/deploy/appname/public;
try_files $uri/index.html $uri #app;
location #app {
proxy_pass http://app;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect off;
}
error_page 500 502 503 504 /500.html;
client_max_body_size 4G;
keepalive_timeout 10;
}
It's a direct copy from the above RoR tutorial. For some reason if I switch the file name it stops working? Even that I couldn't find the file name being defined anywhere.
The file is under /etc/nginx/sites-available/ and named "default".
I tried to follow this tutorial for deploying the Nodejs app:
https://www.digitalocean.com/community/tutorials/how-to-set-up-a-node-js-application-for-production-on-ubuntu-14-04
I get my app running with pm2. And I tried writing the following code in another file in /etc/nginx/sites-available/ and also including the code in the existing default file alongside with the code for RoR but neither of them worked.
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://APP_PRIVATE_IP_ADDRESS:8080;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
}
Nginx logs are not showing anything useful either. I ran
sudo tail -f /var/log/nginx/error.log
a couple of times but the logs were empty.
I would be really thankful if someone could give me some guidance which way to go from here.
First question "How to put two apps on same server?".
All those approaches you mentioned will work, but probably the cleanest way for you and potential users would be to use a subdomain. To do this,
1) Decide on your subdomain (ie. nodejs.example.com and ror.example.com) and point both of those to your server.
2) In your 2 ngnix files for each name set the server name to the respective subdomain:
server {
listen 80;
server_name ror.example.com;
# Rest of conf below (pointing to ROR project)
}
And:
server {
listen 80;
server_name nodejs.example.com
# Rest of conf below (pointing to nodejs project)
}
Second Question: "Where do I place these files?"
To see where these files are actually used by nginx, I'd first look at the main nginx.conf file usually in the location: '/etc/nginx/nginx.conf'. There should be 2 lines near the end that tell you where nginx is looking for conf files:
http {
# A bunch of default nginx settings
include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;
}
If this is what is in your nginx.conf file, then nginx never will look in the sites-available folder. Where nginx is actually looking is in the two folders: /etc/nginx/conf.d/ and /etc/nginx/sites-enabled What I would suggest is do 3 things:
1) cd into /etc/nginx/sites-enabled folder. Remove the 'default' file.
2) create 2 files, nodejs and ror, and put the resptive nginx configurations into both of those files within the folder: /etc/nginx/sites-enabled.
3) Run:
sudo service nginx reload
sudo service nginx restart
If reload fails, run the following to help debug the configuration:
sudo nginx -t
This will show the file and line number of the issue.
Hope this helps. Comment if you have trouble with any of this.

nginx tries to serve .rss/.json itself rather than letting rails/unicorn serve it

My rails app runs perfectly fine in production using nginx and unicorn except for one thing:
Requesting /articles.rss and /articles.json leads to a 404 with an error in the nginx logs that the requested file doesn't exist. Requesting e.g. /articles?format=rss works. So it looks like the .rss leads nginx to think this is a static file rather than dynamically generated content. In development (using the builtin server of rails) this works fine.
I use the h5bp config files for nginx, here's my site configuration (domain name replaced):
# www to non-www redirect -- duplicate content is BAD:
# https://github.com/h5bp/html5-boilerplate/blob/5370479476dceae7cc3ea105946536d6bc0ee468/.htaccess#L362
# Choose between www and non-www, listen on the *wrong* one and redirect to
# the right one -- http://wiki.nginx.org/Pitfalls#Server_Name
upstream app {
server unix:/var/www/rails-cms/shared/tmp/sockets/rails-cms.unicorn.sock fail_timeout=0;
}
server {
# don't forget to tell on which port this server listens
listen [::]:80;
listen 80;
# listen on the www host
server_name www.<my domain>;
# and redirect to the non-www host (declared below)
return 301 $scheme://<my domain>$request_uri;
}
server {
# listen [::]:80 accept_filter=httpready; # for FreeBSD
# listen 80 accept_filter=httpready; # for FreeBSD
# listen [::]:80 deferred; # for Linux
# listen 80 deferred; # for Linux
listen [::]:80;
listen 80;
# The host name to respond to
server_name <my domain>;
# Path for static files
root /var/www/rails-cms/current/public;
try_files $uri/index.html $uri #app;
location #app {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Host $http_host;
proxy_redirect off;
proxy_pass http://app;
}
#Specify a charset
charset utf-8;
# Custom 404 page
error_page 404 /404.html;
# Include the basic h5bp config set
include h5bp/basic.conf;
}
This occurs because you include h5bp/basic.conf file which in turn includes h5bp/location/expires.conf file. Take a look at the latter file's contents:
# cache.appcache, your document html and data
location ~* \.(?:manifest|appcache|html?|xml|json)$ {
expires -1;
access_log logs/static.log;
}
# Feed
location ~* \.(?:rss|atom)$ {
expires 1h;
add_header Cache-Control "public";
}
See? the first location intercepts .json requests, and the second one - .rss requests, so your app never catches these requests.
If you want your app to handle these requests, you should either refuse h5bp at all, or just comment out abovementioned locations.
In both cases your app should send cache headers itself, if you want it of course.
Edit:
But since your json and rss contents are dynamically generated, I would not recommend using cache headers.

Set public directory outside rails app

Is it possible to make public directory outside of main Rails App directory?
Schema
/apps/app1
/apps/app2
/apps/this_dir_wants_to_be_public
I need use it with several Rails and Non-Rails Applications to manage Uploads
Two ways I can think of:
First: if you are using nginx, you can configure it to first check your custom dir, and serve files from there if found, otherwise ask rails app for responce. Nginx config would look like this
upstream backend {
server localhost:3000;
}
server {
listen 80;
server_name whatever.com;
root /your_static_path;
try_files $uri $uri/index.html #backend;
location #backend {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect off;
proxy_pass http://backend;
}
}
It is using nginx as a reverse proxy. You run thin server on 3000 port (or any other server you like), and nginx turns to it only if it could not find requested file in root directory.
Second: Just create symlinks
ln -s /apps/this_dir_wants_to_be_public /apps/rails_app/public
so /apps/this_dir_wants_to_be_public would be actual directory, and /apps/rails_app/public link to it. Seems absolutelly transparent for rails app and simple
Multiple public folders, single rails installation
use public.path
Symlinks may be the best approach in your case I think

nginx 403 forbidden error in Rails 4 (with no index.html file)

I'm following along with a Railscast http://railscasts.com/episodes/293-nginx-unicorn?view=asciicast about setting up Nginx and Unicorn on Vagrant, with one important difference. Ryan's making his application with Rails 3 (that has the default /public/index.html that Rails 4 only generates dynamically). After getting Nginx installed and running, we were able to see the default page on port 8080. We then created a basic config file for Nginx to put in the config directory of the rails application
/config/nginx.conf
server {
listen 80 default;
# server_name example.com;
root /vagrant/public;
}
and then removed the default page in sites enabled and symlinked to the configuration file
vagrant#lucid32:/etc/nginx/sites-enabled$ sudo rm default
vagrant#lucid32:/etc/nginx/sites-enabled$ sudo ln -s /vagrant/config/nginx.conf todo
After this, Ryan restarted nginx and was able to see the Rails index page at localhost:8080. However, when I visit localhost:8080, I'm getting a 403 Forbidden error.
403 Forbidden
nginx/1.1.19
Update
since Rails 4 doesn't have the public/index.html file anymore, I think the 403 error might be caused by that, as I learned from this blog post
http://www.nginxtips.com/403-forbidden-nginx/. It says to set autoindex to on (the default is off) in the config, but I'm not sure how to set it to get the Rails homepage to show.
When I did this
server {
listen 80 default;
root /vagrant/public;
location / {
autoindex on;
}
}
it got rid of the 403 permissions error (yay!), however, it's not showing the default Rails home page. Rather it's showing the directory structure so I'm wondering what the proper way to set it is.
If I try to set it to location/public, I get the 403 error again. Any ideas?
location /public {
autoindex on;
}
Update
Since I'm using Vagrant (virtual box), the app is in /vagrant, however setting the location to location/vagrant also results in a 403 error
location /vagrant {
autoindex on;
}
You'll need to pass the request from Nginx to Unicorn. You can do this like so:
server {
listen *:80;
root /vagrant/public;
location / {
# Serve static files if they exist, if not pass the request to rails
try_files $uri $uri/index.html $uri.html #rails;
}
location #rails {
proxy_redirect off;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_pass http://127.0.0.1:8080;
}
}
You may have to change the proxy_pass url. By default, unicorn will listen on 127.0.0.1:8080 but, if you have changed that, then you will need to specify that port.

Resources