Rails 3, @font-face failing in production with fir

2019-01-31 08:29发布

I'm using font-awesome in a rails 3 app, and everything is ok in development mode, but when I push to Heroku, Firefox fails to render the icons, and instead, I see this:

enter image description here

  • Chrome renders the icons fine in development and production
  • This just affects FireFox (although I've not tried IE)
  • The app is here, I'd appreciate if someone could confirm that this is not just happening on my machine (to help me rule out a localhost caching issue).
  • All assets, including fonts and stylesheets, are hosted on S3, using the asset_sync gem.

Here's what I've done:

Added the following to the top of font-awesome.css.scss:**

// font-awesome.css.scss
@font-face {
  font-family: 'FontAwesome';
  src: font-url("fontawesome-webfont.eot");
  src: font-url("fontawesome-webfont.eot?#iefix") format("eot"),
       font-url("fontawesome-webfont.woff") format("woff"),
       font-url("fontawesome-webfont.ttf") format("truetype"),
       font-url("fontawesome-webfont.svg#FontAwesome") format("svg");
  font-weight: normal;
  font-style: normal;
}

Then I put this in application.rb:

# application.rb
config.assets.paths << Rails.root.join("app", "assets", "fonts")
config.assets.precompile += %w( .svg .eot .woff .ttf )

Finaly I placed all 4 font files in app/assets/fonts.

I would really like to know what I'm doing wrong here.

7条回答
淡お忘
2楼-- · 2019-01-31 08:46

This is the configuration I added to my bucket in AWS management console to configure this cross thing:

Log in to AWS -> AWS Management Console -> S3 -> Find your Bucket -> Push properties button (magnifying glass on paper for some reason) -> Clic PERMISSIONS on the right -> "Edit CORS configuration"

<CORSConfiguration>
    <CORSRule>
        <AllowedOrigin>*</AllowedOrigin>
        <AllowedMethod>GET</AllowedMethod>
        <MaxAgeSeconds>3000</MaxAgeSeconds>
        <AllowedHeader>Content-*</AllowedHeader>
        <AllowedHeader>Host</AllowedHeader>
    </CORSRule>
</CORSConfiguration>

After two hours of researches... :-(

查看更多
3楼-- · 2019-01-31 08:47

There's an update to this thread. It seems that it's not possible to set CORS by uploading cors.xml file to a bucket. Now you have to CLICK it ;). This thread saved me some time while looking for a solution, but on the other hand I lost some time uploading and changing cors.xml file.

The current solution is to click on a bucket's properties > Permissons > and then click Add CORS Configuration

查看更多
戒情不戒烟
4楼-- · 2019-01-31 08:48

Amazon S3 now support CORS, you're no longer forced to embed BASE64 fonts in your css (it will save you some bandwidth :)

http://aws.amazon.com/about-aws/whats-new/2012/08/31/amazon-s3-announces-cross-origin-resource-sharing-CORS-support/

查看更多
你好瞎i
5楼-- · 2019-01-31 08:54
我想做一个坏孩纸
6楼-- · 2019-01-31 08:56

You can also use some rack middleware to serve the fonts directly with the required access-control headers to cloudfront.

# config/environment/production.rb

  # Rack Headers
  # Set HTTP Headers on static assets

  config.assets.header_rules = {
    :global => {'Cache-Control' => 'public, max-age=31536000'},
    :fonts  => {'Access-Control-Allow-Origin' => '*'}
  }
  require 'rack_headers'
  config.middleware.insert_before '::ActionDispatch::Static', '::Rack::Headers'

-----

# lib/rack_headers.rb

require 'rack/utils'

module Rack
  class Headers

    def initialize(app, options={})
      @app = app

      default_path = Rails.application.config.assets.prefix || '/assets'
      @asset_path = options.fetch(:path, default_path)

      default_rules = Rails.application.config.assets.header_rules || {}
      @rules = options.fetch(:header_rules, default_rules)
    end

    def call(env)
      dup._call(env)
    end

    def _call(env)
      status, @headers, response = @app.call(env)
      @path = ::Rack::Utils.unescape(env['PATH_INFO'])

      if @path.start_with?(@asset_path)
        set_headers
      end

      [status, @headers, response]
    end

    def set_headers
      @rules.each do |rule, headers|
        case rule
        when :global # Global
          set_header(headers)
        when :fonts  # Fonts Shortcut
          set_header(headers) if @path.match %r{\.(?:ttf|otf|eot|woff|svg)\z}
        when Array   # Extension/Extensions
          extensions = rule.join('|')
          set_header(result) if @path.match %r{\.(#{extensions})\z}
        when String  # Folder
          set_header(result) if
            (@path.start_with? rule || @path.start_with?('/' + rule))
        when Regexp  # Flexible Regexp
          set_header(result) if @path.match rule
        else
        end
      end
    end

    def set_header(headers)
      headers.each { |field, content| @headers[field] = content }
    end
  end
end

-----

This solution uses rules for setting different headers on every file based on rules. The rules are described here: https://github.com/thomasklemm/butler#providing-rules-for-setting-http-headers. Basically you can do anything with Regexps, but there are shortcuts for file endings, folders, web fonts and global headers.

查看更多
7楼-- · 2019-01-31 09:10

I fixed my problem.

From this article, I learned that:

Firefox rejects all cross-site font requests unless some specific headers are set:

[i.e. Access-Control-Allow-Origin]

And, from this article:

Sadly, right now S3 doesn’t allow you to specify the Access-Control-Allow-Origin header that your objects get served with

So you have a few options:

  1. Serve the fonts from your app's public folder, not from S3
  2. Serve the fonts from Rackspace, where you can set the headers
  3. Embed the font in your as a Base64 string

I've gone with the first option since this is gonna be a low traffic site, but here's a nice write-up on how to serve fonts from Rackspace whilst simultaneously serving all other assets from S3.


UPDATE:

Amazon announced yesterday that they now support Cross Origin Resource Sharing (CORS), so the solution posted above should no longer be necessary. Their developer guide explains more.

查看更多
登录 后发表回答