HTTPS通信下でJavaScriptが動かない原因と解決方法

先輩への質問の返答が、全部「ググレカス」

ローカルやデモ環境ではちゃんと動いていたJavaScriptが、本番サーバーにアップした途端に無反応になる。私も一度これで半日ハマったことがあります。エラーらしいエラーも出ないのに動かないので、原因の切り分けにやたら時間がかかるんですよね。

このパターン、SSL(HTTPS)を入れたサイトで起きているなら、かなりの確率で「混在コンテンツ(Mixed Content)」が犯人です。今回はその仕組みと、いまのブラウザの挙動を踏まえた直し方をまとめておきます。

原因はほぼ「混在コンテンツ(Mixed Content)」

HTTPSで表示しているページの中で、http://から始まるJavaScriptやCSS、画像を読み込むと、それは混在コンテンツと呼ばれます。ページ本体は暗号化されているのに、一部のリソースだけ暗号化されていない通信で取りに行っている状態ですね。この非暗号の部分は途中で盗み見・改ざんされるおそれがあるため、ブラウザは安全のために手を打ちます。(MDN Web Docs: Mixed content

ここで大事なのが、リソースの種類によって扱いが違うという点です。

  • スクリプト・スタイルシート・iframe・fetch/XHR・Webフォントなど、ページの動きに深く関わるもの(かつてのアクティブ混在コンテンツ)は既定でブロックされます。JavaScriptが「エラーもなく動かない」のはこれが理由です。
  • 画像・音声・動画など影響の小さいもの(かつてのパッシブ混在コンテンツ)は、いまのChromeやFirefoxでは自動的にhttpsへアップグレードして読み込もうと試みます。アップグレードに失敗した場合は読み込まれません。

つまり、動かなくなっているスクリプトのURLがこうなっていないか、まず疑ってみてください。

<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>

http://でjQueryを取りに行っているので、HTTPSページではブロックされ、jQueryに依存したコードが軒並み動かなくなる、というわけです。

まずはコンソールで犯人を特定する

手を動かす前に、ブラウザの開発者ツール(F12)を開いてConsoleタブを見てください。混在コンテンツがあると、Mixed Content: The page at ... was loaded over HTTPS, but requested an insecure ... といった警告が出ています。ここに「どのURLがブロックされたか」がそのまま書いてあるので、直す対象を漏れなく洗い出せます。ブロックされた分は「blocked」、httpsへ自動アップグレードされた分は「upgraded」といった形で区別して表示されるので、いちいち推測しなくて済みます。

解決方法

1. リソースのURLをhttpsに書き換える(基本)

一番素直な直し方です。読み込んでいる外部リソースのURLのhttp://https://にするだけ。いまどきの主要なCDNはHTTPSに対応しているので、これで解決するケースがほとんどです。

<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>

CSSや画像も同じで、外部から読むものは最初からhttpsで書いておくのが安全です。

2. プロトコル相対URLは、いまはもう使わない

昔は//example.com/...のようにプロトコルを省いた「プロトコル相対URL」を使い、閲覧中のプロトコルに自動で合わせる書き方が定番でした。ただ、サイト全体をHTTPSに寄せるのが当たり前になったいまでは、この書き方は非推奨とされています。素直にhttps://と明記したほうが、ローカルでfile://から開いたときの事故も防げますし、意図もはっきりします。

<!-- 昔の書き方(今は避ける) -->
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>

3. 数が多いなら CSP の upgrade-insecure-requests

古いサイトを移行したときなど、http://のURLがページ中に大量に残っていて手作業では追いきれない場合があります。そんなときはCSP(Content Security Policy)のupgrade-insecure-requestsディレクティブが便利です。これを付けると、ページ内のリソース読み込みリクエストをブラウザが自動でhttpsに読み替えてくれます。(MDN Web Docs: upgrade-insecure-requests

HTTPレスポンスヘッダーで指定するか、

Content-Security-Policy: upgrade-insecure-requests;

ヘッダーをいじれない環境なら、HTMLの<head>内にmetaタグで書く手もあります。

<meta http-equiv="Content-Security-Policy" content="upgrade-insecure-requests">

ただし、これはあくまで応急処置的な保険です。恒久対応としてはやはり、ソース側のURLをhttpsに直しておくのが本筋になります。

4. 外部に頼らずサーバーに置く手も

外部ライブラリをそれほど使っていないなら、jQueryなどのファイルを自分のサーバーに置いて、そこから読み込む方法もあります。読み込み元が自サイト(HTTPS)に統一されるので、混在コンテンツの心配自体がなくなります。外部CDNの障害に左右されないという副次的なメリットもあります。

確認しておきたいポイント

最後に、SSL対応後にJavaScriptが動かなくなったときの見直しポイントをまとめておきます。

  • JavaScript・CSS・画像などの外部リソースはhttpsで読み込む
  • プロトコル相対URL(//始まり)は使わず、https://と明記する
  • コンソール(F12 → Console)で、どのURLがブロックされているかを確認する
  • httpのURLが大量に残っているならupgrade-insecure-requestsで一括対応しつつ、順次ソースを直す
  • 外部ライブラリが少なければ、自サーバーにホストしてしまうのも手

動かない原因が見えないと焦りますが、混在コンテンツはコンソールを見ればほぼ一発で特定できます。まずはF12でConsoleを開いて、httpで読み込んでいるものがないかを確認する。そこから直していくのが結局いちばんの近道です。