Column

WordPressサイトを「見分ける」システムを作る— QuinQueインターン最初の2週間

5月から、QuinQueで長期インターンとして働き始めました。
配属されたプロジェクトは WordPressサイト診断ツール の開発です。最終的には、URLを渡すと自動でスキャンし、セキュリティ状態をスコアリングしてレポートを出すツールになる予定です。
この記事では、最初の2週間で自分が取り組んだことと、そこで感じたことを振り返ります。

目次

プロジェクトの全体像

ツールの大まかな流れはこんな感じです。

  1. CSVやCLIからURLを受け取る
  2. HTTPでHTMLを取得し、技術的な「指紋」を検出する
  3. 結果をDBに保存する
  4. スコアリング・ダッシュボード・レポートにつなげる

5月のスコープは「土台づくり(Walking Skeleton)」と「WordPress検出ロジック」の2週間分でした。

私は Intern B(Backend + AI担当) として、HTTPクライアントの実装、レート制限、BaseWorkerアーキテクチャ、そしてWordPress判定・バージョン・サーバー・CDN検出のロジックを担当しました。
Intern A(Shruti)はスキーマ設計・DB層・テーマとプラグインの検出を担当していて、同じFingerprintWorkerの中で役割を分担して並行に進めました。

Week 1:まず「動く骨組み」を作る(5/18–5/22)

最初の1週間のテーマは 土台づくり でした。

「とにかく1つのURLをCLIに渡して、SQLiteに保存できる状態にする」——それだけを目標にした週です。
検出ロジックはまだ何もない。でも、プロダクトの背骨を作る週でした。

この週に取り組んだ主な内容はこんな感じです。

  • packages/http-client — undiciを使ったHTTPクライアントの実装
  • packages/http-client — レート制限の実装(p-limit、ホスト別)
  • packages/workers — BaseWorker抽象クラスの設計
  • GitHub ActionsとCIの整備

正直に言うと、最初はあまりテンションが上がりませんでした。目に見える成果物が少なくて、地味な作業が続く感じがしていて。
でも、週が終わる頃には少し考えが変わっていました。

レート制限がなければ、本番サイトにリクエストを送りすぎてブロックされてしまう。BaseWorkerがなければ、後続の4つのWorkerがバラバラになる。「後で困らないためにやっておく仕事」の意味を、作りながら実感した週でした。

もう一つ気づいたことがあります。既存のコードベースに入るのは、ゼロから始めるのとは全然違う、ということです。
大学のプロジェクトでは、フォルダを作るところから自分で決められます。でもここでは、すでにある設計・パッケージ構成・命名規則を理解してから、その上に自分のコードを乗せていく必要があります。
最初の数日は、コードを書く前に「どこに何があるか」を読み解くことのほうが多かったです。この感覚は、やってみないとわからなかったです。

Week 2:WordPressを「見分ける」ロジックを作る(5/25–5/29)

2週目のテーマは WordPress検出ロジックの完成 です。ここからが自分にとって一番面白かったです。

WordPress判定

最初のタスクは「このサイトはWordPressで動いているか」を判定するロジックです。

正直、最初は「/wp-content/ があるかどうか調べるだけじゃないの?」と思っていました。でも実際には、1つの手がかりだけでは信頼できる判定ができません。/wp-content/ パス、wp-json REST APIエンドポイント、<meta name="generator">タグ、wp-login.php の存在
——これらをシグナルとして積み上げて、はじめて「たぶんWordPressだ」と言えるようになります。

バージョン・PHP・サーバー検出

次は、もう少し詳細な情報を取り出す検出器を作りました。

  • WordPressバージョン — <meta generator>readme.html、RSSフィードから
  • PHPバージョン — X-Powered-By ヘッダーから
  • Webサーバー — Server ヘッダーのパース

同じWordPressサイトでも、ヘッダーを全部消しているところもあれば、バージョンをそのまま出しているところもある。数サイト見ただけで「本番環境って本当にバラバラなんだ」と実感しました。教科書では「設定次第」と読んでいたことが、実際のHTTPレスポンスを見て初めて腑に落ちた感覚でした。

CDN検出

この週で個人的に一番達成感があったのが、CDN検出器 の実装でした。

レスポンスヘッダーのパターンを見て、Cloudflare・Fastly・Akamai などのCDNを判定するロジックです。実装そのものだけでなく、「要件を理解する→コードを書く→PRを出す→レビューフィードバックをもらう→CIを通す」という一連の流れを、この実装でしっかり経験できました。
GitHubのワークフローが「授業で使うもの」から「実際の開発のリズム」に変わった瞬間だったと思います。

WordPress.org API ヘルパー

プラグイン情報を公式メタデータで補完するために、WordPress.org APIと連携するヘルパーも実装しました。

