Webサイトにアクセスすると、コンソールから次のエラーメッセージが表示され続けます。
font from Origin 'https://xxx.cloudfront.net' has been blocked from loading by Cross-Origin Resource Sharing policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'https://www.example.com' is therefore not allowed access.
私はすべてを試しました:
application.rbファイルを構成しました
config.font_assets.Origin = 'http://example.com'
この記事 で説明されているCloudfrontのホワイトリストに登録されたヘッダー
Access-Control-Allow-Origin
Access-Control-Allow-Methods
Access-Control-Allow-Headers
Access-Control-Max-Age
しかし、何も、ゼロ、灘。
HerokuでRails 4.1を使用しています。
これは、次の2つの理由から、対処するのが非常に難しい問題でした。
CloudFrontがミラーリングであるという事実は、Railsアプリの応答ヘッダーであるため、気をひねる必要があります。 CORSプロトコルは、そのままでは理解するのが難しいですが、ブラウザーとCloudFrontの間(RailsアプリがCDNとして使用する場合)とブラウザーの間の2つのレベルで従う必要があります。 Railsアプリ(悪意のあるサイトが私たちを悪用したい場合)。
CORSは、実際には、ブラウザとWebページがアクセスしたいサードパーティのリソースとの間のダイアログに関するものです。 (私たちのユースケースでは、それはCloudFront CDNであり、アプリのアセットを提供します。)しかし、CloudFrontはAccess-Control応答ヘッダーを取得するためfromアプリなので、アプリはそれらのヘッダーを提供する必要がありますあたかも CloudFrontが話しているので、同時にそもそも同一生成元ポリシー/ CORSの開発につながるタイプの悪用にさらされるようなアクセス許可を付与しないでください。特に、当サイトの*
リソースへの*
アクセスを許可しないでください。
私はそこに非常に多くの古い情報を見つけました-ブログ投稿とSOスレッドの無限の行。 CloudFrontは、これらの投稿の多くからCORSサポートを大幅に改善しましたが、まだ完全ではありません。 (CORSは実際にはすぐに処理する必要があります。)そして、宝石自体は進化しました。
私のセットアップ:Rails 4.1.15はHerokuで実行され、アセットはCloudFrontから提供されます。私のアプリは、両方の「www」でhttpとhttpsの両方に応答します。リダイレクトを行わずに、ゾーンの頂点。
質問で言及されているfont_assetsgemを簡単に調べましたが、すぐにそれを削除して、より適切に思われるラックコアを採用しました。 CORSのポイントと同一生成元ポリシーのセキュリティを損なうため、すべてのオリジンとすべてのパスを単純に開くことはしたくありませんでした。そのため、許可するいくつかのオリジンを指定できる必要がありました。最後に、私は個人的に、標準の構成ファイル(config/initializers/*.rb
やconfig.ru
など)を編集するのではなく、個々のconfig/application.rb
ファイルを介してRailsを構成することを好みます。 2016年4月16日現在、利用可能な最善のソリューションであると私は信じています。
Gemfile
gem "rack-cors"
Rack-cors gemは、RackミドルウェアにCORSプロトコルを実装します。承認されたオリジンにAccess-Control-Allow-Originと関連ヘッダーを設定することに加えて、Vary: Origin
応答ヘッダーを追加し、CloudFrontに各オリジンの応答(応答ヘッダーを含む)を個別にキャッシュするように指示します。これは、複数のオリジンを介して(たとえば、httpとhttpsの両方を介して、および「www。」とベアドメインの両方を介して)サイトにアクセスできる場合に重要です。
config/initializers/Rack-cors.rb
## Configure Rack CORS Middleware, so that CloudFront can serve our assets.
## See https://github.com/cyu/rack-cors
if defined? Rack::Cors
Rails.configuration.middleware.insert_before 0, Rack::Cors do
allow do
origins %w[
https://example.com
http://example.com
https://www.example.com
http://www.example.com
https://example-staging.herokuapp.com
http://example-staging.herokuapp.com
]
resource '/assets/*'
end
end
end
これは、ブラウザに、Railsアプリ(および拡張すると、CloudFrontはミラーリングしているため)のリソースにアクセスできるのは、Railsアプリ(および- not悪意のある-site.comに代わって)、/assets/
urlの場合のみ(およびnotコントローラーの場合)。つまり、CloudFrontがアセットを提供できるようにしますが、必要以上にドアを開けないでください。
ノート:
起源リストは正規表現としても実行できます。文字列の終わりにパターンを固定するように注意してください。
origins [
/\Ahttps?:\/\/(www\.)?example\.com\z/,
/\Ahttps?:\/\/example-staging\.herokuapp\.com\z/
]
しかし、リテラル文字列を読むだけの方が簡単だと思います。
ブラウザのOriginリクエストヘッダーをRailsアプリに渡すようにCloudFrontを設定します。
不思議なことに、CloudFrontはOriginヘッダーをブラウザーからRailsアプリに転送するようです関係なくここに追加するかどうかに関係なく、CloudFrontはアプリのVary: Origin
キャッシングディレクティブを尊重しますOriginがヘッダーホワイトリストに明示的に追加されている場合のみ(2016年4月現在)。
リクエストヘッダーのホワイトリストは一種の埋もれています。
ディストリビューションがすでに存在する場合は、次の場所で見つけることができます。
ディストリビューションをまだ作成していない場合は、次の場所で作成してください。
[配布の作成]をクリックします
(完全性と再現性のために、デフォルトから変更したすべての設定をリストしていますが、この説明に関連するのはホワイトリスト設定のみです)
配信方法:Web(RTMPではない)
オリジン設定
デフォルトのキャッシュ動作設定
これらすべてを変更した後、キャッシュされた古い値がCloudFrontから期限切れになるまでに時間がかかる場合があることに注意してください。 CloudFrontディストリビューションの[Invalidations]タブに移動し、*
の無効化を作成することで、キャッシュされたアセットを明示的に無効化できます。
乗客とHerokuでRailsを実行する場合:(そうでない場合は、Noach Magedmanの回答に直接ジャンプします)
Noach Magedmanの回答は、CloudFrontを適切にセットアップするのに非常に役立ちました。
また、説明どおりにrack-cors
をインストールしました。開発では正常に機能しましたが、本番環境のCURLコマンドはCORS構成を返しませんでした。
curl -H "Origin: https://tidyme-staging.com.au" -I http://tidyme-staging.com.au/assets/31907B_4_0-588bd4e720d4008295dcfb85ef36b233ee0817d7fe23c76a3a543ebba8e7c85a.ttf
HTTP/1.1 200 OK
Connection: keep-alive
Server: nginx/1.10.0
Date: Wed, 03 Aug 2016 00:29:37 GMT
Content-Type: application/x-font-ttf
Content-Length: 316664
Last-Modified: Fri, 22 Jul 2016 03:31:57 GMT
Expires: Thu, 31 Dec 2037 23:55:55 GMT
Cache-Control: max-age=315360000
Cache-Control: public
Accept-Ranges: bytes
Via: 1.1 vegur
CDNを経由せずにサーバーに直接pingを実行すると、CDNはすべてのコンテンツを無効にした後、サーバーが応答するものをすべて転送する必要があることに注意してください。ここで重要な行はServer: nginx/1.10.0
です。これは、アセットがRailsではなくnginxによって提供されることを示します。結果として、rack-cors
構成は適用されません。
私たちのために働いた解決策はここにあります: http://monksealsoftware.com/Ruby-on-Rails-cors-heroku-passenger-5-0-28/
基本的に、Passengerのnginx構成ファイルのクローンを作成して変更する必要がありましたが、Passengerがアップグレードされてテンプレートが変更されるたびにこのコピーを維持する必要があるため、理想的ではありません。
===
ここに要約があります:
Railsプロジェクトのルートフォルダーに移動し、nginx構成テンプレートのコピーを作成します
cp $(passenger-config about resourcesdir)/templates/standalone/config.erb config/passenger_config.erb
config/passenger_config.erb
を開き、この行をコメントアウトします
<%# include_passenger_internal_template('Rails_asset_pipeline.erb', 8, false) %>
上記の行の下にこれらの構成を追加します
### 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
location ~ "^/assets/.+\.(woff|eot|svg|ttf|otf).*" {
error_page 490 = @static_asset_fonts;
error_page 491 = @dynamic_request;
recursive_error_pages on;
if (-f $request_filename) {
return 490;
}
if (!-f $request_filename) {
return 491;
}
}
# 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 "";
}
location @static_asset_fonts {
gzip_static on;
expires max;
add_header Cache-Control public;
add_header ETag "";
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Methods' 'GET, HEAD, OPTIONS';
add_header 'Access-Control-Allow-Headers' '*';
add_header 'Access-Control-Max-Age' 3628800;
}
location @dynamic_request {
passenger_enabled on;
}
### END your own configuration options ###
Procfile
を変更して、このカスタム構成ファイルを含めます
web: bundle exec passenger start -p $PORT --max-pool-size 2 --nginx-config-template ./config/passenger_config.erb
次に展開します...
===
より良い解決策を知っている場合は、コメントを入力してください。
実装後、CURLコマンドは次の応答を返しました。
curl -H "Origin: https://tidyme-staging.com.au" -I http://tidyme-staging.com.au/assets/31907B_4_0-588bd4e720d4008295dcfb85ef36b233ee0817d7fe23c76a3a543ebba8e7c85a.ttf
HTTP/1.1 200 OK
Connection: keep-alive
Server: nginx/1.10.0
Date: Wed, 03 Aug 2016 01:43:48 GMT
Content-Type: application/x-font-ttf
Content-Length: 316664
Last-Modified: Fri, 22 Jul 2016 03:31:57 GMT
Expires: Thu, 31 Dec 2037 23:55:55 GMT
Cache-Control: max-age=315360000
Cache-Control: public
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, HEAD, OPTIONS
Access-Control-Allow-Headers: *
Access-Control-Max-Age: 3628800
Accept-Ranges: bytes
Via: 1.1 vegur
私はちょうど同じ問題を抱えていて、なんとかそれを解決することができました。
これらのヘッダーを許可するようにCloudfrontに正しく指示しましたが、Cloudfrontがフォントを取得する場所にこれらのヘッダーを追加していません。はい、Originヘッダーは許可されていますが、Herokuはそれらのヘッダーをフォントとともに送信していません。
これを修正するには、Herokuのフォントに適切なCORSヘッダーを追加する必要があります。幸いなことに、これは非常に簡単です。
まず、rack/cors
gemをプロジェクトに追加します。 https://github.com/cyu/rack-cors
次に、Rackサーバーを構成して、提供するアセットのCORSをロードおよび構成します。アプリケーションがconfig.ru
にプリロードされた後、以下を追加します
require 'rack/cors'
use Rack::Cors do
allow do
origins '*'
resource '/cors',
:headers => :any,
:methods => [:post],
:credentials => true,
:max_age => 0
resource '*',
:headers => :any,
:methods => [:get, :post, :delete, :put, :patch, :options, :head],
:max_age => 0
end
end
これにより、Herokuから返されるリソースが設定され、適切なCORSヘッダーが適用されます。ファイルとセキュリティのニーズに応じて、ヘッダーの適用を制限できます。
デプロイしたら、Cloudfrontに移動し、以前にCORSパーミッションエラーを発生させていたものに対して無効化を開始します。これで、CloudfrontがHerokuから新しいコピーをロードすると、正しいヘッダーが設定され、Cloudfrontは、Origin
権限で以前に設定されたように、それらのヘッダーをクライアントに渡します。
サーバーから適切なヘッダーを提供していることを確認するには、次のcurlコマンドを使用してヘッダーを検証します。curl -I -s -X GET -H "Origin: www.yoursite.com" http://www.yoursite.dev:5000/assets/fonts/myfont.svg
次のヘッダーが返されるはずです。
Access-Control-Allow-Origin: www.yoursite.com
Access-Control-Allow-Methods: GET, POST, DELETE, PUT, PATCH, OPTIONS, HEAD
Access-Control-Max-Age: 0
Access-Control-Allow-Credentials: true
バージョン5.0以降、RailsではアセットのカスタムHTTPヘッダーを設定でき、rack-corsまたはfont-assetsgemを使用する必要はありません。Access-Control-Allowを設定するために-アセット(フォントを含む)のオリジン。次のコードをconfig/environment /production.rbに追加するだけです。
config.public_file_server.headers = {
'Access-Control-Allow-Origin' => '*'
}
ヘッダー値は、次のように特定のドメインにすることもできます。
config.public_file_server.headers = {
'Access-Control-Allow-Origin' => 'https://www.example.org'
}
これは私のアプリで機能し、Cloudfrontの設定を変更する必要はありませんでした。
これは、Herokuで動作するRails 5.2でカスタムフォントを提供するデモです。さらに進んで、 https:/)に従ってフォントの提供を可能な限り高速にするように最適化します。 /www.webpagetest.org/
https://github.com/nzoschke/edgecors
app/assets/fonts
に配置します@font-face
宣言をscssファイルに配置し、font-url
ヘルパーを使用しますapp/assets/stylesheets/welcome.scss
から:
@font-face {
font-family: 'Inconsolata';
src: font-url('Inconsolata-Regular.ttf') format('truetype');
font-weight: normal;
font-style: normal;
}
body {
font-family: "Inconsolata";
font-weight: bold;
}
Heroku Edgeアドオン で追加されたCloudFrontを使用しています。
独自のCloudFrontを使用している場合は、ブラウザーのOrigin
ヘッダーをバックエンドのオリジンに転送するように設定してください。
まず、CDNプレフィックスとデフォルトのCache-Control
ヘッダーをproduction.rb
に設定します。
Rails.application.configure do
# e.g. https://d1unsc88mkka3m.cloudfront.net
config.action_controller.asset_Host = ENV["Edge_URL"]
config.public_file_server.headers = {
'Cache-Control' => 'public, max-age=31536000'
}
end
Herokuapp.comURLからCDNURLにフォントにアクセスしようとすると、ブラウザでCORSエラーが発生します。
' https://d1unsc88mkka3m.cloudfront.net/assets/Inconsolata-Regular.ttf ' from Origin ' https://edgecors.herokuapp.com 'のフォントへのアクセスCORSポリシーによってブロックされました:「Access-Control-Allow-Origin」ヘッダーがリクエストされたリソースに存在しません。 edgecors.herokuapp.com/ GET https://d1unsc88mkka3m.cloudfront.net/assets/Inconsolata-Regular.ttf net :: ERR_FAILED
したがって、HerokuからCDNURLへのフォントへのアクセスを許可するようにCORSを構成します。
module EdgeCors
class Application < Rails::Application
# Initialize configuration defaults for originally generated Rails version.
config.load_defaults 5.2
config.middleware.insert_after ActionDispatch::Static, Rack::Deflater
config.middleware.insert_before 0, Rack::Cors do
allow do
origins %w[
http://edgecors.herokuapp.com
https://edgecors.herokuapp.com
]
resource "*", headers: :any, methods: [:get, :post, :options]
end
end
end
end
アセットパイプラインは.ttf.gz
ファイルを作成しますが、それを提供しません。このモンキーパッチは、アセットパイプラインのgzipホワイトリストをブラックリストに変更します。
require 'action_dispatch/middleware/static'
ActionDispatch::FileHandler.class_eval do
private
def gzip_file_path(path)
return false if ['image/png', 'image/jpeg', 'image/gif'].include? content_type(path)
gzip_path = "#{path}.gz"
if File.exist?(File.join(@root, ::Rack::Utils.unescape_path(gzip_path)))
gzip_path
else
false
end
end
end
最終的な結果は、長寿命のCloudFrontキャッシュから提供されるapp/assets/fonts
のカスタムフォントファイルです。