コンテンツにスキップ

ntorelabo

Front End Developer

  • pickup
  • WordPress
  • PHP
    • PHP技術者認定試験
  • Docker
  • Next.js
  • AWS
  • Laravel
  • site map
  • search

ntorelabo

Front End Developer

  • search
  • pickup
  • WordPress
  • PHP
    • PHP技術者認定試験
  • Docker
  • Next.js
  • AWS
  • Laravel
  • site map

プロキシサーバーのフィルターが原因?アクセスできないサイトを突き止める方法、自作拡張機能で確認

  • ホームページ
  • プロキシサーバーのフィルターが原因?アクセスできないサイトを突き止める方法、自作拡張機能で確認
  • 12月, 木, 2024
  • JavaScript
プロキシサーバーのフィルターが原因?アクセスできないサイトを突き止める方法、自作拡張機能で確認

はじめに

社内ネットワークや学校などの環境では、インターネットアクセスの管理・セキュリティ強化のためにプロキシサーバーが導入されていることがよくあります。ときにはこのプロキシサーバーのフィルタリング機能が原因で、特定のWebサイトやサービスにアクセスできなくなることも。

Contents [hide]

  • 1 プロキシサーバーとは
    • 1.1 代理サーバーとしての役割
    • 1.2 フィルタリングの仕組み
    • 1.3 プロキシサーバーのフィルターでサイトにアクセスできないケース
  • 2 検証ツールを使って原因(ホワイトリスト)を突き止める手順
    • 2.1 開発者ツール(検証画面)の起動
    • 2.2 Networkタブの確認
    • 2.3 Consoleタブのエラーを確認
    • 2.4 実際にブロックされているURLをブラウザで直接開いてみる
  • 3 補足:コマンドラインの活用例
    • 3.1 curlでの確認
    • 3.2 pingでの接続確認
  • 4 自作Chromeの拡張機能でリソースURLリストを取得
    • 4.1 構成ファイル
    • 4.2 拡張機能のインストール方法:
    • 4.3 主な処理の特徴:
    • 4.4 WebRequest API

プロキシサーバーとは

代理サーバーとしての役割

プロキシサーバー(proxy server)とは、ユーザーのリクエスト(通信)を“代理”として受け取り、インターネット上の目的のサーバーにアクセスする役割を担うサーバーのことです。
一般的に、以下の目的で利用されます。

キャッシュ機能: よくアクセスするサイトのデータをプロキシ側で一時保存し、ネットワーク帯域を節約する。
セキュリティ・フィルタリング: 社内ルールや企業ポリシーに反するサイトや有害サイトへのアクセスを制限する。
ログ管理: どの端末がどのサイトにアクセスしているかを記録・監視する。

フィルタリングの仕組み

プロキシサーバーにはURLやキーワードをもとにアクセスを許可・ブロックする機能が搭載されている場合があります。たとえば「SNSサイト禁止」「特定のカテゴリのサイトは禁止」のように設定されている場合、該当のサイトにアクセスしようとすると、ブラウザ上にブロック画面が表示されたり“接続エラー”になったりすることがあります。

プロキシサーバーのフィルターでサイトにアクセスできないケース

よくある症状

プロキシサーバーが原因で特定のサイトにアクセスできないとき、以下のような症状が現れることが多いです。

接続タイムアウト: ブラウザ上でエラーが表示され、ページがまったく表示されない。
ブロック画面が表示される: 「このサイトはアクセス禁止です」などの専用ページが表示される。
部分的なリソースの読み込み失敗: サイトは見られるが、画像や特定のスクリプトだけが読み込めない。
こういった場合、プロキシサーバーの管理画面を確認できれば、どのURLがブロックされたのか一目でわかることがあります。しかし、組織の方針などで管理画面にアクセスできないケースも少なくありません。

検証ツールを使って原因(ホワイトリスト)を突き止める手順

ここでは、プロキシサーバーの管理画面に頼らずに、Chromeの開発者ツール(検証ツール)などを使ってフィルタリングの原因を調べる方法を紹介します。
他のブラウザ(Firefox、Edgeなど)でも同様の手順で確認できますが、ここではChromeを例とします。

開発者ツール(検証画面)の起動

Chromeを開き、アクセスしたいサイトに移動。
キーボードのF12キー、または右上のメニューから「その他のツール > デベロッパーツール」を選択。

Networkタブの確認

開発者ツールを開いたら、上部のタブからNetworkを選択する。
すでにページが読み込まれている場合は、いったんリロード(もしくは「Disable cache」にチェックを入れてリロード)する。
すると、該当サイトの読み込みリクエストの一覧が表示される。
ここで注目すべきは、

