I have a Rails3 app hosted via NginX which provides a JSON API that shall support cross origin resource sharing (CORS).
The related headers should be added via NginX, not via the Rails app.
Therefore I added a catcher in routes.rb to fetch OPTIONS preflight requests:
match "*options", controller: "application", action: "options", constraints: { method: "OPTIONS" }
and I added the corresponding endpoint to my application controller:
def options
render :text => "", :layout => false
end
Works. Now I want a wide open CORS config for my NginX:
server {
listen 80;
server_name localhost;
passenger_enabled on;
root /home/deployer/apps/fooapp/current/public;
location /v1 {
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,X-FooApp-Auth-Token';
add_header 'Access-Control-Max-Age' 1728000;
add_header 'Content-Type' 'text/plain charset=UTF-8';
add_header 'Content-Length' 0;
return 200;
}
if ($request_method = 'POST') {
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,X-FooApp-Auth-Token';
}
if ($request_method = 'GET') {
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,X-FooApp-Auth-Token';
}
}
}
Great, OPTIONS request have all the needed headers in the response now:
curl -i -H'Accept: application/json' -H'Content-Type: application/json' -XOPTIONS 'http://api.fooapp.net/v1/api_session' -d'{"user":{"email":"demo#fooapp.net", "password":"somepassword"}}'
HTTP/1.1 200 OK
Server: nginx/1.2.3
Date: Sun, 02 Sep 2012 11:04:28 GMT
Content-Type: application/octet-stream
Content-Length: 0
Connection: keep-alive
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, POST, OPTIONS
Access-Control-Allow-Headers: DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,X-FooApp-Auth-Token
Access-Control-Max-Age: 1728000
Content-Type: text/plain charset=UTF-8
Content-Length: 0
But a real POST requests for example now fails with a 404:
curl -i -H'Accept: application/json' -H'Content-Type: application/json' -XPOST 'http://api.fooapp.net/v1/api_session' -d'{"user":{"email":"demo#fooapp.net", "password":"somepassword"}}'
HTTP/1.1 404 Not Found
Server: nginx/1.2.3
Date: Sun, 02 Sep 2012 11:06:19 GMT
Content-Type: text/html
Content-Length: 168
Connection: keep-alive
<html>
<head><title>404 Not Found</title></head>
<body bgcolor="white">
<center><h1>404 Not Found</h1></center>
<hr><center>nginx/1.2.3</center>
</body>
</html>
And it has to, cause the NginX log says:
2012/09/02 13:06:19 [error] 2464#0: *3 open() "/home/deployer/apps/fooapp/current/public/v1/api_session" failed (2: No such file or directory), client: 127.0.0.1, server: localhost, request: "POST /v1/api_session HTTP/1.1", host: "api.fooapp.net"
I am new to NginX, and I know thats a wide open as well as very lightweight configuration. I do not understand why NginX tries to route these requests via public, so that the underlying Rails app is not requested. How do I have to change the configuration, so that all my requests are passed to the app as before, but the CORS headers are added anyway?
Thx in advance
Felix
This comes a little late but since I just ran into the same problem it might help somebody else.
You need to enable passenger inside the location block
location /{
...
passenger_enabled on;
...
}
I hope this helps.
Related
Earlier I was using heroku to server static assets. Then I decided to use cloud front to serve static assets for my rails(5.0.2) app on heroku. After configuring it all seemed good but for fonts chrome was throwing this error .
Access to Font at 'https://eeeeeee.cloudfront.net/assets/fontawesome-webfont-18e6b5ff511b90edf098e62ac45ed9d6673a3eee10165d0de4164d4d02a3a77f.woff?v=3.2.1' from origin 'https://staging-example.herokuapp.com' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'https://staging-example.herokuapp.com' is therefore not allowed access.
I googled the issue and found some info here 'Cloudfront CORS issue serving fonts on Rails application'. As per the first answer I followed all the steps. My rock-cors configuration is
config.middleware.insert_before 0, Rack::Cors do
allow do
origins %w[
https://staging-example.herokuapp.com
http://staging-example.herokuapp.com
]
resource '/assets/*'
end
end
which is there in application.rb
Still I am getting this issue
Access to Font at 'https://eeeeeee.cloudfront.net/assets/fontawesome-webfont-18e6b5ff511b90edf098e62ac45ed9d6673a3eee10165d0de4164d4d02a3a77f.woff?v=3.2.1' from origin 'https://staging-example.herokuapp.com' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'https://staging-example.herokuapp.com' is therefore not allowed access.
Using curl to look for headers I got this out puts
when hitting my url
curl -H "Origin: https://staging-example.herokuapp.com" -I https://staging-example.herokuapp.com/assets/fontawesome-webfont-18e6b5ff511b90edf098e62ac45ed9d6673a3eee10165d0de4164d4d02a3a77f.woff?v=3.2.1
HTTP/1.1 200 OK
Server: Cowboy
Date: Sat, 17 Jun 2017 13:49:11 GMT
Connection: keep-alive
Access-Control-Allow-Origin: https://staging-example.herokuapp.com
Access-Control-Allow-Methods: GET
Access-Control-Max-Age: 1728000
Access-Control-Allow-Credentials: true
Last-Modified: Tue, 02 May 2017 11:13:21 GMT
Content-Type: application/font-woff
Vary: Origin
Content-Length: 43572
Via: 1.1 vegur
When hitting direclty to cdn url
curl -H "Origin: https://staging-example.herokuapp.com" -I https://eeeeeee.cloudfront.net/assets/fontawesome-webfont-18e6b5ff511b90edf098e62ac45ed9d6673a3eee10165d0de4164d4d02a3a77f.woff?v=3.2.1
HTTP/1.1 200 OK
Content-Type: application/font-woff
Content-Length: 43572
Connection: keep-alive
Server: Cowboy
Date: Sat, 17 Jun 2017 13:19:04 GMT
Access-Control-Allow-Origin: https://staging-example.herokuapp.com
Access-Control-Allow-Methods: GET, POST, OPTIONS
Access-Control-Max-Age: 1728000
Access-Control-Allow-Credentials: true
Last-Modified: Tue, 02 May 2017 11:13:21 GMT
Via: 1.1 vegur, 1.1 21e1fe3458bce196f8eb1701ebbbce53.cloudfront.net (CloudFront)
Vary: Origin
Age: 2023
X-Cache: Hit from cloudfront
X-Amz-Cf-Id: zFXm3g53TJ4Nm6a9oH0yjVq-KUvvPoQI1chz_XN8nnaEd-p-TtQPNg==
Clearly the headers are present then why chrome is throwing that error. Kindly help.
You need to add preflight headers to your application_controller.rb
:
before_action :cors_set_access_control_headers
def cors_set_access_control_headers
headers['Access-Control-Allow-Origin'] = '*'
headers['Access-Control-Allow-Methods'] = 'POST, PUT, DELETE, GET, PATCH, OPTIONS'
headers['Access-Control-Request-Method'] = '*'
headers['Access-Control-Allow-Headers'] = 'Origin, X-Requested-With, Content-Type, Accept, Authorization'
end
Some time my server got this error
<html>
<head>
<title>502 Bad Gateway</title>
</head>
<body bgcolor="white">
<center>
<h1>502 Bad Gateway</h1>
</center>
<hr>
<center>nginx/1.4.6 (Ubuntu)</center>
</body>
</html>
<!-- a padding to disable MSIE and Chrome friendly error page -->
<!-- a padding to disable MSIE and Chrome friendly error page -->
<!-- a padding to disable MSIE and Chrome friendly error page -->
<!-- a padding to disable MSIE and Chrome friendly error page -->
<!-- a padding to disable MSIE and Chrome friendly error page -->
<!-- a padding to disable MSIE and Chrome friendly error page -->
after restart it working normal. I don't know why. I'm appreciate with any help .
Thanks
This is my Nginx config, it run with Unicorn .
user www-data;
worker_processes 10;
pid /run/nginx.pid;
events {
worker_connections 768;
# multi_accept on;
}
http {
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
types_hash_max_size 2048;
include /etc/nginx/mime.types;
default_type application/octet-stream;
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
gzip on;
gzip_disable "msie6";
upstream app_server { server unix:/tmp/unicorn.spui.sock fail_timeout=0; }
upstream faye_server { server 127.0.0.1:9292 fail_timeout=0; }
upstream seo_server { server 127.0.0.1:8000 fail_timeout=0; }
# CACHE
proxy_cache_path /var/www/cache keys_zone=seo:10m;
include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;
}
When i coming server see the error log i got.
2507 connect() to unix:/tmp/unicorn.spui.sock failed (111: Connection refused) while connecting to upstream, client:
I have a rails app that I just deployed to Digital Ocean and it's running on Puma and Nginx.
Eventually all it returns is a bad gateway and this is what is in the error.log
2014/09/09 22:23:06 [error] 5729#0: *3059 connect() to unix:///var/www/mysite/mysite_app.sock failed (111: Connection refused) while connecting to upstream, client: 67.5.19.192, server: mysite.com, request: "GET / HTTP/1.1", upstream: "http://unix:///var/www/mysite/mysite_app.sock:/", host: "mysite.com"
To fix it, I just restart puma and it seems to work.
How can I debug this to figure out why it keeps dying?
Here's my nginx config:
upstream mysite {
server unix:///var/www/mysite/mysite_app.sock;
}
server {
listen 80;
server_name mysite.com;
root /var/www/mysite/current/public;
client_max_body_size 20M;
location / {
proxy_pass http://mysite; # match the name of upstream directive which is defined above
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
location ~* ^/assets/ {
# Per RFC2616 - 1 year maximum expiry
expires 1y;
add_header Cache-Control public;
# Some browsers still send conditional-GET requests if there's a
# Last-Modified header or an ETag header even if they haven't
# reached the expiry date sent in the Expires header.
add_header Last-Modified "";
add_header ETag "";
break;
}
}
EDIT
Could this be caused from running out of memory?
Here's my current state of memory, but as I keep running this command every so often, the amount of free memory goes down and once i restart puma it jumps back up to like 150.
$ free -m
total used free shared buffers cached
Mem: 490 440 50 0 17 84
-/+ buffers/cache: 338 151
Swap: 0 0 0
It seems this is actually an issue with ruby 2.1 (specifically i'm using 2.1.2) and its garbage collection.
A google search like this seems to have lots of various threads on it http://bit.ly/1s2vBC0
Here's a ruby bug ticket on the issue: https://bugs.ruby-lang.org/issues/9607
Lack of memory can be issue, but you better look into puma and rails logs, not nginx only. In the application folder:
tail -f log/puma*
tail -f log/production.log
I've had similar issues but I note that 2.1.3 has now been released and specifically discusses memory issues:
https://www.ruby-lang.org/en/news/2014/09/19/ruby-2-1-3-is-released/
I'm going to try it now!
Here's a bare-bones Rails 4 project I've set up to troubleshoot my problem:
https://github.com/rejacobson/rails4-streamtest
I have a route set up at /home/stream that should stream a line of text 5 times in 1 second intervals.
def stream
5.times do |n|
puts "Streaming: #{n}"
response.stream.write "Streaming: #{n+1}"
sleep 1
end
rescue IOError => e
puts 'Connection closed'
ensure
response.stream.close
end
When I run puma using tcp://, without nginx, the streaming works perfectly.
curl -N http://localhost:3000/home/stream
And I get the 5 lines streamed back, no problem.
When I introduce nginx, curl will output the first line but immediately exit after that. I do continue to see output from the puts calls on the server and logs, so I know the request is still processing in the 5.times loop.
It also doesn't throw an IOError exception like it would if the user cut the connection.
Here's what I've tried so far:
Different combinations of nginx directives in the main conf file and
the server conf.
Changing rails settings.
Various puma config settings.
Setting all kinds of different headers in the controller method in the hopes that it was a caching issue.
Searching the internet has provided very little help as well.
I'm at a loss and require some direction.
Here's the output of both curl calls, with and without nginx.
Without nginx
~/projects/streamtest ▰ master ▰
ryan mirage ▰▰▰▰ curl -i -N http://192.168.1.100:3000/home/stream
HTTP/1.1 200 OK
X-Frame-Options: SAMEORIGIN
X-XSS-Protection: 1; mode=block
X-Content-Type-Options: nosniff
X-UA-Compatible: chrome=1
Cache-Control: no-cache
Content-Type: text/html; charset=utf-8
Set-Cookie: request_method=GET; path=/
X-Request-Id: 9ce86358-4476-404a-97e5-769c16ec7b0c
X-Runtime: 0.978099
Transfer-Encoding: chunked
Streaming: 1Streaming: 2Streaming: 3Streaming: 4Streaming: 5
puma.stdout
Streaming: 0
Streaming: 1
Streaming: 2
Streaming: 3
Streaming: 4
[8048] 192.168.1.100 - - [14/Mar/2014 21:04:50] "GET /home/stream HTTP/1.1" 200 - 6.0661
With nginx
~/projects/streamtest ▰ master ▰
ryan mirage ▰▰▰▰ curl -i -N http://192.168.1.100:3000/home/stream
HTTP/1.1 200 OK
Server: nginx/1.4.5
Date: Sat, 15 Mar 2014 04:02:40 GMT
Content-Type: text/html; charset=utf-8
Transfer-Encoding: chunked
Connection: keep-alive
X-Frame-Options: SAMEORIGIN
X-XSS-Protection: 1; mode=block
X-Content-Type-Options: nosniff
X-UA-Compatible: chrome=1
ETag: "a505e0aa3b11b25301a9a704252a519a"
Cache-Control: max-age=0, private, must-revalidate
Set-Cookie: request_method=GET; path=/
X-Request-Id: 8983d199-026b-4082-a5f1-f1d6c886a3d6
X-Runtime: 0.016516
Streaming: 1
puma.stdout
Streaming: 0
[7558] 192.168.1.100 - - [14/Mar/2014 21:02:40] "GET /home/stream HTTP/1.0" 200 - 0.0214
Streaming: 1
Streaming: 2
Streaming: 3
Streaming: 4
What's interesting, and I've just noticed it, is that the location of the get request log line:
"GET /home/stream HTTP/1.0" 200
is different in each curl call, and is placed in relation to how much text is actually streamed.
Any ideas as to what's going on here? Why can't rails stream the entire thing when using nginx?
Solved
It turns out all that I needed was this line in my location directive to get streaming working through nginx:
proxy_http_version 1.1;
I discovered this fix when investigating the keepalive directive in nginx's upstream module:
http://nginx.org/en/docs/http/ngx_http_upstream_module.html#keepalive
This piece of text in particular clued me in:
For HTTP, the proxy_http_version directive should be set to “1.1” and the “Connection” header field should be cleared:
upstream http_backend {
server 127.0.0.1:8080;
keepalive 16;
}
server {
...
location /http/ {
proxy_pass http://http_backend;
proxy_http_version 1.1;
proxy_set_header Connection "";
...
}
}
It seems that nginx defaults to, proxy_http_version 1.0;
And according to Wikipedia, http://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol
HTTP/1.1 introduced chunked transfer encoding to allow content on persistent connections to be streamed rather than buffered.
So there was my problem.
TIL
Ruby 1.92
Rails 3.1.1
Nginx 1.1.6
Passenger 3.0.7
My rails application works fine on my laptop, but it does not work on amazon server. I open it in chrome, browser display nothing, HTTP header like below:
HTTP/1.1 200
Content-Type: text/html; charset=utf-8
Content-Length: 0
Connection: keep-alive
Status: 302
X-Powered-By: Phusion Passenger (mod_rails/mod_rack) 3.0.7
If I run rails server, it does work, I can get correct html. Nginx cannot generate correct content-length?
nginx config
server {
listen 8000;
server_name xxxx;
root /xxx/xxx/public;
passenger_enabled on;
rails_env development;
location = /favicon.ico {
expires max;
add_header Cache-Control public;
}
location ~* \.(png|gif|jpg|jpeg|css|js|swf|ico)(\?[0-9]+)?$ {
access_log off;
expires max;
add_header Cache-Control public;
}
}
http raw message is below
total 3496 bytes return, content-length is wrong,so browser do read rest html
HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
Content-Length: 0
Connection: keep-alive
Status: 200
X-Powered-By: Phusion Passenger (mod_rails/mod_rack) 3.0.7
X-UA-Compatible: IE=Edge
ETag: "12aa68a45bf774886311f827d2149cbe"
Cache-Control: max-age=0, private, must-revalidate
X-Runtime: 0.499998
Server: nginx/1.1.6 + Phusion Passenger 3.0.7 (mod_rails/mod_rack)
<!DOCTYPE html>
<html>
<head>
<title>xxxx</title>
......
A HTTP status of 302 is usually a redirect. Are there any other headers or body content? I'd expect a location header telling the browser where to redirect to.