Webページに掲載した画像を、右クリックの「名前を付けて画像を保存」やドラッグ操作から守りたいことがあります。JavaScriptやCSSを使えば、一般的な保存操作を分かりにくくすることは可能です。
ただし、ブラウザへ表示された画像は利用者の端末へデータが届いています。開発者ツール、ネットワーク通信、ブラウザキャッシュ、スクリーンショットなどを使えるため、フロントエンドだけで画像保存を完全に防ぐことはできません。
この記事では、「完全防止」ではなく軽い操作による持ち出しを抑止するという前提で、右クリック・ドラッグ対策、透かし、低解像度配信、アクセス制御を目的別に整理します。
- 右クリック禁止は、コンテキストメニューからの保存を抑止するだけです。
draggable="false"で画像のドラッグ操作を抑止できます。- CSSの透明レイヤーやCanvas表示は、元画像の取得防止にはなりません。
- 無断転載対策には、元画像へ直接埋め込んだ透かしが実用的です。
- 高解像度画像は公開ページへ置かず、低解像度版だけを表示します。
- 会員限定画像は、認証や期限付きURLで「誰に配信するか」を制御します。
- 正規利用者へ配信したあとの保存や撮影まで完全には防げません。
画像保存対策で防げること・防げないこと
| 対策 | 抑止できる操作 | 防げないこと |
|---|---|---|
| 右クリック禁止 | 通常の右クリック保存 | 開発者ツール、URL直接表示、スクリーンショット |
| ドラッグ禁止 | 画像をデスクトップなどへドラッグ | 右クリック保存、通信内容の取得 |
| CSSレイヤー | 表面上のマウス操作 | DOMやCSSを変更できる利用者 |
| 透かし | 無断転載の利用価値を下げる | トリミングや画像加工 |
| 低解像度配信 | 高品質な原本の持ち出し | 表示中の低解像度画像の保存 |
| 認証・期限付きURL | 未認証者からの直接アクセス | 認証後に表示した画像の保存 |
技術を一つ選ぶのではなく、画像の重要度に応じて複数の対策を組み合わせます。
右クリックメニューを画像だけ無効化する
contextmenuイベントでpreventDefault()を呼ぶと、指定した画像上のコンテキストメニューを抑止できます。ページ全体で禁止すると、リンクを新しいタブで開く、文章をコピーする、ブラウザの支援機能を使うといった操作まで妨げるため、保護対象だけに限定します。
<figure class="protected-media">
<img
class="protected-image"
src="/images/sample-watermarked.webp"
alt="透かし入りの作品サンプル"
width="1200"
height="800"
draggable="false"
>
<figcaption>作品サンプル</figcaption>
</figure>
<script src="/js/image-protection.js" defer></script>
document.querySelectorAll(".protected-image").forEach((image) => {
image.addEventListener("contextmenu", (event) => {
event.preventDefault();
});
});
インラインのoncontextmenu="return false"でも動作しますが、HTMLと処理を分離できるaddEventListener()の方が保守しやすい書き方です。FirefoxではShiftを押しながら右クリックするとメニューが表示される場合もあり、完全な禁止ではありません。
画像のドラッグを禁止する
画像はブラウザの既定動作でドラッグできます。HTMLのdraggable属性へ明示的にfalseを指定すると、ドラッグ操作を抑止できます。
<img class="protected-image" src="/images/sample-watermarked.webp" alt="透かし入りの作品サンプル" draggable="false" >
draggableは列挙型属性なので、draggableだけではなくdraggable="false"と値まで書きます。動的に追加される画像も含めてJavaScript側で抑止するなら、dragstartイベントも処理します。
document.addEventListener("dragstart", (event) => {
if (event.target.matches(".protected-image")) {
event.preventDefault();
}
});
モバイルの長押しメニューを抑止する
モバイルブラウザでは、画像の長押しで保存や共有メニューが表示されることがあります。ブラウザ依存ですが、対象画像へCSSを指定すると一部の操作を抑止できます。
.protected-image {
-webkit-user-drag: none;
-webkit-touch-callout: none;
user-select: none;
}
-webkit-touch-calloutや-webkit-user-dragは標準化された万能な保護機能ではなく、ブラウザによって効き方が異なります。タッチ操作や拡大表示を必要以上に妨げないか、実機で確認してください。
右クリック・ドラッグをまとめて対象画像だけ抑止する
画像が後から追加されるページでは、各画像へリスナーを付ける代わりにイベント委譲を使えます。次の例は.protected-imageだけを対象にし、ページの他の場所では通常の右クリックやドラッグを残します。
const protectedSelector = ".protected-image";
document.addEventListener("contextmenu", (event) => {
if (event.target.matches(protectedSelector)) {
event.preventDefault();
}
});
document.addEventListener("dragstart", (event) => {
if (event.target.matches(protectedSelector)) {
event.preventDefault();
}
});
jQueryでクリックや既定動作を制御している既存サイトでは、jQueryでクリックを禁止・無効化する方法も考え方の参考になります。ただし、新規実装では標準のaddEventListener()で十分です。
透明画像を重ねても元画像は守れない
透明な要素を画像の上へ重ねる手法は、表面上のクリック先を変えるだけです。HTMLのsrc、CSS、ネットワーク通信には元画像のURLが残ります。開発者ツールでレイヤーを削除すれば操作できるため、保護策としては弱く、レスポンシブ表示やリンク操作を壊しやすい欠点もあります。
また、透明要素へpointer-events: noneを指定すると、ポインター操作は下の画像へ通過します。透明画像を重ねながらpointer-events: noneを指定する実装は、操作抑止として成立しません。
Canvasに描画しても保存防止にはならない
画像を<canvas>へ描画すると、HTML上の<img>要素は隠せます。しかし、ブラウザは元画像を取得してからCanvasへ描画します。通信内容から画像URLを確認でき、Canvas自体もtoDataURL()やtoBlob()で画像として書き出せます。
const canvas = document.querySelector("canvas");
// Canvasの内容はData URLとして取得できる
const dataUrl = canvas.toDataURL("image/png");
console.log(dataUrl);
外部ドメインの画像をCanvasへ描画する場合はCORS条件によって書き出し時にSecurityErrorになることがありますが、これは画像保護の仕組みではありません。配信元がCanvasからの読み出しを許可していないことによる制約です。
無断転載には画像へ透かしを直接入れる
保存そのものを止められないなら、保存されてもそのまま再利用しにくい状態にします。作品名、サイト名、著作者名などを元画像へ直接合成した透かしは、CSSで上に文字を重ねる方法より強力です。
- CSSの透かし:表示は簡単だが、開発者ツールで消せる
- 画像へ合成した透かし:削除には画像加工が必要になる
- 中央付近の半透明透かし:端に置くよりトリミングされにくい
- 利用者別の透かし:会員IDなどを入れると流出経路の特定に役立つ
透かしが作品を隠しすぎると閲覧価値が下がります。公開用画像と購入・納品用の原本を分ける設計が現実的です。
高解像度の原本を公開ページへ置かない
ページ上で幅800pxしか表示しないのに、幅6000pxの原本をsrcへ指定すると、利用者は高解像度ファイルへアクセスできます。公開ページでは縮小済み・圧縮済み・透かし入りの画像だけを配信します。
<img
src="/previews/artwork-800-watermarked.webp"
srcset="
/previews/artwork-480-watermarked.webp 480w,
/previews/artwork-800-watermarked.webp 800w,
/previews/artwork-1200-watermarked.webp 1200w
"
sizes="(max-width: 800px) 100vw, 800px"
alt="透かし入りの作品プレビュー"
class="protected-image"
draggable="false"
loading="lazy"
width="1200"
height="800"
>
srcsetへ原本を含めるとブラウザから取得できるため、候補には公開してよいサイズだけを指定します。原本は公開ディレクトリへ置かず、購入後のダウンロード処理など別の経路から提供します。
loading="lazy"やsrcsetを含む画像表示の最適化は、JavaScriptのLazyloadとloading属性の使い方でも詳しく解説しています。
会員限定画像はサーバー側でアクセス制御する
ログインユーザーだけに見せたい画像は、JavaScriptで要素を隠すだけでは不十分です。画像URLへ直接アクセスされたときにも、サーバー側で認証・認可を確認する必要があります。
- 未ログインなら画像データを返さない
- ユーザーがその画像を閲覧できる権限を持つか確認する
- 期限付き・用途限定の署名付きURLを発行する
- 原本をWeb公開ディレクトリの外へ保存する
- 必要に応じて利用者別の透かしを合成する
署名付きURLも有効期限内は画像データへアクセスできます。URLの無期限共有を抑える仕組みであり、表示を許可された利用者による保存を禁止する仕組みではありません。
避けた方がよい実装
- ページ全体の右クリックを禁止する
- キーボード操作や開発者ツールのショートカットを妨害する
- 透明レイヤーを保護機能として過信する
- Canvasへ描画すれば元画像を取得できないと説明する
- CSSやJavaScriptだけで「絶対に保存できない」と表示する
- 高解像度の原本をHTMLから参照したまま見た目だけ縮小する
強い操作制限は、利用者に不信感を与えたり、ブラウザの便利な機能や支援技術を妨げたりします。抑止対象を画像へ限定し、目的に対して必要最小限の制御にします。
目的別のおすすめ構成
ブログの写真を軽く抑止したい
右クリックとドラッグを対象画像だけで抑止し、公開用の縮小画像を使います。完全防止をうたわないことが重要です。
イラストや写真の無断転載を減らしたい
画像本体へ透かしを合成し、公開版の解像度を抑えます。右クリック禁止は補助策として追加します。
購入者や会員だけに原本を渡したい
原本を公開領域から分離し、サーバー側の権限確認を通して配信します。期限付きURLや利用者別透かしも検討します。
よくある質問
まとめ
JavaScriptのcontextmenuイベントとHTMLのdraggable="false"を使えば、右クリック保存やドラッグによる持ち出しを抑止できます。ただし、これは一般的な操作を一段増やすだけで、画像データの取得を完全に防ぐものではありません。
重要な画像は、透かし入りの公開版と原本を分け、公開版の解像度を抑えます。会員限定・購入者限定の画像はサーバー側でアクセスを制御してください。画像保護は「保存ボタンを消す」問題ではなく、どの品質の画像を誰へ配信するかという設計の問題です。
