Nginx reverse proxy instead of directory listing - ruby-on-rails

I'm trying to have nginx serve static content while reverse proxying everything else to a rails server.
All that works, except for the home page.
If I go to example.com I get a 403 error and the error log shows
2019/06/14 04:32:59 [error] 9746#9746: *1 directory index of "/var/www/html/" is forbidden
I want the request to be sent to the rails server as example.com/ instead of trying (and failing) to get a directory listing. The rails server should display a homepage instead. (side note: if I turn autoindex on I will get a directory listing)
Configuration is here:
server {
listen 80 default_server;
listen [::]:80 default_server;
root /var/www/html;
server_name example.com;
index index.html;
location / {
autoindex off;
root /var/www/html;
try_files $uri $uri/ #rails;
expires max;
access_log off;
}
location #rails {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header Host $host;
proxy_pass http://127.0.0.1:3000;
}
}

If you want to disable the index behaviour for all URIs, drop the $uri/ term from the try_files statement. For example:
location / {
try_files $uri #rails;
...
}
See this document for details.
Alternatively, add a new location block to specifically handle the URI /, for example:
location = / {
try_files nonexistant #rails;
}
See this document for details.

A fix for the homepage would be to add an exact location for the homepage, like so:
location = / {
try_files #rails =404;
}

Related

How to serve compile assets with rails and nginx on docker?

I've been preparing for production with my rails application and I'm having a lot of trouble with nginx. Compared to development where the assets weren't precompiled, nothing works now. I use sprockets to handle the css, images and fonts and webpacker for the javascript. Problem is, whenever I try anything in the tutorials I see, it doesn't work. I always get erors such as "No route matches [GET] "/packs/js/" or "No route matches [GET] "/assets/...".
Here's what my default.conf looks like :
upstream railsapp {
server hubsite:3000;
}
server {
listen 80 default_server;
listen [::]:80 default_server;
root /app/public;
# Deny requests for files that should never be accessed
location ~ /\. {
deny all;
}
location ~* ^.+\.(rb|log|go|exe)$ {
deny all;
}
try_files $uri #rails;
# serve static (compiled) assets directly if they exist (for rails production)
location ~ ^/(assets|packs|images|javascripts|stylesheets|swfs|system)/ {
try_files $uri #rails;
access_log off;
gzip_static on;
# to serve pre-gzipped version
expires max;
add_header Cache-Control public;
add_header Last-Modified "";
add_header ETag "";
break;
}
location #rails {
proxy_pass http://railsapp;
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;
}
# 404 HANDLING
location = /404.html {
internal;
}
}
I also tried to modify the proxy_pass for my proper url, but it didn't work. Basically, there would always be a "/portal/" after the url, so like "mytestwebsite.com/portal/signin". All of this is running in a docker container and the root path does seem to be correct from what I checked.
If anyone has an idea as to why it doesn't work, feel free to point me in the good direction! Thanks!
Everything was fine with nginx, problem was that I wasn't properly linking my volumes with my docker-compose!

how to deploy angular plus rails on nginx?

I am trying to configure my NGINX, I ham hosting both the Angular and the Rails server on the same instance and would like to know how to get them to work together on the same server. I have alreadt
My NGINX conf file:
#rails config
server {
listen 3000;
server_name www.mysite.com;
root /var/www/mysite/public;
try_files $uri #app;
location #app {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect off;
proxy_pass http://mysite_prod;
proxy_intercept_errors on;
}
error_page 500 502 504 /500.html;
error_page 503 /503.html;
error_page 404 /404.html;
client_max_body_size 4G;
keepalive_timeout 10;
}
#angular config
server {
listen 80;
listen [::]:80;
server_name www.mysite.com;
root /var/www/my_app/dist;
server_tokens off;
index index.html index.htm;
location / {
try_files $uri $uri/ /index.html =404;
}
}
In my rails application, I have addded the IP to the Origins file
config/application.rb
config.middleware.insert_before 0, "Rack::Cors" do
allow do
origins 'localhost:3000', 'www.mysite.com', 'localhost:4200',
resource '*',
headers: :any,
expose: ['access-token', 'expiry', 'token-type', 'uid', 'client'],
methods: [:get, :post, :options, :put]
end
end
I get a cors error and the front end is unable to talk to the back end.
Error - 'has been blocked by CORS policy: The 'Access-Control-Allow-Origin' header has a value '
How do I get this to work?
I think you should not have two servers running. Only the Rails app should run and Angular app should just be the index.html file that the rails app serves.
Build the Angular app for distribution and replace the Rails' index file with the Angular app build artifacts. One you get this working look for options that enables you to perform this manual steps automatically.
Could not find too much in terms of documentation online for this combination on angular-rails-nginx. Figured it out based on similar apps people had built for react-rails-nginx. Finally this is what worked for me. Did not have any CORS issues etc.
nginx.conf
server {
listen 80;
listen [::]:80;
server_name mysite.com;
root /var/www/my_angular_app/dist;
server_tokens off;
index index.html index.htm;
location / {
try_files $uri $uri/ /index.html =404;
}
location /api {
root /var/www/my_app/public;
proxy_pass http://my_app_prod;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect off;
}
}
Of course, this also means that all the api calls to the rails back-end should go through the route '/api/xxx' so nginx knows it has to route it to the rails app without having to host it on a different port.