ステータスコード: 200(OK)、404(Not Found)、403(Forbidden)、407(Proxy Authentication Required)など
URL: ブロックの原因となっているかもしれないドメインやファイルパス
もしフィルタリングが作動している場合、特定のリソースだけ403や404、あるいは proxy error が返っているといったことが確認できるでしょう。

Consoleタブのエラーを確認

Networkタブにエラーがない場合でも、Consoleタブにエラーメッセージが残っていることがあります。

ブロックされたリソースのURL
「Failed to load resource: the server responded with a status of 403」などの文言
これらのエラー文言から、フィルタリングによるエラーかどうか判断材料になります。

実際にブロックされているURLをブラウザで直接開いてみる

NetworkタブやConsoleタブで見つけたエラーURLに対して、

ブラウザのアドレスバーに直接貼り付けてアクセス
「このURL自体がブロックされているのか」を確認
これにより、主サイト自体は表示できても、広告や解析用のドメインがブロックされてページが中途半端に崩れているというケースも特定できるはずです。

補足:コマンドラインの活用例

もし開発者ツールだけでは原因が分かりづらい場合、curlやpingなどのコマンドラインツールを使っても検証ができます。

curlでの確認

ターミナルやコマンドプロンプトで下記のように入力します:

curl -I http://アクセスしたいサイトのURL

ヘッダー情報(ステータスコードなど)が返ってきますが、もしプロキシが介入している場合は「プロキシのエラーメッセージ」「Access Denied」などが返ってくることがあります。

pingでの接続確認

ping アクセスしたいサイトのドメイン

pingはICMPプロトコルを使っているため、Webアクセスのフィルタリングと挙動が異なることもありますが、サーバー側に全く応答がないのか、そもそもDNS解決できないのか、といった点を切り分けるヒントになります。

adobe ホワイトリスト プロキシサーバー 拡張機能 参照URL一覧

自作Chromeの拡張機能でリソースURLリストを取得

Webページが読み込む全てのリソース(画像、スクリプト、スタイルシート、API呼び出しなど)のURLを取得

構成ファイル

page-resources-monitor/
├── manifest.json      # 拡張機能の設定ファイル
├── background.js      # バックグラウンドでリクエストを監視
├── popup.html         # ポップアップのUI
└── popup.js          # ポップアップの動作制御

manifest.json

{
  "manifest_version": 3,
  "name": "Page Resources Monitor",
  "version": "1.0",
  "description": "ページが読み込むすべてのリソースのURLを表示します",
  "permissions": [
    "activeTab",
    "declarativeNetRequest",
    "scripting",
    "webRequest",
    "webNavigation"
  ],
  "host_permissions": [
    "*://*/*",
    "https://*/*"
  ],
  "action": {
    "default_popup": "popup.html"
  },
  "background": {
    "service_worker": "background.js",
    "type": "module"
  }
}

background.js

let requests = new Map();

function addRequest(tabId, requestData) {
  if (!requests.has(tabId)) {
    requests.set(tabId, []);
  }
  requests.get(tabId).push(requestData);
}

chrome.webRequest.onBeforeRequest.addListener(
  function(details) {
    console.log('Captured request:', details);
    if (details.tabId !== -1) {
      const requestData = {
        url: details.url,
        type: details.type,
        method: details.method || 'GET',
        timestamp: details.timeStamp,
        initiator: details.initiator || '',
        frameId: details.frameId || 0,
        parentFrameId: details.parentFrameId || 0,
        documentUrl: details.documentUrl || ''
      };
      
      addRequest(details.tabId, requestData);
    }
  },
  {
    urls: ["<all_urls>"],
    types: [
      "main_frame",
      "sub_frame",
      "stylesheet",
      "script",
      "image",
      "font",
      "object",
      "xmlhttprequest",
      "ping",
      "csp_report",
      "media",
      "websocket",
      "other"
    ]
  }
);

// タブが切り替わった時のリクエスト情報保持
chrome.tabs.onActivated.addListener(function(activeInfo) {
  if (!requests.has(activeInfo.tabId)) {
    requests.set(activeInfo.tabId, []);
  }
});

// メッセージリスナー
chrome.runtime.onMessage.addListener(
  function(request, sender, sendResponse) {
    if (request.action === "getRequests") {
      chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
        if (tabs && tabs[0]) {
          const currentTabId = tabs[0].id;
          const tabRequests = requests.get(currentTabId) || [];
          sendResponse({requests: tabRequests});
        } else {
          sendResponse({requests: []});
        }
      });
      return true;
    }
  }
);

