【问题标题】:How do I configure `Access-Control-Allow-Origin` with rails, nginx and passenger?如何使用 rails、nginx 和乘客配置“Access-Control-Allow-Origin”?
【发布时间】:2014-11-14 17:27:25
【问题描述】:

我无法让 Access-Control-Allow-Origin 在 Chrome 中显示 - 我的最终目标是使用 Rails 为字体配置 CORS,因此它可以在 production 和 CloudFront 中使用。不过现在,我只想让它在development 中工作。我可以通过curl 看到标题,但不是 Chrome。

我正在使用Rails 4.0,并且我已经尝试了以下所有方法...

我已经按照the rack-cors example for rails 4配置了Gemfileapplication.rb

宝石文件

gem 'rack-cors', '~> 0.2.9', require: 'rack/cors'

config/application.rb

config.middleware.insert_before 'ActionDispatch::Static', 'Rack::Cors' do
    allow do
        origins '*'
        resource '*',
            :headers => :any,
            :methods => [:get, :options, :head]
    end
end

rails 控制台

2.0.0-p481 :001 > Rails.env
 => "development"
2.0.0-p481 :002 > Hello::Application.config.serve_static_assets
 => true

重击

curl -i http://localhost:5000/assets/OpenSans-Regular-webfont.woff

Content-Type: application/font-woff
Content-Length: 22660
Connection: keep-alive
Status: 200 OK
Cache-Control: public, must-revalidate
Last-Modified: Wed, 30 Apr 2014 23:51:57 GMT
ETag: "467b34801137bd4031e139839ad86370"
X-Request-Id: c4b07b4d-1c43-44ea-9565-dfda66378f98
X-Runtime: 0.046007
X-Powered-By: Phusion Passenger 4.0.50
Date: Sat, 20 Sep 2014 04:39:38 UTC
Server: nginx/1.6.1 + Phusion Passenger 4.0.50

curl -i -H "Origin: http://localhost:5000" http://localhost:5000/assets/OpenSans-Regular-webfont.woff

Content-Type: application/font-woff
Content-Length: 22660
Connection: keep-alive
Status: 200 OK
Cache-Control: public, must-revalidate
Last-Modified: Wed, 30 Apr 2014 23:51:57 GMT
ETag: "467b34801137bd4031e139839ad86370"
Access-Control-Allow-Origin: http://localhost:5000   # adding
Access-Control-Allow-Methods: GET, OPTIONS, HEAD     # -H
Access-Control-Max-Age: 1728000                      # produced
Access-Control-Allow-Credentials: true               # these
Vary: Origin                                         # headers
X-Request-Id: b9666f30-416d-4b5b-946a-bdd432bc191c
X-Runtime: 0.050420
X-Powered-By: Phusion Passenger 4.0.50
Date: Sat, 20 Sep 2014 03:45:30 UTC
Server: nginx/1.6.1 + Phusion Passenger 4.0.50

Chrome (v37) 开发者工具 > 网络 > OpenSans-Regular-webfont.woff > 标头 > 响应标头

HTTP/1.1 304 Not Modified
Connection: keep-alive
Status: 304 Not Modified
Cache-Control: no-cache
X-Request-Id: ac153b8c-e0cb-489d-94dd-90aacc10d715
X-Runtime: 0.116511
X-Powered-By: Phusion Passenger 4.0.50
Date: Sat, 20 Sep 2014 03:41:53 UTC
Server: nginx/1.6.1 + Phusion Passenger 4.0.50

根据various sources,我还尝试了以下替代方案:

config.middleware.insert_before 'ActionDispatch::Static', 'Rack::Cors' do
config.middleware.insert_after Rails::Rack::Logger, Rack::Cors do
config.middleware.insert_before Warden::Manager, Rack::Cors do
config.middleware.insert 0, Rack::Cors do
config.middleware.use Rack::Cors do

我还按照How to Display FontAwesome in Firefox Using Rails and CloudFrontapplications.rb进行了以下尝试:

config.assets.header_rules = {
  :global => {'Cache-Control' => 'public, max-age=31536000'},
  :fonts  => {'Access-Control-Allow-Origin' => '*'}
}

我还在config.ru 中尝试了以下操作,根据CloudFront CDN with Rails on Heroku

require 'rack/cors'
use Rack::Cors do
    allow do
        origins '*'
        resource '*', :headers => :any, :methods => :get 
    end 
end

捆绑 exec rake 中间件

use Rack::Cors
use Rack::Sendfile
use ActionDispatch::Static
use Rack::Lock
use #<ActiveSupport::Cache::Strategy::LocalCache::Middleware:0x007f9ec21590b0>
use Rack::Runtime
use Rack::MethodOverride
use ActionDispatch::RequestId
use Rails::Rack::Logger
use ActionDispatch::ShowExceptions
use ActionDispatch::DebugExceptions
use ActionDispatch::RemoteIp
use ActionDispatch::Reloader
use ActionDispatch::Callbacks
use ActiveRecord::Migration::CheckPending
use ActiveRecord::ConnectionAdapters::ConnectionManagement
use ActiveRecord::QueryCache
use ActionDispatch::Cookies
use ActionDispatch::Session::CookieStore
use ActionDispatch::Flash
use ActionDispatch::ParamsParser
use Rack::Head
use Rack::ConditionalGet
use Rack::ETag
use Warden::Manager
use OmniAuth::Strategies::Facebook
run Hello::Application.routes