How to deploy React front end + Rails api backend with nginx, capistrano and passenger

I am deploying an app with a React front end created using create-react-app and a Rails API as the backend. I do not know much about configuring web servers and proxies so I am not sure exactly how to get it to work in production. I am deploying the app to Ubuntu on an Amazon EC2 instance. Nginx is the web server. I need to configure it so that Nginx serves the static files from the client/build directory and then any requests made to /api go to the Rails app running on port 3001. Right now Nginx is serving the index.html and the Javascript is running properly but requests to /api are not going to the right place. Any thoughts on how to configure Nginx to do this? Here is my /etc/nginx/sites-enabled/default file:
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name mydomain.com;
passenger_enabled on;
rails_env staging;
root /home/ubuntu/app-name/current/client/build;
index index.html;
location /api {
proxy_pass http://127.0.0.1:3001;
}
}
What am I missing? How do I get the Rails app to run on port 3001 and have all requests to /api go there? Do I need a separate server block in this config?
I don't know if you already solved your issue and if this solution will apply to you but I think it might be useful for other people searching on this issue.
I am using Puma as the application server to run my rails 5 api.
This is the configuration file for my dev environment:
upstream app {
# Path to Puma SOCK file, here is where you make the connection to your application server
server unix:/path/to/your/puma.sock fail_timeout=0;
}
server {
listen 80;
server_name mydomain.com;
# this is where my react-app is located
root /var/www/development/ram/public/;
index index.html index.htm;
# Serve the static content (React app)
location / {
try_files $uri /index.html =404;
}
location /api {
# Insert your public app path
root /your/rails-app/path/public;
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;
}
So comparing, I think your problem might be solved by adding a root directive pointing to your rails api public directory
I hope this can give you some hints on how to configure yours
Here is my working nginx config
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
server {
listen 80 default_server;
root /home/deploy/www/sublime/current/public;
index index.html;
server_name domain.com;
access_log /home/deploy/www/sublime/logs/access.log;
error_log /home/deploy/www/sublime/logs/errors.log;
server_name localhost;
passenger_enabled on;
passenger_app_env production;
location ~* ^.+\.(jpeg|gif|png|jpg) {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
}
location /api {
# Insert your public app path
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
proxy_set_header Host $http_host;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_buffering off;
}
location / {
# First attempt to serve request as file, then
# as directory, then fall back to displaying a 404.
try_files $uri /index.html;
}
}
You can use this repos to see rails configuration
https://github.com/Eth3rnit3/rails-react

subdir in nginx for rails api-app

I am trying to configure the nginx to have a separate subdir for rails-5 api backend. (to separate frontend and backend)
Original, I am calling backend api under GET "/bills". Now I'd like to be it: GET '/api/bills'. so all requests under 'api' should be redirected to rails app.
But I can't make it working. The redirection works, but I see on rails side in logs: ActionController::RoutingError (No route matches [GET] "/api/bills"). Of course this route doesn't exists. Rails only knows about the "/bills" route. Could I configure nginx so, that the redirection would be transparent to Rails, and it would see the request like [GET]"/bills" ?
here is my current config:
upstream app {
# Path to Unicorn SOCK file, as defined previously
server unix:/var/sockets/unicorn.myapp.sock fail_timeout=0;
}
server {
#redirect to https
listen 0.0.0.0:80;
listen [::]:80 ipv6only=on default_server;
server_name localhost; ## Replace this with something like gitlab.example.com
server_tokens off; ## Don't show the nginx version number, a security best practice
return 301 https://$server_name$request_uri;
access_log /var/log/nginx/app_access.log;
error_log /var/log/nginx/app_error.log;
}
server {
listen 0.0.0.0:443 ssl;
listen [::]:443 ipv6only=on ssl default_server;
server_name localhost; ## Replace this with something like gitlab.example.com
ssl on;
ssl_certificate /etc/ssl/nginx/host.crt;
ssl_certificate_key /etc/ssl/nginx/host.key;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 5m;
location ^~ /api {
#try_files $uri/index.html $uri $uri/;
try_files $uri/index.html $uri #app;
root /app/backend/public;
}
location #app {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect off;
proxy_pass http://app;
error_page 500 502 503 504 /500.html;
}
location / {
# Application Frontend root, as defined previously
try_files $uri $uri/ =404;
root /app/frontend/;
}
client_max_body_size 4G;
keepalive_timeout 10;
}
Inside your location #app block try adding this:
rewrite ^/api(.*)$ $1 break;
That should just strip off the /api prefix before sending the remainder of the URI upstream.
See this document for details.

