5
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

「画像が保存されない…」泥沼の正月。InvalidSignatureと戦った記録

5
Posted at

はじめに

はじめまして、えりぃーと申します。@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 のままだと、デプロイ後に画像が消えます。

泥沼を抜けてこれらの修正を行い、震える手でデプロイボタンを押し、ブラウザをリロードし、今回のアプリは無事リリースされましたとさ笑

学んだ教訓

  1. エラーログは嘘をつかない:InvalidSignature が出たら、まずはパラメータを見る。文字(ファイル名)しか送られていなければ、フォームの設定ミス。

  2. 入れ子に注意:パーシャル(_form)を使うときは、呼び出し元でさらに form で囲っていないか確認する。

『ヒトノハゴロモ』の画像投稿機能は、こうして私の数日間の苦悩を吸い込んで完成しました。
同じようなエラーでPCを閉じそうになっている誰かの助けになれば幸いです。
一旦閉じてもいい。一分、また一分向き合ってあなたらしい開発をしよう!!(実際正月だったので餅食って休見ましたとも)
最後まで読んでくださりありがとうございました!!

参考記事

ファイルアップロード時のRailsにおけるmultipart指定について

5
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?