Rails 4: Why are fonts not loading in production?

Works with Rails >= 5 && sprockets >= 4:

src: url('Walkway_Bold-webfont.eot');

Works only in development because in development rails serves all app/assets folder directly without pre-compilation (but not in production that's why rails can't find the fonts), so you can access what's inside app/assets folder with just the url url.

In production you need the pre-compiled version of these files. Rails precompile these files to public/assets. To access these files, you need to use sprockets helpers: font-url or the more generic helper asset-url.

src: font-url('Walkway_Bold-webfont.eot');

In addition, all folders under app/assets will be pre-compiled automatically, you don't need to add fonts folder to sprockets search path.

You can check search path with: Rails.application.config.assets.paths in rails console.


Here is a generic way how to troubleshoot this problem for any library.

Reproduce error on local

  1. Run rails server on production

    1. On terminal rake assets:precompile
    2. This on config/environments/production.rb

        # Rails 4 production
        # config.serve_static_files = true
        # Rails 5, Uncomment to run production on local
        # config.log_level = :debug
        config.public_file_server.enabled = true
      
    3. On terminal RAILS_ENV=production rails s
  2. You should see error on web browser

Override font family

  1. application.css needs to be renamed to application.scss since asset-url will be used
  2. Add fonts to precompile on file config/initializers/assets.rb:

    Rails.application.config.assets.precompile << /\.(?:svg|eot|woff|ttf)$/
    
  3. Look for font face definition inside your CSS library and copy that to application.scss. It should be something like:

    @font-face {
      font-family: 'my-library';
      src: url('../fonts/my-library.eot');
      src:
        url('../fonts/my-library.eot?#iefix') format('embedded-opentype'),
        url('../fonts/my-library.woff2') format('woff2'),
        url('../fonts/my-library.ttf') format('truetype'),
        url('../fonts/my-library.woff') format('woff'),
        url('../fonts/my-library.svg?#my-library') format('svg');
      font-weight: normal;
      font-style: normal;
    }
    
  4. Change to:

    @font-face {
      font-family: 'my-library';
      src: asset-url('my-library/fonts/my-library.eot');
      src:
        asset-url('my-library/fonts/my-library.eot?#iefix') format('embedded-opentype'),
        asset-url('my-library/fonts/my-library.woff2') format('woff2'),
        asset-url('my-library/fonts/my-library.ttf') format('truetype'),
        asset-url('my-library/fonts/my-library.woff') format('woff'),
        asset-url('my-library/fonts/my-library.svg?#my-library') format('svg');
      font-weight: normal;
      font-style: normal;
    }
    

What changes were made

  1. Use asset-url instead of url
  2. Change ../fonts or similar to a path that asset-url understands.

What path to put on assets-url

To know which path asset-url understands, go to rails console and enter Rails.application.assets.paths. You will get something like:

[
  '/path/1',
  '/path/2',
  '/path/3',
]

If your fonts are on /path/2/my-library/fonts/ then use asset-url('my-library/fonts/my-library.eot').

i.e. You remove the path that part that you found on Rails.application.assets.paths.

Check you wrote the right path

On Rails console on development:

helper.asset_url('my-library/fonts/my-library.eot')

Should return:

"/assets/my-library/fonts/my-library-2517b97e2c0e1e6c8ceb9dd007015f897926bc504154137281eec4c1a9f9bdc9.eot"

Note the trailing /assets/ and the digest in the last part.


We had this problem last week - the problem is that your assets will be compiled to have MD5 hashes on them, whilst your standard CSS will still be looking for their "standard" names. This is a problem with images & fonts.

@font-face {
    font-family: 'akagi';
    src: asset_url('fonts/akagi-th-webfont.eot');
    src: asset_url('fonts/akagi-th-webfont.eot?#iefix') format('embedded-opentype'),
         asset_url('fonts/akagi-th-webfont.woff') format('woff'),
         asset_url('fonts/akagi-th-webfont.ttf') format('truetype'),
         asset_url('fonts/akagi-th-webfont.svg#akagithin') format('svg');
    font-weight: 300;
    font-style: normal;
}

This is an example of how you should use scss files to load assets dynamically. These files are compiled (either before push or during init) into your .css files, all with their assets correctly synced.

We had a similar problem to you with Heroku, and managed to get it working by putting our files into /stylesheets/layout/fonts.css.scss and then calling

@import '/layout/fonts';

We also called our application.css -> application.css.scss to support the @import function