Rails: Asset Pipeline: production 環境で、CSS で指定している画像が表示されない問題について
Rails の Asset Pipeline を使っていると、
development 環境では動作していたのに、production 環境で画像が表示されない、
アセット(CSS、JS)が正しく取得できないという問題が起きることがある。
この問題は、いろいろとバリエーションがあるのだが、
今回は CSS 上で使用している画像が表示されないケースについて説明しようと思う。
想定環境は次のとおり。
環境 | バージョン |
---|---|
Ruby | 2.0.0p598 |
Ruby on Rails | 4.1.9 |
nginx | 1.6.2 |
unicorn | 4.8.3 |
次のような設定で、production 環境を起動しているとする。
config.serve_static_assets = false # アセットの配信は、Nginx が担当する config.assets.digest = true # フィンガープリントあり
原因1:CSS にアセットのファイル名を直接記入している
Rails では「config.assets.digest = true」とした場合、アセットにフィンガープリントが付与される。
たとえば「image.png」という画像の場合、
production 環境では 次のようなファイル名に変換される。
image-908e25f4bf641868d8683022a5b62f54.png
このハッシュ値は、assets:precompile 時に動的に決定される。
そのため、CSS で次のような書き方をしている箇所は、画像は表示できない。
/* global.css.scss */ background-image: url("image.png");
これについては「アセットパイプライン — Rails ガイド」が詳しい。
原因2:image-url(), image-path() を使っている
CSS 上で、画像パスを下記のように記述しているとする。
/* global.css.scss */ background-image: image-url("image.png");
この image-url は css の機能ではなく、Asset Pipeline 機能である。
image-url で記述された箇所は、次のように変換される。
background-image: image-url("image.png"); ↓↓(precomiple)↓↓ background-image: url("/assets/image.png");
「原因1」でも説明したが、アセットにフィンガープリントを付与している場合は、
image-url, image-path では対応できないようだ。
同じ問題に悩まされている人がいる。
ruby on rails - Rails4: image_url not generating digest in scss - Stack Overflow
image-url('image.jpg'); -> http://www.mydomain.it/images/image.jpg
image_url('image.jpg'); -> same as above
url(image-path('header.jpg')); -> http://www.mydomain.it/images/image.jpg
asset-url('image.jpg', image); -> http://www.mydomain.it/image.jpg
problem still remains: assets are compiled but requested without digest.
一方、こちらの記事には image-url でフィンガープリントまで対応できると書いてあるが、
これは、何かの間違いだろう。
Rails の Asset Pipeline 有効ではまった箇所 | EasyRamble
background-image: image-url("logo.png");
こうすると、ダイジェスト値を付加したURLを指定してくれて、正常に動作する。
解決方法
先ほど紹介した記事にもあったが、個人的にはこの方法がベストな気がしている。
ruby on rails - Rails4: image_url not generating digest in scss - Stack Overflow
Should use asset_path. Also, it needs to run under ERB tag, as SCSS does not compile asset_path. Rename common.scss to common.scss.erb
.body { background-image: url(<%= asset_path 'bg.jpg' %>) }
具体的には、先ほどのコードをこのようにする。
/* global.css.scss.erb */ background-image: url(<%= asset_path "image.png" %>);
一度 ERB で解釈する必要が有るため、拡張子に「erb」を付けなければならないのが、
ちょっとカッコ悪いが。。
その他の方法
下記の記事には、その他の方法がいくつか提案されている。
Rails4ではbackground:url("assets/hoge.png")の書き方は動かない話 - Qiita
余談
image-url で、ハッシュまで付与してくれれば、一番スマートなんですけどね。。