ここで初めて意識したことがあります。外部APIは必ず失敗するときがある、という前提でコードを書くということです。レスポンスが返ってこないとき、データが不完全なとき——そういう状況を「例外」ではなく「仕様の一部」として扱う設計が必要だと、コードを書きながら理解しました。

予想外だった学び

既存のコードベースで働くということ

これが、この2週間で一番難しかったことかもしれません。

検出ロジックでも、TypeScriptでも、APIでもなく——「他の人が設計したアーキテクチャを理解する」ことです。大学では自分でゼロから設計できますが、実際の開発現場では、まず存在するものを理解してから貢献します。この感覚は、やってみないとわからなかったです。今もまだ身につけている最中のスキルだと思っています。

GitHubのワークフローを実際に使う

ブランチを切って、コミットして、PRを出して、レビューを受けて、CIが通るのを確認する——学校で習っていても、実際のプロダクト開発の中でやるのは全然違いました。「小さいPRをこまめに出す」という習慣も、この2週間でようやく自分のリズムになってきました。

簡単そうに見える問題ほど、深いことがある

WordPressの検出というのは一見シンプルに聞こえますが、実際には「複数の不完全な証拠をどう組み合わせるか」という設計の問題でした。コードを書く前に考える時間のほうが長かったくらいです。「手を動かす前に設計する」ということの意味を、初めて実感した2週間でした。

チームについて

QuinQueのメンターは、答えをすぐに教えてくれません。「まず自分で考えてみて」という文化があります。
最初は少し戸惑いましたが、今は「問題を自分の言葉で整理してから相談する」という習慣が少しずつついてきている気がします。それは単に「答えを探す力」ではなく、「初めて見る問題にどう向き合うか」という、もう少し深いところの力だと思います。

チーム全体として「まだ学んでいる途中でいい」という空気があります。わからないことがあっても焦らなくていい、質問は歓迎
——そういう環境で、気持ちよく取り組めています。毎朝のスタンドアップも短くて、でもちゃんと情報共有になっていて、いいリズムだと感じています。

6月以降に向けて

ロードマップによると、6月からはSecurity WorkerとスコアリングのフェーズにIntern Aと一緒に入っていく予定です。
XML-RPC露出、/wp-login.php チェック、バックアップファイル検出など、5月に作った検出ロジックが実際のセキュリティスコアの入力になっていきます。

「自分が作ったものが次のフェーズで使われていく」——その流れが見えてきたのが、今月いちばんの収穫でした。

さいごに

2週間を振り返ると、「コードを書く」以上のことを学んだ気がします。既存の設計を読み解いて、パッケージ境界を理解して、外部APIの失敗を仕様として扱い、PRを出してレビューを受けて——プロダクト開発の一連の流れを、小さいながらも体験できた2週間でした。

まだわからないことはたくさんありますが、「本物のプロダクトに触れながら考える力を鍛えたい」という人には、QuinQueのインターンは良い環境だと思います。

以下、原文

Building a System That Detects WordPress Sites — My First Two Weeks at QuinQue

I started my long term internship at QuinQue in May.

The project I joined is the development of a WordPress Site Diagnostic Tool. The end goal is a tool where you pass in a URL, it scans the site automatically, evaluates its security posture, and generates a report. In May, the focus was the very beginning of that building the foundation and the detection logic.

This post is a reflection on what I worked on during those first two weeks and what I took from them.

What the Project Is

The flow of the tool looks something like this:

  1. Accept URLs from a CSV file or CLI input
  2. Fetch HTML and detect technical "fingerprints" of the site
  3. Store results in a database
  4. Feed that data into scoring, dashboards, and reports

In May, the scope covered two phases: building a Walking Skeleton in Week 1, and completing the fingerprint detection layer in Week 2.

I was Intern B, working on the backend and AI side. My responsibilities covered the HTTP client, rate limiting, the BaseWorker architecture, and the detection logic for WordPress identification, version, server, and CDN. Intern A (Shruti) handled schemas, the storage layer, and theme and plugin detection. We worked in parallel within the same Fingerprint Worker same codebase, divided by detector.

Week 1: Making Something Work First (May 18–22) The theme for Week 1 was Walking Skeleton.

The goal was deliberately narrow: take one URL, pass it through the CLI, and save it to SQLite. No real detection logic yet just proving that the basic pipeline exists. The backbone of the product before anything else.

Here is what I worked on:

  • packages/http-client — an HTTP client wrapper built on undici
  • packages/http-client — per-host rate limiting using p-limit
  • packages/workers — the BaseWorker abstract class that all five future workers would extend
  • GitHub Actions and CI setup

To be honest, it did not feel very exciting at first. There was nothing visible to show, and the work felt quiet.