popup.html

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <style>
      body {
        width: 800px;
        padding: 10px;
      }
      .resource-list {
        max-height: 600px;
        overflow-y: auto;
      }
      .resource-item {
        margin: 5px 0;
        padding: 5px;
        border-bottom: 1px solid #eee;
      }
      .type-filter {
        margin: 10px 0;
      }
      .url {
        word-break: break-all;
      }
      .type {
        color: #666;
        font-size: 0.9em;
      }
      button {
        margin: 5px;
        padding: 8px 16px;
        background-color: #4caf50;
        color: white;
        border: none;
        border-radius: 4px;
        cursor: pointer;
      }
      button:hover {
        background-color: #45a049;
      }
      #exportCSV {
        background-color: #2196f3;
      }
      #exportCSV:hover {
        background-color: #1976d2;
      }
      #exportJSON {
        background-color: #ff9800;
      }
      #exportJSON:hover {
        background-color: #f57c00;
      }
      .stats {
        margin: 10px 0;
        padding: 10px;
        background: #f5f5f5;
      }
    </style>
  </head>
  <body>
    <h3>ページリソース一覧</h3>
    <div class="type-filter">
      <label>リソースタイプでフィルター:</label>
      <select id="typeFilter">
        <option value="all">すべて</option>
        <option value="script">スクリプト</option>
        <option value="stylesheet">スタイルシート</option>
        <option value="image">画像</option>
        <option value="font">フォント</option>
        <option value="xmlhttprequest">API/XHR</option>
        <option value="websocket">WebSocket</option>
        <option value="other">その他</option>
      </select>
    </div>
    <div class="stats" id="stats"></div>
    <button id="copyButton">URLリストをコピー</button>
    <button id="exportCSV">CSVでエクスポート</button>
    <button id="exportJSON">JSONでエクスポート</button>
    <div id="resourceList" class="resource-list"></div>
    <script src="popup.js"></script>
  </body>
</html>

popup.js

document.addEventListener('DOMContentLoaded', function() {
    const resourceList = document.getElementById('resourceList');
    const typeFilter = document.getElementById('typeFilter');
    const stats = document.getElementById('stats');
    const copyButton = document.getElementById('copyButton');
    let allRequests = [];
  
    function updateList(requests, filterType = 'all') {
      resourceList.innerHTML = '';
      
      if (!Array.isArray(requests)) {
        console.error('requests is not an array:', requests);
        requests = [];
      }
  
      const filteredRequests = filterType === 'all' 
        ? requests 
        : requests.filter(r => r.type === filterType);
  
      filteredRequests.forEach(request => {
        const div = document.createElement('div');
        div.className = 'resource-item';
        div.innerHTML = `
          <div class="url">${request.url}</div>
          <div class="type">タイプ: ${request.type} (${request.method || 'GET'})</div>
          <div class="initiator">発信元: ${request.initiator || '不明'}</div>
        `;
        resourceList.appendChild(div);
      });
  
      const typeCount = requests.reduce((acc, r) => {
        acc[r.type] = (acc[r.type] || 0) + 1;
        return acc;
      }, {});
  
      stats.innerHTML = `
        <strong>統計情報:</strong><br>
        総リクエスト数: ${requests.length}<br>
        タイプ別: ${Object.entries(typeCount)
          .map(([type, count]) => `${type}: ${count}`)
          .join(', ')}
      `;
    }
  
    function refreshData() {
      chrome.runtime.sendMessage({action: "getRequests"}, function(response) {
        console.log('Received response:', response);
        if (response && response.requests) {
          allRequests = response.requests;
          updateList(allRequests, typeFilter.value);
        }
      });
    }
  
    refreshData();
    setInterval(refreshData, 3000);
  
    typeFilter.addEventListener('change', function() {
      updateList(allRequests, this.value);
    });
  
    copyButton.addEventListener('click', function() {
      const urls = allRequests.map(r => r.url).join('\n');
      navigator.clipboard.writeText(urls).then(function() {
        copyButton.textContent = 'コピーしました!';
        setTimeout(function() {
          copyButton.textContent = 'URLリストをコピー';
        }, 2000);
      });
    });
  });

拡張機能のインストール方法:

  • Chrome で chrome://extensions/ を開きます
  • 右上の「デベロッパーモード」を有効にします
  • 上記のフォルダを「パッケージ化されていない拡張機能を読み込む」でインポートします

