マガジン

ゲームの画像が表示されない:パス・大文字小文字・CORSを順に確認

遊びをつくる

執筆にClaudeを使用しています。

色鉛筆の線やゲームのスケッチが描かれたノートとカード。
記事のテーマをもとに制作したAI生成イメージ。実際のゲーム画面ではありません。

ブラウザゲームを作っていて「ローカルでは表示されていた画像が、アップロードしたら消えた」という経験は非常によくあります。画像が表示されない原因は大きく三つに絞られます。ファイルパスの誤り、大文字・小文字の不一致、そしてCORS(クロスオリジンリソース共有)の制限です。この三つを上から順に確認すれば、ほとんどのケースで原因を特定できます。 焦って画像を再アップロードしたり、コードを書き直したりする前に、まずブラウザの開発者ツール(F12キー)を開いてConsoleとNetworkタブを確認してください。エラーメッセージが原因の種別をほぼ教えてくれます。「404 Not Found」ならパスか大文字小文字の問題、「CORS policy」という文字列が見えればCORSの問題です。

この記事の目次
  1. パスの問題:「どこから見た相対パスか」を意識する
  2. 大文字・小文字の問題:Windowsでは通るがLinuxでは落ちる
  3. CORSの問題:外部URLから画像を読み込むときの制限
  4. 三つの原因を効率よく切り分けるフロー
  5. 次の行動

パスの問題:「どこから見た相対パスか」を意識する

最も多い原因は、パスの書き方が環境によってずれることです。HTMLファイルと画像ファイルの位置関係を、ブラウザがファイルを読み込む起点から正しく記述できているかを確認します。

よくある間違いは、ローカルで動かしていたときに絶対パス(C:\Users\name\project\img\hero.png や /Users/name/project/img/hero.png)をそのままコードに書いてしまっているケースです。これはローカルの自分のマシンだけで通用するパスなので、サーバーにアップロードした瞬間に無効になります。

相対パスに書き直す際は、HTMLファイルを基点にした相対位置で記述します。

  • HTMLファイルと同じフォルダに画像がある場合:src="hero.png" または src="./hero.png"
  • HTMLファイルの隣に img フォルダがある場合:src="img/hero.png"
  • 画像が一つ上の階層にある場合:src="../hero.png"

ZIPファイルでアップロードする際は、ZIP内のフォルダ構造がそのままサーバー上の構造になります。ローカルで project/img/hero.png というパスで動いていたなら、ZIP内も同じ構造になっているかを解凍して確認してください。ZIPを作るときに「フォルダごと圧縮」するか「フォルダの中身だけを圧縮」するかで、展開後の構造が変わります。意図した構造になっているかを事前に確かめる習慣をつけると、この種のトラブルを防げます。

大文字・小文字の問題:Windowsでは通るがLinuxでは落ちる

パスを正しく書いても画像が表示されないとき、次に疑うのは大文字・小文字の不一致です。

WindowsとmacOSのファイルシステムは、デフォルトで大文字・小文字を区別しません(Hero.png と hero.png を同じファイルとして扱います)。一方、多くのWebサーバーはLinux上で動いており、Linuxのファイルシステムは大文字・小文字を厳密に区別します。

つまり、ローカルでは src="Hero.png" と書いていても hero.png というファイルが見つかっていたものが、サーバー上では「Hero.png というファイルは存在しない」と判断されて404になります。

確認の手順は次のとおりです。

  1. コード内の src や url() に書かれたファイル名を確認する
  2. 実際のファイル名(拡張子を含む)と一文字ずつ照合する
  3. 拡張子も注意する(.PNG と .png は別物として扱われます)

ファイル名のルールとして、プロジェクト全体で「すべて小文字・スペースなし・アンダースコアかハイフンで区切る」と決めておくと、この問題をほぼゼロにできます。AIツールで生成した画像をそのまま保存した場合、ファイル名が Image_001.PNG のように大文字混じりになっていることがあるので、保存時に確認するクセをつけてください。

CORSの問題:外部URLから画像を読み込むときの制限

パスも大文字小文字も正しいのに画像が表示されず、Consoleに Cross-Origin Request Blocked や has been blocked by CORS policy と表示されている場合は、CORS(Cross-Origin Resource Sharing)の制限が原因です。

CORSとは、異なるオリジン(ドメイン・プロトコル・ポートの組み合わせ)からリソースを読み込む際にブラウザが行うセキュリティチェックのことです。たとえば、自分のゲームが https://example.com にあるとき、https://other-cdn.com/image.png という別ドメインの画像を fetch() や Canvas API経由で読み込もうとすると、other-cdn.com 側が許可の応答ヘッダー(Access-Control-Allow-Origin)を返していなければブラウザがブロックします。

単純な <img> タグで画像を表示するだけなら、CORSの制限は基本的に発生しません。問題が起きるのは主に次の場面です。

  • fetch() や XMLHttpRequest で画像データを取得しようとしている
  • Canvas の drawImage() で外部画像を描画し、toDataURL() や getImageData() を使おうとしている
  • JavaScriptの new Image() に crossOrigin 属性が必要な状況

対処の方針は三つあります。

① 画像をゲームと同じ場所に置く 最もシンプルで確実な方法です。外部CDNやストレージから読み込まず、ZIPにまとめてアップロードしたファイルの中に画像も含めます。オリジンが同じなのでCORSは発生しません。

② CORS対応している外部サービスを選ぶ どうしても外部URLから読み込む必要がある場合は、そのサービスがCORSヘッダーを返しているかを確認します。多くのパブリックなCDNや画像ホスティングサービスはCORSに対応していますが、個人のサーバーやストレージサービスはデフォルトで対応していないことがあります。公式ドキュメントで確認してください。

③ crossOrigin 属性を設定する new Image() でCanvasに描画する場合は、img.crossOrigin = "anonymous" を設定してから src を指定します。ただし、これはサーバー側がCORSを許可していることが前提です。属性を付けるだけでサーバーの制限を突破できるわけではありません。

三つの原因を効率よく切り分けるフロー

現場で使える確認の順番をまとめます。

まずブラウザの開発者ツールを開き、Networkタブで問題の画像ファイルを探します。ステータスコードが 404 であればパスか大文字小文字の問題です。ステータスコードが 200(取得できている)なのに表示されないか、Consoleに CORS policy のエラーが出ているならCORSの問題です。

404の場合は、NetworkタブでリクエストされているURLを確認します。そのURLのパスが実際のファイル構造と一致しているかを照合し、次に大文字・小文字を一文字ずつ確認します。修正後は必ずブラウザのキャッシュをクリア(Ctrl+Shift+R または Cmd+Shift+R)してから再確認してください。古いキャッシュが残っていて「直ったはずなのにまだ404」になることがあります。

CORSの場合は、画像をゲームのZIPに同梱する方向でまず試みます。それで解決するなら外部URLへの依存をなくせたということなので、その構成を維持するのが最も安定します。

次の行動

開発者ツールのConsoleとNetworkタブを開き、エラーメッセージの種類(404かCORSか)を確認するところから始めてください。原因が特定できたら、この記事の該当セクションに戻って手順を参照してください。画像をZIPに同梱してアソバースへアップロードする形式であれば、パスと大文字小文字の確認だけで大半のケースは解決します。