我也试过font_assets,但无济于事。

【问题讨论】:

    标签: ruby-on-rails nginx http-headers cors passenger


    【解决方案1】:

    Server 行让我觉得资产可能不是由 Rails 处理,而是由nginx

    这意味着header必须由nginx添加,而不是Rails,因此我们需要配置nginx。原来the ability to configure nginx is possible as of Passenger 4.0.39 - (here is the corresponding Git diff)。 Passenger Standalone, under Advanced configuration 中提供了相应的文档。

    文档中的重要说明:原始配置模板文件可能会不时更改,例如因为在 Phusion Passenger 中引入了新功能。如果您的配置模板文件不包含所需的更改,那么这些新功能可能无法正常工作。在最坏的情况下,Standalone 甚至可能出现故障。因此,每次升级 Phusion Passenger 时,都应该检查原配置模板文件是否发生变化,并将任何变化合并回自己的文件中。

    关于该注释,除了配置文件的可自定义副本之外,创建一个“原始”副本,您可以在升级Passenger 时diff

    重击

    cp $(passenger-config about resourcesdir)/templates/standalone/config.erb config/nginx.conf.erb
    cp config/nginx.conf.erb config/nginx.conf.erb.original
    

    接下来,将--nginx-config-template config/nginx.conf.erb 添加到Procfile 中的web 行。

    过程文件

    web: bundle exec passenger start -p $PORT --max-pool-size 3 --nginx-config-template config/nginx.conf.erb
    

    config/nginx.conf.erb

    接下来,通过找到如下所示的块来编辑配置文件config/nginx.conf.erb

        location @static_asset {
            gzip_static on;
            expires max;
            add_header Cache-Control public;
            add_header ETag "";
        }
    

    ...并添加两个 Access-Control 行:

        location @static_asset {
            gzip_static on;
            expires max;
            add_header Cache-Control public;
            add_header ETag "";
            add_header Access-Control-Allow-Origin *;
            add_header Access-Control-Request-Method *;
        }
    

    就是这样。这将在 production 中有效,但在 development 中无效,因为 config.assets 两者之间存在差异。

    配置差异

    diff 现在不应返回任何内容,但如果未来对乘客的任何更新包括对此文件的更改,您就会知道。

    diff $(passenger-config about resourcesdir)/templates/standalone/config.erb config/nginx.conf.erb.original
    

    nginx 文档

    未来的改进

    • 限制Allow-Origin
    • 限制Request-Method
    • 将两个标题都限制为仅字体

    【讨论】:

      【解决方案2】:

      我不确定它是否是答案,但看起来你也可以尝试使用after_filter 的最简单方法:

      headers['Access-Control-Allow-Origin'] = '*'
      headers['Access-Control-Allow-Methods'] = 'POST, PUT, DELETE, GET, OPTIONS'
      headers['Access-Control-Request-Method'] = '*'
      headers['Access-Control-Allow-Headers'] = 'Origin, X-Requested-With, Content-Type, Accept, Authorization'
      ...
      

      【讨论】:

        【解决方案3】:

        是的!最后。

        user664833 的 answer above 很棒,但我找不到要编辑的乘客配置文件。

        Thomas Nye 的回答 here 在 config/nginx.conf.erb 中提供了要创建的完整文件:

            ##########################################################################
        #  Passenger Standalone is built on the same technology that powers
        #  Passenger for Nginx, so any configuration option supported by Passenger
        #  for Nginx can be applied to Passenger Standalone as well. You can do
        #  this by direct editing the Nginx configuration template that is used by
        #  Passenger Standalone.
        #
        #  This file is the original template. DO NOT EDIT THIS FILE DIRECTLY.
        #  Instead, make a copy of this file and pass the `--nginx-config-template`
        #  parameter to Passenger Standalone.
        #
        #  Learn more about using the Nginx configuration template at:
        #  https://www.phusionpassenger.com/library/config/standalone/intro.html#nginx-configuration-template
        #
        #  *** NOTE ***
        #  If you customize the template file, make sure you keep an eye on the
        #  original template file and merge any changes. New Phusion Passenger
        #  features may require changes to the template file.
        ##############################################################
        
        <%= include_passenger_internal_template('global.erb') %>
        
        worker_processes 1;
        events {
            worker_connections 4096;
        }
        
        http {
            <%= include_passenger_internal_template('http.erb', 4) %>
        
            ### BEGIN your own configuration options ###
            # This is a good place to put your own config
            # options. Note that your options must not
            # conflict with the ones Passenger already sets.
            # Learn more at:
            # https://www.phusionpassenger.com/library/config/standalone/intro.html#nginx-configuration-template
        
            ### END your own configuration options ###
        
            default_type application/octet-stream;
            types_hash_max_size 2048;
            server_names_hash_bucket_size 64;
            client_max_body_size 1024m;
            access_log off;
            keepalive_timeout 60;
            underscores_in_headers on;
            gzip on;
            gzip_comp_level 3;
            gzip_min_length 150;
            gzip_proxied any;
            gzip_types text/plain text/css text/json text/javascript
                application/javascript application/x-javascript application/json
                application/rss+xml application/vnd.ms-fontobject application/x-font-ttf
                application/xml font/opentype image/svg+xml text/xml;
        
            <% if @app_finder.multi_mode? %>
                # Default server entry for mass deployment mode.
                server {
                    <%= include_passenger_internal_template('mass_deployment_default_server.erb', 12) %>
                }
            <% end %>
        
            <% for app in @apps %>
            server {
                <%= include_passenger_internal_template('server.erb', 8, true, binding) %>
                <%# <%= include_passenger_internal_template('rails_asset_pipeline.erb', 8, false) %1> %>
        
                ### BEGIN your own configuration options ###
                # This is a good place to put your own config
                # options. Note that your options must not
                # conflict with the ones Passenger already sets.
                # Learn more at:
                # https://www.phusionpassenger.com/library/config/standalone/intro.html#nginx-configuration-template
                # Rails asset pipeline support.
                location ~ "^/assets/.+-([0-9a-f]{32}|[0-9a-f]{64})\..+" {
                    error_page 490 = @static_asset;
                    error_page 491 = @dynamic_request;
                    recursive_error_pages on;
        
                    if (-f $request_filename) {
                        return 490;
                    }
                    if (!-f $request_filename) {
                        return 491;
                    }
                }
                location @static_asset {
                    gzip_static on;
                    expires max;
                    add_header Cache-Control public;
                    add_header ETag "";
                    if ($http_origin ~* ((https?:\/\/[^\/]*\.herokuapp\.com(:[0-9]+)?))) {
                        add_header 'Access-Control-Allow-Origin' "$http_origin";
                        add_header 'Access-Control-Allow-Credentials' 'true';
                        add_header 'Access-Control-Allow-Methods' 'GET, HEAD';
                        add_header 'Access-Control-Allow-Headers' 'Accept,Authorization,Cache-Control,Content-Type,DNT,If-Modified-Since,Keep-Alive,Origin,User-Agent,X-Mx-ReqToken,X-Requested-With';
                    }
                }
                location @dynamic_request {
                    passenger_enabled on;
                }
        
                ### END your own configuration options ###
            }
            passenger_pre_start <%= listen_url(app) %>;
            <% end %>
        
            <%= include_passenger_internal_template('footer.erb', 4) %>
        }
        

        Procfile 应包含以下行:

        web: bundle exec passenger start -p $PORT --max-pool-size 3 --nginx-config-template config/nginx.conf.erb

        您还需要根据 Guapolo's answer 配置为资产提供服务的 Cloudfront CDN

        在给您带来 CORS 悲伤的发行版中,转到行为选项卡和新行为,选择资产的路径,即 /assets/icons.ttf 并根据上图将“Origin”列入白名单。

        您可能还需要在您的分发中,在无效选项卡中“使”旧的缓存资源“无效”,即从您的检查器中输入完整的资产和缓存名称,然后使之无效。处理完毕后,使用配置部署应用程序并重新启动 heroku。您需要打开检查器并“清空缓存并硬重新加载”页面。

        希望这行得通 - 听起来乘客配置不时发生变化,所以我们可能会发现这个中断,需要更新答案以反映新配置。

        【讨论】:

          【解决方案4】:

          不需要在 nginx 中设置 headers。 Rails 完成了这项工作。

          # initializers/cors.rb
          
          # frozen_string_literal: true
          
          # Be sure to restart your server when you modify this file.
          
          # Avoid CORS issues when API is called from the frontend app.
          # Handle Cross-Origin Resource Sharing (CORS) in order to accept cross-origin AJAX requests.
          
          # Read more: https://github.com/cyu/rack-cors
          
          Rails.application.config.middleware.insert_before 0, Rack::Cors do
            allow do
              origins '*'
          
              resource '*',
                       headers: :any,
                       methods: %i[get post put patch delete options head]
            end
          end
          
          
          # nginx.conf
          ...
          
          upstream rails_app {
              server 127.0.0.1:3000;
          }
          
          ...
          
          server {
            listen 80;
            server_name api.example.com;
          
            # send non-static file requests to the app server  
            location / {    
              try_files $uri @rails;
            }
          
            location @rails {
              proxy_set_header  X-Real-IP  $remote_addr;    
              proxy_set_header  X-Forwarded-For $proxy_add_x_forwarded_for;         
              proxy_set_header Host $http_host;    
              proxy_redirect off;    
              proxy_pass http://rails_app;  
            }
          
          }
          

          【讨论】:

            【解决方案5】:
            猜你喜欢
            • 1970-01-01
            • 2018-09-19
            • 2013-06-13
            • 2016-09-30
            • 2018-04-12
            • 1970-01-01
            • 2013-10-28
            • 2014-03-24
            • 2019-02-15
            相关资源
            最近更新 更多