概要
Mixed Content(混在コンテンツ)エラーは、HTTPSで配信されるページがHTTP(非暗号化)のリソース(画像、スクリプト、スタイルシート等)を読み込もうとした場合に発生します。
エラーメッセージ
Mixed Content: The page at 'https://example.com/' was loaded over HTTPS,
but requested an insecure resource 'http://example.com/image.jpg'.
This request has been blocked; the content must be served over HTTPS.
または
混在コンテンツ: 'https://example.com/' のページが HTTPS で読み込まれましたが、
安全でないリソース 'http://example.com/image.jpg' をリクエストしました。
このリクエストはブロックされました。
原因
- ハードコードされたHTTP URL: コード内にhttp://が残っている
- 外部リソース: HTTPのみ対応のCDNや外部サービス
- データベース内のURL: CMSに保存された古いURL
- 相対プロトコル未使用: プロトコルを明示的に指定
Mixed Contentの種類
Passive/Display Mixed Content(表示型)
- 画像 (<img>)
- 動画 (<video>)
- 音声 (<audio>)
警告は出るが、多くのブラウザでは読み込まれる
Active Mixed Content(アクティブ型)
- JavaScript (<script>)
- CSS (<link rel="stylesheet">)
- iframe (<iframe>)
- XMLHttpRequest / Fetch
- WebSocket
セキュリティリスクが高いため、ブロックされる
解決策
1. 相対プロトコルまたはHTTPSを使用
1
2
3
4
5
6
7
8
9
10
| <!-- 問題のあるコード -->
<img src="http://example.com/image.jpg">
<script src="http://cdn.example.com/lib.js"></script>
<!-- 修正版: HTTPS を使用 -->
<img src="https://example.com/image.jpg">
<script src="https://cdn.example.com/lib.js"></script>
<!-- 修正版: プロトコル相対URL(非推奨だが動作する) -->
<img src="//example.com/image.jpg">
|
2. Content-Security-Policy で自動アップグレード
1
2
| <!-- HTML meta タグ -->
<meta http-equiv="Content-Security-Policy" content="upgrade-insecure-requests">
|
1
2
| # Nginx
add_header Content-Security-Policy "upgrade-insecure-requests" always;
|
1
2
| # Apache
Header always set Content-Security-Policy "upgrade-insecure-requests"
|
3. データベース内URLの一括置換
1
2
3
4
5
6
7
8
9
10
| -- WordPress の例
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')
WHERE option_name = 'home' OR option_name = 'siteurl';
|
4. JavaScript での検出と修正
1
2
3
4
5
6
7
8
| // 動的にHTTPS化
document.querySelectorAll('img[src^="http://"]').forEach(img => {
img.src = img.src.replace('http://', 'https://');
});
// Fetch/XHR でHTTPS強制
const secureUrl = url => url.replace(/^http:\/\//i, 'https://');
fetch(secureUrl(apiUrl));
|
5. 開発時のMixed Content確認
1
2
3
4
5
6
7
| // Service Worker で検出
self.addEventListener('fetch', event => {
const url = new URL(event.request.url);
if (url.protocol === 'http:' && url.hostname !== 'localhost') {
console.warn('Mixed content detected:', url.href);
}
});
|
1
2
| # ページ内のHTTPリソースを検索
curl -s https://example.com | grep -oE 'http://[^"'"'"'> ]+'
|
6. CSPレポートで監視
1
2
3
| # Nginx - レポートのみ(ブロックしない)
add_header Content-Security-Policy-Report-Only
"default-src https:; report-uri /csp-report" always;
|
1
2
3
4
5
| // レポート受信エンドポイント
app.post('/csp-report', express.json({ type: 'application/csp-report' }), (req, res) => {
console.log('CSP Violation:', req.body);
res.status(204).end();
});
|
ブラウザでの確認方法
Chrome DevTools:
1. F12 で開発者ツールを開く
2. Console タブで警告/エラーを確認
3. Network タブで "mixed-content" をフィルタ
4. Security タブで詳細を確認