By the end of the week, though, I started to see it differently. Without rate limiting, the scanner would flood production websites with requests and get blocked. Without BaseWorker, all the detection workers that came later would have been inconsistent and hard to maintain. I had not really thought about infrastructure work this way before building it yourself makes you understand why it matters in a way that reading about it does not.

There was also something else I did not expect from this week. Joining an existing codebase is very different from starting from an empty folder. In university projects, I make all the architectural decisions myself from the beginning. Here, I had to first understand the existing conventions, package structure, and abstractions before I could add anything. The first few days were more about reading and mapping than writing. That adjustment took some getting used to.

Week 2: Teaching the System to Recognize WordPress (May 25–29)

The theme for Week 2 was Fingerprint complete. This was the part I found most interesting.

WordPress Detection

The first task was implementing logic to decide whether a site is running WordPress.
My initial assumption was something like: just check if /wp-content/ appears in the HTML. That should be enough. It was not.

A single signal is not reliable on its own. Sites can easily hide one indicator.
So the detector stacks multiple signals together: /wp-content/ paths, the wp-json REST API endpoint, the <meta name="generator"> tag, the presence of wp-login.php. Hiding one of these is straightforward. Hiding all of them is much harder.

Working through this made me realize something I had not consciously thought about before: when you write detection logic for real production sites, you cannot assume information will be visible. You have to design around the possibility that it is deliberately hidden.

Version, PHP, and Server Detection

After WordPress detection, I built detectors for more detailed information:

  • WordPress version — from <meta generator>, readme.html, and the RSS feed
  • PHP version — from the X-Powered-By header
  • Web server — parsed from the Server header

What struck me was how differently production sites behave. Two sites can both run WordPress and yet expose completely different information depending on how they are configured. Some strip all headers entirely. Others leave everything visible. Seeing this across even a handful of real sites gave me a more grounded sense of what "production environment" actually means — things I had read about became concrete.

CDN Detection

The implementation I felt most satisfied with was the CDN detector (PR #27).

The logic reads response header patterns to identify CDNs like Cloudflare, Fastly, and Akamai. Beyond the detection itself, this was the implementation where I most clearly went through the full development cycle: understanding the requirement, writing the code, opening a PR, receiving review feedback, and getting CI to pass. That end-to-end experience felt like an important step — the GitHub workflow shifted from something I knew in theory to something that became a natural rhythm.

WordPress.org API Helper

I also implemented a helper that enriches plugin data with official metadata from the WordPress.org API.

This brought something into focus that I had heard before but not really internalized: external APIs are not always available, and responses are not always complete. Writing code that treats those situations as expected behavior — not edge cases — is a different mindset from what most university projects require. I understood it more concretely here.

What I Did Not Expect to Learn

Working inside an existing codebase

This was probably the biggest challenge of the two weeks not the detection logic, not TypeScript, but understanding an architecture someone else designed. In university, you start from scratch and make every decision yourself. Here, the job is first to understand what already exists, and then contribute within it. That is a different skill, and one I am still developing.

Using GitHub as part of a real workflow

Branching, committing, opening PRs, responding to review comments, watching CI pass or fail I had done these things before in theory. Doing them inside an actual product, with other developers depending on the same codebase, felt genuinely different. Building the habit of small, frequent PRs was one of the more practical things I took from these two weeks.

Simple-looking problems are often not simple

Detecting WordPress sounds like a small problem until you start thinking about how production sites hide information and how many ways a detection approach can fail. The thinking that happened before writing code was at least as important as the code itself.

The Team

QuinQue's mentors do not hand you the answer straight away. The expectation is that you think through the problem first, form your own understanding, and then bring your question. At the start, that felt a little unfamiliar. Over time, I have noticed it is building a different kind of skill — not just knowing how to solve a specific problem, but knowing how to approach one you have not seen before.

Beyond that, the team is genuinely kind and approachable. Nobody makes you feel behind for not knowing something. Daily standup is short but keeps everyone aligned, and there is an overall sense that learning is part of the work, not something you were expected to have already finished before arriving.

Looking Ahead

According to the roadmap, June moves into the Security Worker and scoring phase — XML-RPC exposure, /wp-login.php checks, backup file detection. The fingerprint data from May becomes the input for actual vulnerability scoring.

Seeing that connection take shape — knowing that what was built in Week 1 and Week 2 will eventually feed into dashboards and reports — is probably the most motivating part of where this project is going.

Final Thoughts

Looking back, these two weeks were about more than writing code. Understanding an existing codebase, working through a real PR and review cycle, designing for failure, thinking about reliability — these were the things that shaped the experience more than any individual feature.

There is still a lot ahead, but I feel like I have a much clearer picture of what professional software development actually involves. And I am looking forward to what comes next.

If you are looking for an internship where you work on real product code and grow in a team that genuinely supports learning, QuinQue has been that for me.

目次