はじめに
はじめまして、えりぃーと申します。@Ery_manabito5en
2025年7月からオンラインプログラミングスクールRUNTEQにてRuby on Railsを中心に学習しており、現在、卒業制作としてQOLを整えるファッション管理アプリ『ヒトノハゴロモ』をリリースしました。
https://hitonohagoromo.onrender.com
「服を記録することは、生活を大切にすること」——そんなコンセプトで開発し、クローゼット画面に自分のお気に入りの服が並ぶ様子を夢見てコードを書いていました。
しかし、その夢は画像アップロードという厚い壁に阻まれる日々が続いており、やっと今回リリースに辿りつくことができました。
ローカルでは動いている気がするのに、本番環境(Render)にデプロイすると動かない。いや、よく見ると編集画面だけ動かない……?
画面には無慈悲な We're sorry, but something went wrong.。(マジで夢に出そうなくらい見た。)
ログには謎の InvalidSignature。
ここから、私の「画像データが送信されず、ファイル名だけが虚しく送られる」という泥沼のデバッグ生活が始まりました。今回はこの泥沼デバッグ生活の回想録です(笑)
それでは気になる人は以下からどうぞ↓
絶望のログ確認
エラーが出た時、私たちはまずログを見ると思います。
しかし、当時の私はこのログの「本当の意味」に気づいていませんでした。
F, [2026-01-06Txx:xx:xx] FATAL -- :
ActiveSupport::MessageVerifier::InvalidSignature (ActiveSupport::MessageVerifier::InvalidSignature):
app/controllers/items_controller.rb:59:in `update'
「署名(Signature)が無効? CloudinaryのAPIキー間違えたかな?」
そう思って環境変数を何度も確認しました。でも直らない。
ふと、送信されたパラメータ(Parameters)に目を落としました。
Parameters: {
"item" => {
"name" => "お気に入りのTシャツ",
# ↓ !!!???
"image" => "スクリーンショット 2026-01-05.png"
}
}
「……文字?」
本来、画像が正しくRailsに送られている場合、ここは ActionDispatch::Http::UploadedFile... というオブジェクトになっているはずです。
しかし、私のアプリは「画像データそのもの」ではなく、「ファイル名の文字列」だけをサーバーに送りつけていたのです。
Rails(Active Storage)側からすれば、
「画像データくれって言ってるのに、"画像.png" っていうメモ紙だけ渡されても困るよ!」
と怒っていたわけです。(それが InvalidSignature の正体でした)。
犯人探しと解決策
なぜ画像本体が送られないのか?
原因は以下のものでした。
① multipart: true の不在
HTMLの仕様として、ファイルを送るフォームには enctype="multipart/form-data" が必須です。
form_with は賢いので、自動で付けてくれることもありますが、明示的に書かないとサボることもあります。
修正前(動かない)
<%= form_with(model: item, local: true) do |form| %>
修正後
<%= form_with(model: item, local: true, html: { multipart: true }) do |form| %>
「これで勝った!」と思いました。でも、まだ直りません。
② Rails 7の相棒:Turboの干渉
Rails 7標準のTurbo Driveは、画面遷移を高速化してくれますが、時としてフォーム送信の挙動を複雑にします。
「もう余計なことはしないでくれ!」という願いを込めて、このフォームではTurboを無効化します。これが一番確実でした。
<%= form_with(model: item, local: true, html: { multipart: true, data: { turbo: false } }) do |form| %>
③ 最大の落とし穴:フォームの「入れ子(ネスト)」
これが一番の盲点でした。
私は _form.html.erb という共通部品を作っていたのですが、呼び出し元の edit.html.erb で、さらに form_with で囲ってしまっていたのです。
🔪当時の edit.html.erb(犯人)
<%= form_with model: @item, url: item_path(@item) do |f| %>
<%= render 'form', item: @item %>
<% end %>
HTMLとして form タグの中に form タグが入るのは不正です。
ブラウザは外側の(設定が足りない)フォームを優先して認識してしまい、内側でいくら「画像をくれ!」と叫んでも無視されていたのです。
⭕️修正後の edit.html.erb
<%= render 'form', item: @item %>
④設定ファイルの最終確認(Render + Cloudinary)
コードが直っても、本番環境(Render)が「画像をどこに保存するか」を知らなければ意味がありません。ここでも数時間溶かしました。
config/storage.yml
cloudinary:
service: Cloudinary
config/environments/production.rb
# 本番環境では Cloudinary を使う!
config.active_storage.service = :cloudinary
ここが :local のままだと、デプロイ後に画像が消えます。
泥沼を抜けてこれらの修正を行い、震える手でデプロイボタンを押し、ブラウザをリロードし、今回のアプリは無事リリースされましたとさ笑
学んだ教訓
-
エラーログは嘘をつかない:InvalidSignature が出たら、まずはパラメータを見る。文字(ファイル名)しか送られていなければ、フォームの設定ミス。
-
入れ子に注意:パーシャル(_form)を使うときは、呼び出し元でさらに form で囲っていないか確認する。
『ヒトノハゴロモ』の画像投稿機能は、こうして私の数日間の苦悩を吸い込んで完成しました。
同じようなエラーでPCを閉じそうになっている誰かの助けになれば幸いです。
一旦閉じてもいい。一分、また一分向き合ってあなたらしい開発をしよう!!(実際正月だったので餅食って休見ましたとも)
最後まで読んでくださりありがとうございました!!