DAICHI OKAMOTO
BLOG 一覧へ

2026.07.09

サッカーHPを「フォーメーション」で診断するツールをつくった ── ウェブつく診断

サッカーHPを「フォーメーション」で診断するツールをつくった ── ウェブつく診断

サッカーチームのホームページを、サッカーのフォーメーション(GK / DF / MF / FW)になぞらえて4ラインで採点する無料の診断ツールを作りました。使い方はシンプルで、チームHPのURLを1つ入力するだけ。数秒のスキャン演出のあと、総合スコアと4枚のフォーメーションカードが並んだ結果画面が出ます。ホームページ制作サービス「ウェブつく」の入口として、まず弱点を知ってもらい相談につなげる集客ツールです。

どんなツールか

診断は次の4ラインで見ます。GK(守備の要)=安全性(SSL・混在コンテンツ・セキュリティヘッダー)、DF(ディフェンスライン)=スマホ対応・表示速度・検索対策(viewport・表示速度・title/description・OGP)、MF(中盤の構成力)=コンテンツの充実度(選手紹介・試合結果・スケジュール・お知らせ)、FW(攻撃)=集客・問い合わせ導線(体験募集・問い合わせ窓口・SNS連携・写真動画)。各カードに「できていること」と「強化ポイント」が並び、専門用語はやさしい言葉に置き換えつつ、詳しい人向けには内訳を折りたたみで見せています。

使った技術

このツールで一番語りたいのは、あえてフレームワークを使わなかったことです。サーバーは素の Node.js(v20)+ Express(ローカル用)で、本番は Vercel のサーバーレス関数。診断対象のHTML解析には cheerio、HTTPクライアントは undici、計測DBに Upstash Redis を使っています。フロントはフレームワーク・ビルドツール・npmパッケージなしの1枚の HTML/CSS/JS。スタイルは素のCSS、アニメーションはCSSの keyframes だけです。

普段は React / Next.js で作ることが多いのですが、この診断ツールは「URLを受け取って→HTMLを取ってきて→採点して→JSONを返す」だけの薄いサーバーと、結果を描くだけの1画面。それに対して React はオーバースペックだと判断しました。依存を極端に絞ったことで、コールドスタートも速く、仕組み全体を頭に入れたまま作れています。ローカル開発すら nodemon を使わず、Node標準の node --watch で済ませました。

何ができるのか

  • URL入力だけのサッカーHP無料診断(4ライン+総合スコア/グレードS〜D)
  • サーバー側で対象サイトを実際に取得して自動採点(二値ではなく部分点で採点)
  • スコア帯に応じた「監督のひとこと」自動生成と、一番弱いラインの特定
  • やさしい言葉で表示しつつ、Web担当者向けにはチェック内訳を折りたたみ表示
  • スタジアムの戦術ボード風ダークUI(走査線のスキャン演出、ピッチ背景)
  • 改善アドバイスをLINE友だち追加で解放するソフトゲート(localStorageで保持)
  • SNSシェア(LINE / X / Facebook / ネイティブ共有シート)
  • 自前の計測ダッシュボード(訪問数・診断数・訪問→診断率・LINE登録のCV率・日別推移・診断サイトのランキング)

難しかったところ ①:他人のURLをサーバーが叩く危うさ(SSRF対策)

このツールの勘所は、ユーザーが入れた任意のURLをサーバー側でfetchするという構造そのものです。無防備だと、クラウドのメタデータIP(169.254.169.254)や社内アドレスを入れられて内部を覗かれる踏み台にされます。そこで、入力URLの正規化とプライベートアドレスのブロックに加え、DNSで名前解決した後のIPまで検査します。10進・16進のIP表記、IPv4射影IPv6、CGNAT帯、リンクローカルまで潰す——ここがDNSリバインディング対策の本丸です。さらにfetchを redirect: 'manual' にして、1ホップずつ手動でリダイレクトを追い、各ホップでIPを再検証します。follow のままだと「公開URL→302で内部IP」でガードを回避されてしまうためです。

難しかったところ ②:一番ハマったのは証明書(TLSルートとVercelのバンドル)

じつは今回一番時間を溶かしたのは、SSRFでも採点ロジックでもなくTLS証明書でした。Let's Encrypt の新ルート「ISRG Root YR」が Node 同梱のルート一覧にまだ無く、その配下のサイト(自社の web-tsuku.com も含む)を診断すると UNABLE_TO_GET_ISSUER_CERT_LOCALLY で失敗する。対処は、Node標準のルートを置き換えず、そこに不足ルートを足したCAリストを undici の Agent に渡すこと。

const httpsDispatcher = new Agent({
  connect: { ca: [...tls.rootCertificates, ...EXTRA_ROOTS] },
});

さらに Vercel 特有の罠として、証明書PEMを readFileSync で外部ファイルから読む書き方だと、@vercel/node のバンドラが依存を検出できず本番でファイルごと欠落します。結局、PEMを文字列としてコードに直接埋め込むことで解決しました。

難しかったところ ③:採点の精度(メニューに項目が並んでるだけ、を見抜く)

多くのチームHPは全ページ共通のヘッダー/フッターに「選手紹介|試合結果|お知らせ」を並べています。素直にHTML全体を検索すると、中身が空でもメニューの文字だけで満点になってしまう。そこで採点前に nav, header, footer をDOMから除去してから本文を判定します。リンクもURLの英単語による誤検出を避けてテキストだけを見る。採点も0/1ではなく部分点方式で、たとえば表示速度は速い=満点/やや遅い=0.6点/遅い=0点と段階評価にしています。加えて、送信するUser-Agentを実ブラウザ相当にしないと Cloudflare などのWAFに403で弾かれ、対象のチームHPが軒並み診断不能になるため、そこも合わせました。

難しかったところ ④:「ちゃんと診断した感」の演出

診断自体は速いと1秒未満で終わりますが、一瞬で結果が出ると「本当に見てくれたの?」という感覚になります。そこで実際のデータ取得と最低2.4秒のスキャン演出を Promise.all で並走させ、走査線アニメと日本語ログを順に出すことで、速くても最低限の"診断している間"を見せるようにしました。速度と体験のバランスをあえてコードで作った部分です。

まとめ

派手なフレームワークを使う仕事ではなかったぶん、「目的に対して技術を最小限に選ぶ」「他人の入力を受け取るサーバーを安全に作る」という地に足のついた部分がそのまま出たプロジェクトでした。特に証明書まわりのように、動いて当たり前に見えるところで一番詰まる——という学びが濃かったです。URLを入れるだけで、チームのHPの弱点をサッカーの言葉で理解してもらえる。制作の相談前に、まず価値を先に渡せる入口ができました。