主な処理の特徴:

  1. バックグラウンドプロセス(background.js)
  • 常時動作してリクエストを監視
  • Mapデータ構造でタブごとにリクエストを保持
  • メッセージングAPIでポップアップとデータをやり取り
  1. ポップアップUI(popup.html/js)
  • ユーザーインターフェースの提供
  • リアルタイムデータ更新
  • フィルタリングと表示制御
  • データエクスポート機能
background.js Webリクエスト監視 データ保存 popup.html/js UI表示 データ取得・更新 データ取得 Webブラウザ リクエスト情報

WebRequest API

WebRequest APIは、Chrome拡張機能がブラウザのネットワークリクエストを監視・分析できるAPIです。

取得できる情報:

  • URLやメソッド
  • リクエスト/レスポンスヘッダー
  • リクエストタイプ(image, script, stylesheet等)
  • リクエスト元のタブID
pickup
最近の投稿
  • WordPressサイトのサブディレクトリ→サブドメイン移行手順 2025年7月6日
  • Laravelでリッチテキストエディタで使いやすい画像アップロード機能を実装(TinyMCE) 2025年6月22日
  • 【PHP】出力バッファリング制御 2025年5月22日
  • basic認証のパスワードを忘れた場合 2025年5月14日
  • PHP 8.0〜から実装で便利な機能(名前付き引数、match) 2025年5月11日
  • さくらインターネットからWHOIS登録メールアドレスの利用廃止のメールが来た 2025年5月9日
  • スムーズにスクロールする処理をjQueryで実装 2025年5月5日
  • Gemini for Google Workspace 活用術 2025年4月24日
  • Google Cloud の始め方 2025年4月20日
  • 【PHP】switch分の書き方、PHP 7.3 から PHP7.4の変更点 2025年4月19日
  • 【PHP】新しい方の指定方法「nullable」 2025年3月25日
  • 「PHPでJSONを扱う」json_encodeとjson_decode完全ガイド 2025年3月23日
  • 【WordPress自作テーマ開発】固定ページを自動作成する方法(下層ページ実装) 2025年3月18日
  • 【PHP】プロトコル / ラッパーについて(file:// php:// http:// https://) 2025年3月17日
  • 【PHP】stdClass を使用して(object) でキャスト 2025年3月16日
  • モダンなPHPコードで使用!無名関数の使い方 2025年3月15日
  • Cursor 便利な機能まとめ、Project Rules 2025年3月15日
  • PHP セッション名の変更方法と php.ini の記述方法まとめ 2025年3月12日
  • 【PHP】関数内でグローバル変数を使用するためには global キーワード 2025年3月10日
  • 【php8上級/準上級試験】模擬問題解説 問題30 strtotime() 2025年3月4日
About Us

Ready To Start Work With Us?

Felis consequat magnis est fames sagittis ultrices placerat sodales porttitor quisque.

Get a Quote
recent posts
  • WordPressサイトのサブディレクトリ→サブドメイン移行手順
  • Laravelでリッチテキストエディタで使いやすい画像アップロード機能を実装(TinyMCE)
  • 【PHP】出力バッファリング制御
  • basic認証のパスワードを忘れた場合
  • PHP 8.0〜から実装で便利な機能(名前付き引数、match)
  • さくらインターネットからWHOIS登録メールアドレスの利用廃止のメールが来た
  • スムーズにスクロールする処理をjQueryで実装
  • Gemini for Google Workspace 活用術
Opening Hours
Week Days 10:00 - 17:00
Saturday 10:00 - 15:00
Sunday Day Off
Contact us
popular posts
  • WordPressで画像を拡大表示するなら「Firelight Lightbox」(旧名:Easy Fancybox) 1.1k件のビュー
  • 【WordPress】カスタムフィールド作成の仕方「ACF」Advanced Custom Fieldsの使用法 739件のビュー
  • 【チートシート】Linuc Lv1-102 実務での使用例も解説 597件のビュー
  • My-Snow-Monkey使用例、子テーマによるカスタマイズ 511件のビュー
  • 【WordPressエラー】「Warning: Undefined variable $post in …」「Warning: Attempt to read property “ID” on null in …」 457件のビュー
  • Twitter、Facebookのタイムラインを埋め込む 415件のビュー
  • 【Next.js】ライブラリ使わないでカルーセルスライダー 300件のビュー
  • ローカル、グローバルIPアドレスの違いと確認手順 248件のビュー
  • AWS認定ソリューションアーキテクト-アソシエイトレベル 248件のビュー
  • WordPress自作、カスタマイズ 228件のビュー
Search
Copyright © 2025 ntorelabo | Powered by Desert Themes