2026.10.05

コピーボタンがhttpで動かない — Clipboard APIのセキュアコンテキスト制約と回避策

コピーボタンがhttpで動かない — Clipboard APIのセキュアコンテキスト制約と回避策

コードブロックの「コピー」ボタンをClipboard APIで実装したら、本番では動くのに検証環境では反応しない——原因はバグではなく、Clipboard APIがhttps(セキュアコンテキスト)でしか使えない仕様です。この記事では、当社ブログのCOPYボタン実装で実際にこの制約を踏んだ経緯と、http環境でも動くフォールバック付きの実装を紹介します。

背景: ブログにコードのCOPYボタンを付けた

当社のTECH-BLOGでは、コードブロックにマウスを乗せるとCOPYボタンが現れ、クリックでコード全文をクリップボードへコピーできるようにしています。実装はシンプルで、記事内のpre要素をラップしてボタンを差し込むだけです。

document.querySelectorAll('.article pre').forEach(function(pre){
  var wrap = document.createElement('div'); wrap.className = 'code-wrap';
  pre.parentNode.insertBefore(wrap, pre); wrap.appendChild(pre);
  var btn = document.createElement('button'); btn.className = 'code-copy'; btn.type = 'button'; btn.textContent = 'COPY';
  btn.addEventListener('click', function(){
    ndCopy(pre.querySelector('code').innerText).then(function(){
      btn.textContent = 'COPIED!'; btn.classList.add('done');
      setTimeout(function(){ btn.textContent = 'COPY'; btn.classList.remove('done'); }, 1600);
    });
  });
  wrap.appendChild(btn);
});

コピー処理の本体は、最初はnavigator.clipboard.writeText()を呼ぶだけの1行でした。

ハマりポイント: Clipboard APIはhttpsでしか存在しない

当社サイトの構築中、検証はIP制限をかけたステージング環境で行っていました。SSL証明書は設定済みでしたが、WordPressのURL設定(siteurl)をhttpsへ切り替える前だったため、サイト内は実質httpで動いている状態です。そこでCOPYボタンをクリックしても、何も起きませんでした。

原因はClipboard APIの仕様です。navigator.clipboardはセキュアコンテキスト(https、またはlocalhost)でのみ提供されます(MDN: クリップボード API / 保護されたコンテキスト)。httpのページではプロパティ自体がundefinedになるため、「エラーが出る」のではなく「静かに何も起きない」のが厄介なところです。開発をlocalhostで行っていると気づけず、httpの検証サーバーや社内環境に載せた瞬間に発覚する、という踏み方をします。

解決: セキュアコンテキストを判定してフォールバック

httpsではClipboard APIを使い、httpでは旧来のdocument.execCommand('copy')に切り替える関数を用意しました。どちらもPromiseを返すので、呼び出し側は同じ書き方のままです。

// クリップボード書き込み(https=Clipboard API / httpステージング=execCommandフォールバック)
function ndCopy(text){
  if (navigator.clipboard && window.isSecureContext) return navigator.clipboard.writeText(text);
  return new Promise(function(res, rej){
    var ta = document.createElement('textarea'); ta.value = text;
    ta.style.position = 'fixed'; ta.style.opacity = '0';
    document.body.appendChild(ta); ta.select();
    try { document.execCommand('copy') ? res() : rej(); } catch (e) { rej(e); }
    document.body.removeChild(ta);
  });
}

要点は3つです。

同じ関数を「記事リンクをコピー」ボタンでも使い回しています。コピー先の文字列が変わるだけで、呼び出し方は同一です。

検証

httpのステージング環境で、COPYボタン(→COPIED!表示)と「リンクをコピー」(→コピーしました表示)の両方を実際にクリックし、クリップボードに正しい内容が入ることを確認しました。本番のhttps環境ではClipboard API側のパスが使われるため、フォールバックの品質に依存しない構成です。

まとめ

Clipboard APIが「httpでは静かに動かない」ことを知っていれば、原因調査は数分で済みます。isSecureContextで分岐してexecCommandへフォールバックする10行少々を関数にしておけば、検証環境から本番まで同じコードで通せます。

Webサイトの構築や、こうした細部まで動くUIの実装のご相談は、SERVICESからお気軽にどうぞ。

← TECH-BLOG一覧へ