【2021年版】Herokuデプロイ時に "Refile.secret_key was not set" というエラーが出た場合の対処方法

## はじめに

[refile](https://rubygems.org/gems/refile) gemを使っているRailsアプリケーションをHerokuにデプロイすると、以下のようなエラー画面（"We're sorry, but something went wrong."）が出る場合があります。

![Screen Shot 2021-08-08 at 13.39.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/7465/a9929bf1-2c40-7d5f-bd32-2a5bb3428b6b.png)


そしてログを確認すると、以下のようなエラーメッセージが表示されていることがあります。

```
Completed 500 Internal Server Error in 177ms (ActiveRecord: 30.7ms)

ActionView::Template::Error (Refile.secret_key was not set.

Please add the following to your Refile configuration and restart your application:

Refile.secret_key = 'fbf61ef7dd09c2b398...'
```

この記事ではこの問題への対処方法と、ネットでよく見かける間違った解決方法について説明します。

## 正しい解決方法：refileをやめてActiveStorageに乗り換える

衝撃の解決策ですが、一番正しい解決方法はrefile gemを使わないことです。それはなぜか？refile gemは[2015年から更新が止まっている](https://rubygems.org/gems/refile/versions)ためです。（2021年8月現在）

![Screen Shot 2021-08-08 at 13.43.54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/7465/e6dbad06-d94f-3de7-4b2f-7e0289af8f6d.png)


refile gemを使うと`bundle install`に失敗するため、ネット上の情報では本家refileではなく、他の人がフォークしたrefile（[manfe/refile](https://github.com/manfe/refile)）を使うように勧めていることが多いです。

```rb:Gemfile
# manfe/refileは本家refileではなく、他の人がフォークしたrefile
gem "refile", require: "refile/rails", github: "manfe/refile"
```

が、manfe/refileの変更内容もRails 4からRails 5.0に切り替わった際（2016年頃）に発生したエラーを[雑に解決しただけ](https://github.com/manfe/refile/commit/46b4178654e60bb5846b1423acf6d0740cdecc25)です。

![Screen Shot 2021-08-08 at 14.38.43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/7465/3f6ada84-0b97-003c-695c-e7b01ead5fb7.png)

Railsはすでに5.1、5.2、6.0、6.1とアップデートされていますが、manfe/refileはRails 5.0でbundle installができるようにしただけで、決して最新のRailsを公式にサポートしているわけではありません。それゆえ、Rails 5.2以降のRailsアプリケーションではmanfe/refileを使った場合に前述のエラー（"Refile.secret_key was not set"）が発生します。

結局のところ、 **本家refileも、フォークされたmanfe/refileもRails 5やRails 6を公式にサポートしていない以上、現在は使用するべきではありません。ファイルアップロードの機能を実装するのであれば、Rails標準のActiveStorageを使うのがベストです。**

[Active Storage の概要 \- Railsガイド](https://railsguides.jp/active_storage_overview.html)

特に「これからRailsアプリの勉強をしていきまーす！」っていう初心者さんは、古くてマイナーなrefileよりも、Rails標準のActiveStorageの使い方を学んだ方がはるかに有益です。

## すぐにActiveStorageには移行できません！という場合の次善策

できればすぐにでもrefile gemを窓から投げ捨ててActiveStorageに移行してほしいのですが、Rails初心者さんは「さあ、移行しろ」と言われてもハードルが高いかもしれません。その場合は、次善策として僕が修正を加えたgemを使ってください。（NOTEとして書いてるコメントも一緒にコピーしてください！！）

```rb:Gemfile
# NOTE: refileの更新が止まっているため本来はActiveStorage等に移行すべきだが、一次対応としてやむを得ずフォークしたgemを使う
gem "refile", require: "refile/rails", github: "JunichiIto/refile", branch: "use-env-secret-key-base"
```

上のgemをインストールしてHerokuにデプロイすれば、エラーなくRailsアプリが起動するはずです（環境変数に設定された`SECRET_KEY_BASE`が`Refile.secret_key`に設定されます）。

ただし、あくまで「次善策」であり、僕がフォークしたrefileもRails 5系や6系を公式にサポートしているわけではない、ということをお忘れなく。

## 💀💀💀間違った対処方法💀💀💀

ネットを見ていると、「`application_controller_renderer.rb`に`Refile.secret_key`の値を設定したら動きました！」という解決方法をよく見かけますが、これは完全にNGです！！🙅‍♂️

```ruby:config/initializers/application_controller_renderer.rb
# ＜＜＜絶対ダメ!!＞＞＞
# config/initializers/application_controller_renderer.rbに以下の行を追加
Refile.secret_key = '3b280b2ea435...'
```

### 秘密の値を平文でコミットするな！！

まず、"secret"と名前が付いた設定値を平文で保存してgitにコミットするのは **絶対NG** です！
それをpublicなGitHubリポジトリにpushすると、全世界に向けて「秘密の値」を大公開することになってしまいます。

幸いなことに`Refile.secret_key`はそのまま公開しても深刻な問題が起きる可能性はそこまで高くありませんが、それでも「セキュリティ対策のイロハ」として、こういった危険な解決策を選択するのは迂闊（うかつ）と言わざるを得ません。

秘密の値が必要なときは必ず、Railsが提供している[credentials](https://railsguides.jp/security.html#%E7%8B%AC%E8%87%AA%E3%81%AEcredential)の仕組みや、環境変数を使うようにしてください。

### refileの設定値はrefile.rbに保存すべし！

次に、gemに関する設定は`(gemの名前).rb`というファイルに保存するのが慣習になっています。refileの設定であれば、`config/initializers/refile.rb`に保存すべきです。もしそのファイルがなければ新規に作成してください。

おそらくですが、このエラーを解決する記事を最初に書いた人が「アルファベット順で一番最初に並んでるから〜」みたいな理由で`application_controller_renderer.rb`にrefileの設定を書き込んでしまい、それを見た初心者さんが「なるほど」と思って盲目的にコピペしていった結果、猫も杓子も`application_controller_renderer.rb`に設定を書き込むようになってしまったんじゃないかと僕は推測しています。しかし、本来`application_controller_renderer.rb`とrefileは無関係なので、ここにrefileの設定値を書き込むべきではないです（動作上問題はないが、あとから見た人の誤解の原因になる）。


## ところで Refile.secret_key っていったい何なの？

`Refile.secret_key`は画像ファイルのURLのtokenを生成・検証するために使用される値です。たとえば、

```
https://example.com/attachments/286d.../store/40ea.../image.jpg
```

というrefileの画像URLがあった場合、`286d...`にあたる部分がtokenになります。一方、`40ea...`の部分は画像ファイルにランダムに割り振られるユニークなIDです。

このtokenは`Refile.secret_key`に設定された値と、パス情報（ここでは`/store/40ea.../image.jpg`）を元に自動計算されます。

画像URLにアクセスがあった場合、refileはこのtokenが正しい値であることを検証します。正しくなければ403エラーとなり、画像は表示されません。

このtokenは不正なリクエストやDoS攻撃を防止するために利用されます。

参考: https://github.com/refile/refile/commit/4463967e4963d5d13e53d8f37e2c452d7f9565a9

## まとめ

本記事のまとめです。

- refile gemは何年もアップデートされてないので、2021年に使うべきgemではない[^1]。ActiveStorageに移行すべし。
- どうしても移行できない場合は筆者がフォークした`github: "JunichiIto/refile", branch: "use-env-secret-key-base"`を使え。（あまり使ってほしくないけど）
- `Refile.secret_key`のような秘密の値を平文でコミットするのは絶対にやめろ！！

以上です。

[^1]: 将来もし、refile gemが最新のRailsに正式対応するようになれば話は別。