何が起きているか
ページはHTTPS経由で読み込まれていますが、スクリプト、画像、スタイルシート、iframe、またはAPIコールなど、1つ以上のリソースがまだ平文のHTTP経由で取得されています。ブラウザはこれをセキュリティ上の欠陥として扱います。ページ自体が暗号化されていても、中間者攻撃者がそのHTTPリソースを悪意のあるものに置き換える可能性があるためです。
Chrome 86以降は、画像などのパッシブリソースを含むすべての混在コンテンツを自動的にブロックします。古いブラウザはアクティブリソース(スクリプト、iframe)のみブロックし、パッシブリソースについては警告を表示するだけでした。いずれの場合も、リソースの読み込みに失敗し、コンソールに以下のメッセージが表示されます:
Mixed Content: The page was loaded over HTTPS, but requested an insecure resource. This request has been blocked.
画像の場合、より軽微な警告は次のように表示されます:
Mixed Content: The page at 'https://example.com' was loaded over HTTPS, but contains a link to an insecure image 'http://example.com/photo.jpg'. This content should also be served over HTTPS.
問題のあるリソースをすべて特定する
最も手っ取り早い方法は、DevToolsを開き(F12)、Consoleタブで「Mixed Content」でフィルタリングすることです。ブロックされたURLが、それをトリガーした正確な行とともに表示されます。また、Networkタブも確認し、リクエストURLの列をhttp://でフィルタリングしてください。
すべてのページをクリックせずにサイト全体をスキャンするには、mixed-content-scanner CLIツールを使うと、サイト全体をクロールして問題のあるURLをすべて報告してくれます:
npx mixed-content-scanner https://example.com
または、生のHTMLを取得してHTTP参照を直接grepする方法もあります:
curl -s https://example.com | grep -Eo 'http://[^"\x27 >]+' | sort -u
手軽な修正 — upgrade-insecure-requests CSPヘッダーを有効にする
サーバーにContent-Security-Policyヘッダーを追加します。これにより、リクエストを行う前にすべてのサブリソースのhttp://をhttps://に暗黙的に書き換えるよう、ブラウザに指示できます — コードの変更は不要です。
Nginx
add_header Content-Security-Policy "upgrade-insecure-requests;";
Apache (.htaccess)
Header always set Content-Security-Policy "upgrade-insecure-requests;"
HTMLのmetaタグ(サーバーを変更できない場合)
<meta http-equiv="Content-Security-Policy" content="upgrade-insecure-requests">
他のリソースタグより前の<head>内に配置してください。このタグより前にHTMLに記述されたリソースはアップグレードされません。
注意点: upgrade-insecure-requestsはURLを書き換えますが、存在しないHTTPSエンドポイントを作り出すことはできません。リモートサーバーがHTTPSをまったくサポートしていない場合、リクエストは結局失敗します — ただしエラーの内容が異なります。そのようなリソースは、以下の方法で恒久的に修正してください。
恒久的な修正 — コード内のすべてのHTTP参照を更新する
1. HTML/テンプレート内のハードコードされたURL
コードベース全体で検索・置換を実行します。ほとんどのケースを数秒で解決できます:
# テンプレート内のすべてのhttp://参照を検索
grep -r 'http://' ./src --include='*.html' --include='*.jsx' --include='*.tsx' --include='*.vue'
# すべてのファイルでhttp://example.comをhttps://example.comに置換
find ./src -type f \( -name '*.html' -o -name '*.jsx' -o -name '*.tsx' \) \
-exec sed -i 's|http://example.com|https://example.com|g' {} +
2. プロトコル相対URL(サードパーティの埋め込み用)
スキームを完全に省略し、現在のページのプロトコルをブラウザに継承させます — 本番サイトではHTTPS、ローカル開発ではHTTPになります:
<!-- 変更前 -->
<script src="http://cdn.example.com/lib.js"></script>
<!-- 変更後 -->
<script src="//cdn.example.com/lib.js"></script>
3. WordPressサイト
WordPressはURLをデータベースに保存するため、ファイルレベルの検索・置換だけでは不十分です。データベースの検索・置換を実行します — WP-CLIが最も安全な方法です:
-- WP-CLI経由(シリアライズされたデータを正しく処理)
wp search-replace 'http://example.com' 'https://example.com' --all-tables
-- またはMySQLで直接実行
UPDATE wp_posts SET post_content = REPLACE(post_content, 'http://example.com', 'https://example.com');
UPDATE wp_postmeta SET meta_value = REPLACE(meta_value, 'http://example.com', 'https://example.com');
UPDATE wp_options SET option_value = REPLACE(option_value, 'http://example.com', 'https://example.com');
データベースの更新後、Really Simple SSLプラグインをインストールしてください。PHPの出力バッファレベルでリアルタイムに混在コンテンツを修正し、データベースの更新で漏れたテーマファイル、ウィジェット、動的出力をすべてカバーします。
4. JavaScriptのFetch / XHR
絶対HTTP URLを使用したfetch()またはXMLHttpRequestの呼び出しはブロックされます。可能な限り相対URLを使用してください — 短くなり、常に正しいプロトコルを継承します:
// 悪い例
fetch('http://api.example.com/data')
// 良い例 — 相対URL(プロトコルを自動的に継承)
fetch('/api/data')
// 良い例 — 外部APIには明示的なHTTPSを使用
fetch('https://api.example.com/data')
5. CSSの背景画像と@import
/* 悪い例 */
.hero {
background-image: url('http://example.com/hero.jpg');
}
/* 良い例 */
.hero {
background-image: url('https://example.com/hero.jpg');
}
リソースがHTTPSをまったくサポートしていない場合
古いサードパーティCDNやセルフホストのアセットの中には、HTTPでしか配信されないものもあります。その場合、3つの対処法があります:
- セルフホストする — ファイルをダウンロードして、自分のHTTPSドメインから配信します。フォント、ライブラリ、静的なものすべてに有効です。
- HTTPSのCDNに切り替える — jsDelivr、cdnjs、unpkgはすべてのパッケージをHTTPS経由で配信しています。URLを置き換えるだけで完了です。
- プロキシを経由させる — 自分のHTTPSエンドポイントを通じてリクエストをルーティングし、HTTPリソースを取得・転送します。手間はかかりますが、ライブデータフィードでは避けられないこともあります。
修正を確認する
- ページをハードリロードして(
Ctrl+Shift+R/Cmd+Shift+R)キャッシュをバイパスします。 - DevTools → Consoleを開きます。混在コンテンツの警告がゼロであれば完了です。
- アドレスバーの鍵アイコンを確認します — 警告の三角形がなく、クリーンな鍵マークが表示されているはずです。
- Chromeで鍵アイコンをクリックし、接続は保護されています → 証明書は有効ですを確認します。
- 最終クロールを実行します:
npx mixed-content-scanner https://example.com— 0件の問題が報告されるはずです。

