SSL証明書を入れてサイト全体をhttps://にしたのに、アドレスバーの鍵マークが出ない・「保護されていない通信」と警告される——。この記事はそんなWeb担当者・サイト運営者向けに、原因の大半を占める 混在コンテンツ(mixed content) を、検出から恒久解消・再発防止まで一気に片づける手順をまとめました。WordPress と nginx を例に、そのままコピペして使える設定を載せています。
混在コンテンツとは何か(なぜ鍵マークが消えるのか)
混在コンテンツとは、https:// で配信しているページの中に、http://(暗号化されていない通信)で読み込まれる画像・CSS・JavaScript などが混ざっている状態です。ページ本体は暗号化されていても、その一部が平文で取得されると、通信路上での盗聴や差し替えのリスクが残ります。ブラウザはこれを検知すると鍵マークを外し、「完全には安全でない」と扱います。
混在コンテンツには2種類あり、ブラウザの扱いが異なります(挙動は MDN のMixedContent仕様に準拠)。
| 種類 | 対象リソース | 2026年時点のブラウザの扱い |
|---|---|---|
| 受動的(passive / upgradable) | 画像・音声・動画(img / audio / video) |
可能なら https:// へ自動アップグレード。取得できなければブロック |
| 能動的(active / blockable) | スクリプト・CSS・iframe・fetch/XHR・Web フォント等 |
原則そのままブロック(表示崩れ・機能停止の原因) |
能動的な混在コンテンツはページの改ざんに直結しうるため、モダンブラウザは黙ってブロックします。「スタイルが当たらない」「ボタンが動かない」といった不具合の裏に混在コンテンツが潜んでいることは珍しくありません。
Step 1: どこが http:// なのかを特定する
まずブラウザの開発者ツール(DevTools)で確認します。Chrome / Edge / Firefox いずれも、対象ページを開いて Console(コンソール) を見ると、以下のような警告が出ます。
Mixed Content: The page at 'https://example.com/' was loaded over HTTPS,
but requested an insecure element 'http://example.com/wp-content/uploads/logo.png'.
This request was automatically upgraded to HTTPS.
Mixed Content: The page at 'https://example.com/' was loaded over HTTPS,
but requested an insecure script 'http://example.com/js/slider.js'.
This request has been blocked; the content must be served over HTTPS.
automatically upgraded が受動的、has been blocked が能動的です。ファイル名まで出るので、置き換えるべき URL が一覧で分かります。
コマンドラインで機械的に洗い出すなら、公開 URL を取得して http:// の参照を抜き出します。
# トップページの HTML 内に残る http:// 参照を一覧化する
curl -s https://example.com/ \
| grep -oE '(src|href)="http://[^"]+"' \
| sort -u
ページ数が多い場合は、後述の CSP レポートで本番の実トラフィックから収集するのが確実です。
Step 2: 原因を切り分ける(WordPress でよくある3パターン)
WordPress サイトで混在コンテンツが残る典型は次の3つです。
-
データベースに
http://の絶対 URL が保存されている(記事本文・カスタムフィールド・ウィジェット等)。HTTPS 化前に投稿した画像リンクがhttp://のまま残るのが最多です。 -
サイト URL 設定が
http://のまま(siteurl/home)。 -
テーマ・プラグインが
http://をハードコード、または外部 CDN をhttp://で読み込んでいる。
まず 2 を確認します。wp-cli があれば一発です。
# 現在のサイト URL 設定を確認する
wp option get siteurl
wp option get home
# どちらかが http:// なら https:// に更新する
wp option update siteurl 'https://example.com'
wp option update home 'https://example.com'
Step 3: データベース内の http:// を一括置換する(最重要)
原因の大半である「本文中の http:// 絶対 URL」は、wp-cli の search-replace でまとめて置き換えます。シリアライズされたデータ(ウィジェットやオプション)も安全に処理されるため、SQL で直接 UPDATE するより確実です。
作業前に必ずバックアップを取ります(復元手順は関連記事を参照)。
# 1) まずは --dry-run で「何件・どこが」変わるか確認する(実際には変更しない)
wp search-replace 'http://example.com' 'https://example.com' \
--all-tables-with-prefix --precise --recurse-objects --dry-run
# 2) 問題なければ本実行する。--skip-columns=guid は投稿の GUID を書き換えないため
wp search-replace 'http://example.com' 'https://example.com' \
--all-tables-with-prefix --precise --recurse-objects --skip-columns=guid
--dry-run で影響範囲を必ず先に確認するのが安全側の運用です。guid は投稿の内部識別子で、フィードの購読者判定に使われるため、原則書き換えません(画像などの実 URL は本文側で置換されます)。
wp-cli を導入できない共有サーバーでは、「Better Search Replace」等の置換プラグインで同じ処理ができます。プラグイン利用時も先にドライラン(検索のみ)で件数を確認してください。
Step 4: サーバー側で http:// を締め出す(nginx / Apache)
DB を直しても、テーマ・プラグインが動的に http:// を出力する余地は残ります。仕上げに、サーバーからブラウザへ「このページ内の http:// は全部 https:// に読み替えて」と指示する CSP ディレクティブ upgrade-insecure-requests を付けます。これはページ内のサブリソース要求を送信前に https:// へ書き換え、iframe の中まで波及します(web.dev のガイド参照)。
nginx の場合、HTTPS を配信する server ブロックに1行足します。
# https を配信する server { } の中に追記する
add_header Content-Security-Policy "upgrade-insecure-requests" always;
# 併せて、http でのアクセスは https へ 301 リダイレクトしておく
server {
listen 80;
server_name example.com www.example.com;
return 301 https://$host$request_uri;
}
Apache(.htaccess または httpd.conf)の場合は次のとおりです。
# mod_headers が有効な前提
Header always set Content-Security-Policy "upgrade-insecure-requests"
# http を https へ恒久リダイレクト(mod_rewrite)
RewriteEngine On
RewriteCond %{HTTPS} off
RewriteRule ^(.*)$ https://%{HTTP_HOST}/$1 [R=301,L]
なお、かつて使われた block-all-mixed-content ディレクティブは非推奨です。これから設定するなら upgrade-insecure-requests を使ってください。すでに CSP を運用している場合は、既存のポリシー文字列の末尾に ; upgrade-insecure-requests を足す形になります(CSP の段階導入は関連記事を参照)。
Step 5: 残りを CSP レポートで拾って確認する
upgrade-insecure-requests は「自動で読み替える」対策なので、根本の http:// 参照が残っていても表面上は鍵マークが戻ります。取りこぼしを可視化したいときは、Content-Security-Policy-Report-Only で違反レポートだけ収集し、本番トラフィックから残存箇所を洗い出します。
# 実際のブロックはせず、違反があった URL だけ収集する
add_header Content-Security-Policy-Report-Only "default-src https: 'unsafe-inline' 'unsafe-eval'; report-uri /csp-report" always;
最後に、鍵マークが戻ったかを機械的に確認します。
# レスポンスヘッダに CSP が乗っているか確認する
curl -sI https://example.com/ | grep -i content-security-policy
# ページ内に http:// の参照が残っていないか再チェックする
curl -s https://example.com/ | grep -oE '(src|href)="http://[^"]+"' | sort -u
2つ目のコマンドが何も出力しなければ、少なくともトップページの静的 HTML からは http:// 参照が消えています。あとは DevTools の Console に Mixed Content 警告が出ないことを、主要ページで確認すれば完了です。
まとめ
- 鍵マークが出ない原因の大半は、ページ内に残る
http://の画像・CSS・JS(混在コンテンツ)。 -
検出は DevTools の Console と
curl | grep、本番全体は CSP Report-Only で。 -
恒久解消の主役は
wp search-replaceによる DB 内 URL の一括置換(必ず--dry-runから)。 -
仕上げにサーバーで
upgrade-insecure-requestsと http→https の 301 リダイレクトを設定する。 -
block-all-mixed-contentは非推奨。新規はupgrade-insecure-requestsを使う。
関連記事
本記事の設定(混在コンテンツ・CSP・リダイレクト)は、Webサイトを9つの守りでまるごと守る「サイトドック」の月次の定期健診でまとめて確認できます → https://sitedock.jp