bin/rails assets:clobber は、assets:precompile が作った public/assets をディレクトリごと削除するタスクです。jsbundling-rails や tailwindcss-rails を入れているアプリでは、app/assets/builds のビルド結果も一緒に消えます。古い世代だけを整理する assets:clean とは削除範囲がまったく違い、取り違えると本番でCSSが当たらなくなります。この記事では、Rails 8.1.4・Propshaft 1.3.2 の環境で3つのタスクを実際に動かした結果と、Propshaft と Sprockets のソースコードをもとに、消える範囲・残る範囲・使ってよい場面を整理します。
まとめ:clobber・clean・precompileの要点
- clobber は全削除:出力先(既定は
public/assets)を.manifest.jsonごと消す。Sprockets はキャッシュ(tmp/cache/assets)も消す。jsbundling-rails・cssbundling-rails・tailwindcss-rails・dartsass-rails は clobber を拡張していて、app/assets/buildsの該当拡張子のファイルも消える。 - clean は世代整理:Sprockets は「現行版+古い版2つ+1時間以内のファイル」を残す。Propshaft 1.2.0 以降(最新の1.3.2を含む)は現行版を別枠にできておらず、残るのは「更新時刻が新しい順に2つ+1時間以内のファイル」。
- Propshaft 1.2.0 以降の clean は現行ファイルを消すことがある:以前の版へ戻して precompile した直後に clean すると、マニフェストが指すファイルが消える。修正のプルリクエスト #279 は2026年10月2日にマージされないまま閉じられた。
- precompile は生成:本番向けに作り直すなら clobber の後に precompile する(development で precompile を取り消すために clobber したときは、precompile し直さない)。Propshaft では
config.assets.precompileやconfig.assets.compileは効かない(読み込みパス配下の全ファイルが対象)。 - clobber を使う場面:development で precompile してしまい変更が反映されないとき、パイプラインを入れ替えたとき、ビルド結果を一から作り直したいとき。稼働中の本番サーバーで直接叩く用途ではない。
rails assets:clobberが消すもの
clobber の削除範囲は、アセットパイプラインの種類と、追加しているバンドラ系gemで変わります。
Propshaft:出力先ディレクトリを丸ごと削除
Rails 8.0(2024年11月7日公開)以降の新規アプリは Propshaft が既定です。Propshaft 1.3.2 の Processor の clobber は FileUtils.rm_r(output_path) の1行だけで、config.assets.output_path(既定は public/assets)を中身ごと消します。マニフェストの置き場所 config.assets.manifest_path の既定値は出力先の中の .manifest.json なので、マニフェストも一緒に消えます。Propshaft は変換結果のキャッシュを持たないため、消えるのはこのディレクトリだけです。
$ bin/rails -T assets
bin/rails assets:clean[count] # Removes old files in config.assets.output_path
bin/rails assets:clobber # Remove config.assets.output_path
bin/rails assets:precompile # Compile all the assets from config.assets.paths
bin/rails assets:reveal # Print all the assets available in config.assets.paths
bin/rails assets:reveal:full # Print the full path of assets available in config.assets.paths
$ RAILS_ENV=production bin/rails assets:clobber
$ ls public | grep assets
(何も出ない=public/assets ごと消えている)
Sprockets:出力先に加えてtmp/cache/assetsも削除
Rails 7.x 以前から使い続けているアプリは sprockets-rails 構成のことが多いはずです。Sprockets 4.4.1 の Manifest#clobber は出力先ディレクトリを削除したあと、environment.cache.clear で変換キャッシュも空にします。sprockets-rails 3.5.2 はこのキャッシュを tmp/cache/assets に置くので、clobber の後の初回 precompile は SCSS の変換などをすべてやり直し、通常より時間がかかります。マニフェストのファイル名は .sprockets-manifest-<ランダムな16進>.json です。
jsbundling・cssbundling・tailwindcss・dartsassはapp/assets/buildsも削除
esbuild や Bun でJavaScriptをまとめる jsbundling-rails、CSSを作る cssbundling-rails などのgemは、assets:precompile の前にビルドを差し込むのと同時に、assets:clobber にも削除タスクをつなげています。jsbundling-rails 1.3.1 の clobber.rake は Rake::Task["assets:clobber"].enhance(["javascript:clobber"]) で、app/assets/builds の .js と .js.map を消します。各gemが消す対象は次のとおりです。
| gem(版) | clobberに足すタスク | 消すファイル |
|---|---|---|
| jsbundling-rails 1.3.1 | javascript:clobber | app/assets/builds 配下の .js・.js.map(サブディレクトリ含む) |
| cssbundling-rails 1.4.3 | css:clobber | app/assets/builds 配下の .css・.css.map(サブディレクトリ含む) |
| tailwindcss-rails 4.7.0 | tailwindcss:clobber | app/assets/builds 直下の .css |
| dartsass-rails 0.5.1 | dartsass:clobber | app/assets/builds 配下の .css・.css.map |
削除はパスと拡張子だけで決まり、gitで管理しているか、手書きのファイルかは見ません。.keep のようなドット始まりのファイルだけが残るので、app/assets/builds に手書きのCSSやJavaScriptを置かないでください。Propshaft 構成で clobber の直後に bin/rails server だけで起動し、消えたビルド結果を参照するページを開くと Propshaft::MissingAssetError になります。開発中は watch プロセス込みの bin/dev で起動してください。自前で assets:precompile を拡張して何かを生成しているなら、clobber 側にも削除タスクを足しておくとやり直しが1コマンドで済みます。
assets:clobber・clean・precompileの違い
| タスク | 動作 | 対象 | 主な用途 | 稼働中の本番で |
|---|---|---|---|---|
| assets:precompile | 生成 | digest付きファイル+マニフェスト | デプロイ時のビルド | 別のリリース・イメージで実行 |
| assets:clean | 世代整理 | 新しい版と1時間以内を残して削除(下記) | 出力先を使い回す環境の掃除 | 条件付き(下記) |
| assets:clobber | 全削除 | 出力先+マニフェスト(+キャッシュ・builds) | 作り直し | 不可(全部消える) |
assets:cleanが残す範囲:Sprocketsは3つ、Propshaft 1.2以降は2つ
clean に渡す既定値は Sprockets と Propshaft で同じですが、残る数は違います。Sprockets の Manifest#clean(count = 2, age = 3600) にはコメントで「By default it will keep the latest version, 2 backups and any created within the past hour.」と書かれていて、現行版を除いた古い版を新しい順に並べ、2つまでと1時間以内のものを残します。sprockets-rails の README にある「keeping the latest 3 copies」は、現行版1つと古い版2つを足した数です。
Propshaft の assets:clean も count = args.fetch(:count, 2) と clean(count, 1.hour) で同じ数を渡し、コードも現行版を除いてから数える作りです。ただし 1.2.0 以降は次の章の理由で現行版を除けていないため、現行版も含めて更新時刻が新しい順に2つしか残りません。CSSを5回書き換えたあと、全ファイルを2時間前の扱いにして clean すると、残ったのは2つでした。
見落としやすいのが「1時間以内は全部残す」の部分です。5回の precompile を続けた直後に clean を実行しても、1つも消えませんでした。
# Rails 8.1.4 + Propshaft 1.3.2。CSSを書き換えてprecompileを5回
$ ls public/assets | grep '^application-'
application-2ade8573.css
application-44f66c31.css
application-9103cbe7.css
application-d1f2df7a.css
application-f88073b3.css
$ RAILS_ENV=production bin/rails assets:clean
(何も削除されない=全ファイルが1時間以内)
デプロイ直後の clean でディスクが空かないのは不具合ではなく、この規則どおりの動きです。
clean[count]の指定とclean[0]で消えるもの
残す数は assets:clean[5] のように角括弧で渡します。zsh では角括弧がワイルドカード扱いになるので、bin/rails "assets:clean[5]" と引用符で囲みます。
assets:clean[0] は危険です。Propshaft 1.3.2 で1時間より古いファイルに対して実行すると、現行のCSSに加えて .manifest.json まで削除されました。Propshaft は出力先のファイルを全部「アセットの版」として扱うので、マニフェスト自身も整理の対象に入ります。0を渡すのは「全部消してよい」場面だけで、それなら clobber を使う方が意図がはっきりします。
$ RAILS_ENV=production bin/rails "assets:clean[0]"
Removed rails-ujs-20eaf715.js
Removed .manifest.json
Removed application-2ade8573.css
Removed application-f88073b3.css
Removed rails-ujs.esm-e925103b.js
Propshaft 1.2以降のassets:cleanが現行ファイルを消す条件
clean は本来、マニフェストが指している現行版を数に入れずに必ず残す設計で、Propshaft 1.1.0 まではマニフェストが文字列のハッシュだったのでそのとおりに動いていました。ところが Propshaft 1.2.0(2025年7月15日公開)でマニフェストの1件が文字列から ManifestEntry オブジェクトに変わったあとも、OutputPath#clean はファイルのパスと manifest[logical_path] をそのまま比べています。文字列とオブジェクトは一致しないので、現行版の保護が効いていません。現行版が残るのは、たまたま「新しい順に2つ」か「1時間以内」に入っているときだけです。
この問題を直す プルリクエスト #279 は2026年10月2日にマージされないまま閉じられました。最新版の 1.3.2(2026年4月17日公開)と、2026年10月27日時点の main ブランチ(コミット dc979db8)の output_path.rb は、どちらも path == current の比較のままです。
以前の版へ戻した直後のcleanで再現
現行版が「古い」と判定されるのは、以前と同じ内容に戻したときです。Propshaft の precompile は、同じdigestのファイルが出力先にあれば書き直さないので、戻した版のファイルは最初に作られた古い更新時刻のまま残ります。次の手順で再現しました。
# v1を作成(5時間前の扱い)→ v2・v3を作成(3時間前の扱い)
# → CSSをv1の内容に戻してprecompile → clean
$ RAILS_ENV=production bin/rails assets:precompile
(application-f88073b3.css は既にあるので書き直されない)
manifest points to: application-f88073b3.css
$ RAILS_ENV=production bin/rails assets:clean
Removed application-f88073b3.css
current file MISSING: application-f88073b3.css
この状態でページを開くと、マニフェストが指すCSSが404になります。影響を受けるのは「public/assets をビルド間で使い回し、precompile の後に clean を呼ぶ」構成です。Heroku の Ruby ビルドパックは precompile に成功すると assets:clean を実行し、public/assets をキャッシュに保存します。リバートでCSSを以前の内容に戻すデプロイがこの条件に当たります。
マニフェストとファイルの突き合わせ
clean を実行する構成では、clean の後にマニフェストが指すファイルが全部あるかを確かめる1行を足しておくと、404を出す前に気づけます。
$ ruby -rjson -e 'm = JSON.parse(File.read("public/assets/.manifest.json")); missing = m.values.map { |v| v.is_a?(String) ? v : v["digested_path"] }.reject { |p| File.exist?(File.join("public/assets", p)) }; puts(missing.empty? ? "OK" : "missing: #{missing.join(", ")}"); exit(missing.empty?)'
missing: application-f88073b3.css
消えていたら、assets:clobber → assets:precompile で作り直すのが確実です。Dockerイメージのように毎回まっさらな状態で precompile する構成なら古い版が溜まらないので、そもそも clean は不要です。
assets:clobberを使う場面と本番で叩いてはいけない理由
clobber の出番は生成物を一から作り直したいときで、ディスクを空けたいだけなら clean の担当です。
developmentでprecompileして変更が反映されないとき
Propshaft は出力先に .manifest.json があると、ソースを毎回読む動的な解決をやめ、マニフェストどおりに配信する静的な解決に切り替えます(Propshaft::Assembly#resolver)。development で一度でも precompile すると、その後CSSを書き換えても古いファイルを配り続けます。precompile 自体も「Rails will not serve any changed assets until you delete public/assets/.manifest.json」と警告を出します。
$ bin/rails assets:precompile
Warning: You are precompiling assets in development. Rails will not serve any changed assets until you delete public/assets/.manifest.json
# CSSを書き換えてもパスが変わらない
dev path(before edit): /assets/application-2c888b33.css
dev path(after edit): /assets/application-2c888b33.css
$ bin/rails assets:clobber
dev path(after clobber): /assets/application-e1d15ecd.css
上のログは bin/rails runner を毎回新しいプロセスで動かした結果です。起動中の開発サーバーは解決方法を選び直さない(Propshaft::Assembly が @resolver ||= で保持する)ので、clobber した後は bin/dev を再起動します。clobber の後に precompile し直すとマニフェストがまた作られるので、development では precompile しません。
パイプラインの入れ替えとビルドのやり直し
Sprockets から Propshaft へ移行すると、.sprockets-manifest-*.json と古いdigestのファイルが public/assets に残るので、clobber で消してから precompile し直します。Webpacker から jsbundling-rails や importmap-rails へ移ったときも同じです。Webpacker は Rails 7 の登場を受けて開発が終わり、リポジトリの README は jsbundling-rails か importmap-rails への移行を勧めています。
$ bin/rails assets:clobber
$ RAILS_ENV=production bin/rails assets:precompile
稼働中の本番でclobberを実行したときの影響
clobber を実行した瞬間から次の precompile が終わるまで、その環境には配信できるアセットが1つもありません。加えて Propshaft の静的な解決は、マニフェストを一度読むとプロセスのメモリに持ち続けます(Propshaft::Resolver::Static の @manifest ||=)。稼働中のPumaは古いマニフェストのdigest名でHTMLを返し続けます。ソースが同じなら precompile で同じ名前のファイルが作り直されて直りますが、ソースを変えていて新しいdigest名になった場合は、アプリを再起動するまでCSSは404のままです。
本番で作り直すときは、新しいリリースのディレクトリやイメージで precompile を済ませてから切り替えます。稼働中のサーバーで clobber を叩く運用や、cron での定期実行はやめてください。
assets:precompileが生成するものと反映されないときの確認
precompile は、読み込みパス(app/assets・lib/assets・vendor/assets とgemが足したパス)のファイルに内容のハッシュを付けて出力先へ書き出し、論理名と実ファイル名の対応表を作ります。ビューの stylesheet_link_tag や asset_path は、この対応表を見てdigest付きのURLを埋め込みます。
precompileの生成物と.manifest.jsonの中身
Propshaft 1.3.2 のマニフェストは、論理名ごとに digested_path と integrity を持つJSONです。integrity は Subresource Integrity 用で、ハッシュ方式を設定していなければ null になります。
$ RAILS_ENV=production bin/rails assets:precompile
Writing application-f88073b3.css
Writing rails-ujs.esm-e925103b.js
Writing rails-ujs-20eaf715.js
$ ruby -rjson -e 'puts JSON.parse(File.read("public/assets/.manifest.json"))["application.css"]'
{"digested_path" => "application-f88073b3.css", "integrity" => nil}
Rails 8.1.4 が生成する Dockerfile は、イメージのビルド中に RUN SECRET_KEY_BASE_DUMMY=1 ./bin/rails assets:precompile を実行します。SECRET_KEY_BASE_DUMMY を設定すると、Rails 8.1.4 は secret_key_base を環境変数や暗号化 credentials から読まず、一時的な値を生成して使います(Rails::Application::Configuration#secret_key_base)。production 環境を選んでいるのは、同じ Dockerfile の ENV RAILS_ENV="production" の行です。初期化処理で secret_key_base 以外の credentials を読んでいるアプリは、この指定だけではビルドが通りません。
Propshaftで効かないconfig.assets.precompileとcompile
Sprockets 時代の記事にある config.assets.precompile += %w( admin.js ) や config.assets.compile = false は、Propshaft では何もしません。Propshaft の railtie は、古い設定を書いたアプリが起動時に落ちないよう、これらを空の値で定義しているだけです。読み込みパス配下は全部 precompile されるので登録は要らず、外したいディレクトリは config.assets.excluded_paths に書きます。
| 設定 | Sprockets | Propshaft 1.3.2 |
|---|---|---|
| config.assets.precompile | 対象を列挙 | 無効(全ファイルが対象) |
| config.assets.compile | 本番で動的変換するか | 無効 |
| config.assets.css_compressor | 圧縮方式 | 無効(圧縮しない) |
| config.assets.excluded_paths | なし | 対象外のパス |
| config.assets.version | digestを変える | digestを変える(.digested除く) |
CDNやブラウザのキャッシュをまとめて捨てさせたいときは、config/initializers/assets.rb の config.assets.version を書き換えます。Propshaft はこの値をdigestの計算に混ぜているので、通常のアセットのURLは一斉に変わります。例外は、バンドラが名前にdigestを付けて -<digest>.digested.js の形で出力したファイルで、Propshaft は名前を変えずにそのまま配信します。
本番でCSSが反映されないときの確認順
「デプロイしたのにCSSが古い」「404になる」ときは、次の順に見ます。
- precompile がデプロイで走ったか:ビルドログに
Writing application-….cssが出ているか。出力先に.manifest.jsonがあるか。 - バンドラのビルドが先に成功したか:jsbundling・cssbundling 構成では
app/assets/buildsに生成物が無いと、Propshaft はそれを拾えない。yarn build:cssなどが失敗していないか。 - アプリを再起動したか:稼働中のプロセスは起動時に読んだマニフェストを持ち続ける。
- ビューがヘルパーを通しているか:
/assets/application.cssのような直書きはdigestが付かず、本番では404になる。 - clean で現行ファイルが消えていないか:Propshaft 1.2以降で clean を使っているなら、前章の突き合わせを実行する。
静的ファイルの配信を Rails の前段に任せる構成は、Thrusterとは?Rails 8のHTTP/2プロキシ設定とKamal 2との役割分担で詳しく扱っています。
デプロイ構成別のprecompile・clean運用
clean の要否は、public/assets がデプロイをまたいで残るかで決まります。
| 構成 | precompileの場所 | 古い版 | clean |
|---|---|---|---|
| Docker・Kamal 2 | イメージのビルド中 | 溜まらない | 不要 |
| Heroku(Ruby ビルドパック) | ビルド中 | キャッシュで溜まる | 自動で実行される |
| 同じサーバーでgit pullして更新 | サーバー上 | 溜まる | 必要 |
Docker・Kamalでのアセットの受け渡し
イメージごとに precompile する構成では、新しいコンテナには新しい版のアセットしかなく、切り替え中に古いHTMLを受け取ったブラウザが古いdigestのCSSを取りに来ると404になります。Kamal の asset_path 設定はこれを避けるためのもので、指定したパスを新旧両方のファイルを含むボリュームに差し替えます。Kamal 側の説明にあるとおり、内容が変わるとファイル名が変わる(digestが付く)ことが前提です。
# config/deploy.yml
asset_path: /rails/public/assets
Dockerfile を書かずにイメージを作る方式はCloud Native Buildpacksとは?Dockerfile不要のイメージ生成とリベース運用を実装目線で解説で比べています。ビルドからデプロイまでのパイプラインの設計を外部に頼みたい場合は、弊社のDevOps・CI/CD導入支援でもご相談を受けています。
キャッシュを使い回す構成でのclean
Heroku のように public/assets をビルド間でキャッシュする構成や、同じサーバーで precompile を繰り返す構成では、clean を回さないと古い版が溜まります。Propshaft 1.2以降なら前の章の問題に当たるので、リバートを含むデプロイの後は突き合わせを実行するか、そのビルドだけキャッシュを捨てて作り直してください。
digest付きのファイルは内容が変わるとURLが変わるので、長期キャッシュを付けても古い内容が残りません。Rails 8.1.4 が生成する config/environments/production.rb は、最初から "cache-control" => "public, max-age=#{1.year.to_i}" を設定しています。CDNから配る場合は同じファイルの config.asset_host にCDNのホストを書きます。
アセットパイプラインとSprockets・Propshaftの違い
アセットパイプラインは、ファイル名に内容のハッシュを付けて、長期キャッシュを効かせながら更新を確実に届ける仕組みです。Rails 7 まで標準だった Sprockets は //= require による結合、SCSSの変換、圧縮まで受け持っていました。Rails 8 の Propshaft はdigestの付与と配信だけを担当し、JavaScriptは importmap-rails か jsbundling-rails、CSSは cssbundling-rails や tailwindcss-rails に任せます。Propshaft を中心にした使い方は Railsガイドのアセットパイプラインが詳しいです。
既存の Sprockets アプリは、Gemfile に sprockets-rails を残せば Rails 8.1 でもそのまま動きます。移行するかどうかの判断はRails 7.2の主な変更点とサポート終了日|Rails 8.1への移行判断、importmap と組み合わせる Turbo・Stimulus はHotwireとは?Turbo・Stimulus・Hotwire Nativeの役割とRails 8.1での使い方にまとめています。jsbundling-rails で使う代表的なバンドラはesbuildとは?Go製の高速バンドラの使い方と採用判断を実装目線で解説を参照してください。
よくある質問
assets:clobberとassets:cleanの違いは何ですか?
clobber は出力先(public/assets)をマニフェストごと全部消します。clean は新しい版と1時間以内に作られたファイルを残し、それより古いものだけを消します。残る数は Sprockets が現行版+古い版2つ、Propshaft 1.2以降は現行版を含めて新しい順に2つで、Propshaft では現行版が消えることもあります。
rails assets:clobberで消したアセットは元に戻せますか?
public/assets の生成物と app/assets/builds のビルド結果が消えるだけで、app/assets/stylesheets などのソースは残ります。bin/rails assets:precompile を実行すれば作り直せます。jsbundling-rails などを使っていれば precompile の前にバンドラのビルドも自動で走ります。
assets:precompileしたのに本番でCSSが反映されないのはなぜですか?
アプリを再起動したかを先に確認してください。Propshaft は起動中にマニフェストを読み直しません。そのうえで、ビルドログに出力があるか、app/assets/builds の生成に失敗していないか、ビューが stylesheet_link_tag などのヘルパーを使っているかを見ます。
Propshaftでもconfig.assets.precompileは必要ですか?
不要で、書いても効きません。Propshaft は読み込みパス配下の全ファイルを precompile します。対象から外したいパスは config.assets.excluded_paths に指定します。
assets:clobberはデプロイで自動実行されますか?
Rails が生成する Dockerfile も Heroku のビルドパックも、自動で実行するのは precompile(Heroku はその後の clean も)で、clobber は実行しません。CIやデプロイ用の独自スクリプトに clobber が書かれていないかは、念のため確認してください。