Using fonts with Rails asset pipeline

前端 未结 12 1803
慢半拍i
慢半拍i 2020-11-22 14:52

I have some fonts being configured in my Scss file like so:

@font-face {
  font-family: \'Icomoon\';
  src: asset-url(\'icoMoon.eot?#iefix\', font) format(\'         


        
12条回答
  •  野趣味
    野趣味 (楼主)
    2020-11-22 15:15

    Here is a repo the demonstrates serving a custom font with Rails 5.2 that works on Heroku. It goes further and optimizes serving the fonts to be as fast as possible according to https://www.webpagetest.org/

    https://github.com/nzoschke/edgecors

    To start I picked pieces from answers above. For Rails 5.2+ you shouldn't need extra asset pipeline config.

    Asset Pipeline and SCSS

    • Place fonts in app/assets/fonts
    • Place the @font-face declaration in an scss file and use the font-url helper

    From 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;
    }
    

    Serve from CDN with CORS

    I'm using CloudFront, added with the Heroku Edge addon.

    First configure a CDN prefix and default Cache-Control headers in 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
    

    If you try to access the font from the herokuapp.com URL to the CDN URL, you will get a CORS error in your browser:

    Access to font at 'https://d1unsc88mkka3m.cloudfront.net/assets/Inconsolata-Regular.ttf' from origin 'https://edgecors.herokuapp.com' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. edgecors.herokuapp.com/ GET https://d1unsc88mkka3m.cloudfront.net/assets/Inconsolata-Regular.ttf net::ERR_FAILED

    So configure CORS to allow access to the font from Heroku to the CDN URL:

    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
    

    Serve gzip Font Asset

    The asset pipeline builds a .ttf.gz file but doesn't serve it. This monkey patch changes the asset pipeline gzip whitelist to a blacklist:

    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
    

    The ultimate result is a custom font file in app/assets/fonts served from a long-lived CloudFront cache.

提交回复
热议问题