Rails page cache multiple servers with nginx unicorn

I am using the page caching gem on Rails 4. I have a web server running nginx, app server running unicorn and rails, and db server running postgre.
When page cache is generated on the app server, nginx will not serve the static files. Only after I set
config.serve_static_assets = true
where the page cache will work in production. I don't think this is ideal though given that now rails is serving the static file.
How do I get nginx to serve my page caches located on the app server?
Here is my nginx config:
upstream unicorn {
server <%= app_private_ip %>:8080 fail_timeout=0;
}
server {
# listen [::]:80 ipv6only=on default_server;
listen 80 default deferred;
server_name <%= domain %>;
# rewrite ^(.*) https://<%= domain %>$1 permanent;
root <%= current_path %>/public;
sendfile on;
if (-f $document_root/system/maintenance.html) {
return 503;
}
error_page 503 #maintenance;
location #maintenance {
rewrite ^(.*)$ /system/maintenance.html last;
break;
}
location ^~ /assets/ {
gzip_static on;
expires max;
add_header Cache-Control public;
}
try_files $uri/index.html $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;
}
error_page 500 502 503 504 /500.html;
client_max_body_size 4G;
keepalive_timeout 10;
server_tokens off;
}
server {
listen 443;
server_name <%= domain %>;
ssl on;
ssl_certificate /home/<%= user %>/ssl/<%= domain %>.pem;
ssl_certificate_key /home/<%= user %>/ssl/<%= domain %>.key;
root <%= current_path %>/public;
sendfile on;
if (-f $document_root/system/maintenance.html) {
return 503;
}
error_page 503 #maintenance;
location #maintenance {
rewrite ^(.*)$ /system/maintenance.html last;
break;
}
location ^~ /assets/ {
gzip_static on;
expires max;
add_header Cache-Control public;
}
try_files $uri/index.html $uri #unicorn;
location #unicorn {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
proxy_set_header Host $http_host;
proxy_redirect off;
proxy_pass http://unicorn;
}
error_page 500 502 503 504 /500.html;
client_max_body_size 4G;
keepalive_timeout 10;
server_tokens off;
}
The nginx try_files directive lets you set cascading ways to resolve the static file for a URI in different locations/backends.
It looks like you need to move your try_files directive into a location block for it to work properly:
location / {
try_files $uri #unicorn;
}
This should tell nginx to try resolve paths locally by URI before passing the request to your unicorn backend.
The page caching gem asks you to set the cache directory to 'public/cache' in
application.rb;
config.action_controller.page_cache_directory = "#{Rails.root.to_s}/public/cache"
so your try_files line should be;
try_files /cache/$uri/index.html /cache/$uri #unicorn;
otherwise you can just set the page_cache_directory to;
"#{Rails.root.to_s}/public" and not change your current nginx config.

Resources