Lighthouse CI(LHCI)は、プルリクエストやデプロイのたびにLighthouseを自動で走らせ、スコアや監査結果が基準を割ったらビルドを落とすためのツールです。npmパッケージ @lhci/cli の lhci autorun 1本で「計測(collect)→判定(assert)→保存(upload)」が回ります。
この記事では2026年10月2日時点の最新版 @lhci/cli 0.15.1 を前提に、lighthouserc.json の書き方、assertの判定規則、GitHub Actions・GitLab CIへの組み込み、結果の保存先を整理します。presetの中身はパッケージのソースで確認し、判定の挙動は手元で lhci autorun を3回実行して確かめました。
まとめ:Lighthouse CI導入の要点
- 最新は @lhci/cli 0.15.1(2025年6月公開)。同梱のLighthouseは12.6.1に固定されており、Lighthouse単体の最新13.5.0(2026年9月18日)とは版が違います。
- 最小構成は
lighthouserc.jsonに計測対象(staticDistDirかurl)とassertのpresetを書き、npx lhci autorunを実行するだけです。計測回数の既定は3回。 - presetだけではTBTの悪化でビルドは落ちません。
total-blocking-timeはpresetでoffなので、性能の門番にするならcategories:performanceやtotal-blocking-timeを明示的にerrorで足します。 - オプション無しのassertは「3回のうち最良値が0.9以上か」で判定されます。ドキュメントは
minScore: 1と書いていますが、0.15.1の実装の既定は0.9です。 - temporary-public-storageは誰でも閲覧できる公開ストレージで、運営はGoogleではなくEris Ventures LLCです。社内ページや未公開URLのレポートは、filesystemか自前のLHCIサーバーへ保存します。
- GitHubのステータスチェックは
LHCI_GITHUB_APP_TOKEN(GitHub App)かLHCI_GITHUB_TOKEN(repo:statusスコープのPAT)で付きます。
Lighthouse CIの仕組みと単体のLighthouseとの違い
ブラウザのDevToolsやPageSpeed Insightsで動かすLighthouseは、1回の計測結果を人が読むための道具です。Lighthouse CIはそれをCIに載せ、毎回同じ条件で複数回計測して、結果を基準値と機械的に比べます。コード変更で画像の最適化漏れや重いスクリプトが入ったとき、マージ前に検出できるのが導入する理由です。
コマンドは役割ごとに分かれていて、autorun がその3つをまとめて呼びます。
| コマンド | 役割 | 主な設定 |
|---|---|---|
lhci collect |
Lighthouseを実行し結果を保存 | url・staticDistDir・numberOfRuns |
lhci assert |
基準と比べて合否を判定 | preset・assertions・budgetsFile |
lhci upload |
結果を保存先へ送る | target(lhci/temporary-public-storage/filesystem) |
lhci autorun |
上の3つを設定に応じて実行 | 子コマンドの設定を引き継ぐ |
lhci server |
履歴と差分を見るダッシュボード | storage(sqlite/mysql/postgres) |
lhci healthcheck |
設定とChromeの有無を事前確認 | なし |
計測されるのはラボデータです。実ユーザーの体感(フィールドデータ)とは別物で、INPのように操作が要る指標はナビゲーション計測では取れません。指標ごとのラボとフィールドの扱いはINPの基準値と3区間・改善手順で、スコアの配点はLighthouseスコアの見方と改善方法で解説しています。
同梱Lighthouseの版:0.15.1は12.6.1固定
@lhci/cli 0.15.1の package.json は依存を "lighthouse": "12.6.1" と完全固定しています。Lighthouse本体はその後13系に進み、13.0.0で動作要件がNode.js 22.19以上に引き上げられています。一方LHCIのリリースは0.15.1(2025年6月)で止まっています。
このため、LHCIのスコアはChromeのDevToolsやPageSpeed Insightsで見る最新Lighthouseの結果と一致しないことがあります。13系で入った監査やカテゴリはLHCIでは計測されません。CIの値は「同じ版で前回と比べる」用途に限り、他ツールの点数と突き合わせないのが安全です。
ローカルでの導入手順とlhci autorunの最小構成
12.6.1の動作要件はNode.js 18.20以上です。まず開発依存として入れます。--save-exact を付けないと package.json には ^0.15.1 で記録されるので、版を固定するなら付けます。
npm install -D --save-exact @lhci/[email protected]
npx lhci --version # 0.15.1
npx lhci healthcheck
設定ファイルはリポジトリ直下に置きます。lighthouserc.json・.lighthouserc.json・lighthouserc.js・.lighthouserc.yml などの名前が自動で読まれます。静的サイトなら、ビルド成果物のディレクトリを渡すだけで組み込みサーバーが立ちます。
{
"ci": {
"collect": {
"staticDistDir": "./dist",
"numberOfRuns": 3
},
"assert": {
"preset": "lighthouse:recommended"
},
"upload": {
"target": "filesystem",
"outputDir": "./lhci-report"
}
}
}
計測対象の指定方法は3通りあり、アプリの形で選びます。
staticDistDir:HTMLを書き出す静的サイト向け。対象URLを省くとHTMLを最大5件(maxAutodiscoverUrls)まで自動で拾います。autorunではdist・build・out・publicの順に自動で探されます。startServerCommandとurl:Next.jsのSSRなどサーバーが要るアプリ向け。起動ログにlistenかreadyが出るまで既定で10秒待ちます。staticDistDirとは併用しません。urlのみ:ステージングなど既に動いている環境を測る場合。
autorun に子コマンドの設定をCLIで渡すときは、lhci autorun --collect.numberOfRuns=5 のように必ず = でつなぎます。空白区切りは受け付けません。
assertの書き方とpresetの選び方
assertは監査IDごとに "監査ID": "レベル" か "監査ID": ["レベル", {オプション}] で書きます。レベルは3段階で、warn は表示だけで終了コードを変えず、error だけがビルドを落とします。
判定条件とaggregationMethodの既定値
オプションを書かないassertの集計方法は optimistic で、「複数回のうち一番良い回」の値で判定されます。スコアの下限について、設定ドキュメントは既定を minScore: 1(満点)と書いています。しかし0.15.1の実装(@lhci/utils/src/assertions.js)は、maxLength も maxNumericValue も無いときに0.9を補います。実測のログでも、presetの監査は expected: >=0.9 と表示されました。計測回数を増やすほど判定が甘くなる点にも注意してください。
| aggregationMethod | 使う値 | 向く場面 |
|---|---|---|
optimistic(既定) |
最も合格しやすい回 | 揺れやすい監査の誤検知を減らす |
pessimistic |
最も合格しにくい回 | 1回でも悪ければ落としたい |
median |
監査値の中央値 | 数値指標の一般的な判定 |
median-run |
性能指標で代表とされた1回 | カテゴリスコアの判定 |
条件はスコアなら minScore(0〜1)、ミリ秒やバイトの実測値なら maxNumericValue、指摘件数なら maxLength を使います。カテゴリ全体は categories:performance のように categories: を前に付けます。
"assertions": {
"categories:performance": ["error", {"minScore": 0.9, "aggregationMethod": "median-run"}],
"largest-contentful-paint": ["error", {"maxNumericValue": 2500}],
"total-blocking-time": ["error", {"maxNumericValue": 200}]
}
CLIで一時的に上書きするなら lhci assert --preset=lighthouse:recommended --assertions.speed-index=off の形です。autorun 経由で渡すときは --assert.assertions.categories:performance=error のように --assert. を前に付けます。
3つのpresetの中身
presetは lighthouse:all・lighthouse:recommended・lighthouse:no-pwa の3つです。0.15.1のソース(@lhci/utils/src/presets/)を読むと、ドキュメントの説明だけでは分からない点があります。
all:ほぼ全監査をerrorにし、オプション無しなので実装上はスコア0.9以上を要求します(ドキュメントの説明は満点)。ただしtotal-blocking-time・interaction-to-next-paint・resource-summaryなどはoffです。recommended:allを引き継ぎ、FCP・LCP・CLS・Speed Indexなど揺れやすい指標をwarnに下げます。画像最適化や未使用JSの指摘はmaxLength: 0のerrorで、1件でも出れば落ちます。no-pwa:recommendedからis-on-httpsとviewportをoffにしただけです。ソースには「PWAはもう存在しないので削除すべきか」というTODOコメントが残っています。
Lighthouse 12でPWAカテゴリは削除されましたが、is-on-https と viewport は12.6.1でもBest Practicesの監査として残っています。no-pwa を選ぶとHTTPSとviewportの検査が黙って外れるので、今から始めるなら recommended を選び、外したい監査を個別にoffにします。
もう1つの落とし穴は、Performanceスコアで配点が最大(30%)のTBTがpresetではoffなことです。presetだけでは、メインスレッドを塞ぐスクリプトが入っても、未使用JavaScriptなど他のerror対象の監査に引っかからない限りビルドは通ります(次章の実測3)。
リソース予算:budgetsFileとresource-summary
転送量やリクエスト数の上限は、budgetsFile でLighthouse形式の budgets.json を読むか、assertに resource-summary:<種類>:size / :count を書くかの2通りです。budgetsFile は他のassert設定と併用できません。presetと一緒に使うなら resource-summary 形式に書き換えます。単位も違い、budgets.json はKB、resource-summary の maxNumericValue はバイトです。
"resource-summary:script:size": ["error", {"maxNumericValue": 300000}],
"resource-summary:third-party:count": ["warn", {"maxNumericValue": 10}]
実測:最小ページにrecommendedを当てた3回の結果
presetの挙動を確かめるため、2026年10月2日に次の環境で lhci autorun を実行しました。@lhci/cli 0.15.1(Lighthouse 12.6.1)、ヘッドレスのChrome 154、Node.js 26.5.0、既定のモバイル設定(CPU 4倍スロットリングのsimulate)で、各ページ3回ずつ計測しています。
| 回 | ページの内容 | assert | 終了コード | 主な結果 |
|---|---|---|---|---|
| 1 | h1と段落だけのHTML | recommended | 1 | errors-in-console 0点 |
| 2 | 1にfaviconを追加 | recommended+3項目 | 0 | Performance 100 |
| 2 | 本文直後に300msの同期ループ | 同上 | 0 | FCP 約1.89秒・TBT 0〜22ms・warn 2件 |
| 3 | 同じループをload後に実行 | 同上 | 1 | TBT 1,118.5ms・Performance 77 |
1回目は、Performance・Accessibility・SEOが満点なのに失敗しました。原因は /favicon.ico の404がコンソールエラーとして記録されたことです。errors-in-console はpresetでerrorなので、faviconを置いていない検証用ページや、外部タグが404を出すページは初回から落ちます。2回目は <link rel="icon" href="data:,"> を足しただけで通りました。
同じ300msの処理でも、置き場所でTBTは0にも1秒超にもなります。本文直後に置いたループは最初の描画(FCP)より前に終わるため、TBTの集計区間(FCP以降)に入りません。FCPが約0.7秒から約1.89秒へ遅れ、presetはFCPとSpeed Indexのwarnを出しただけでビルドは通りました。load後に動かすとTBTが1,118.5ms(3回の値は1,180.5/1,141.5/1,118.5)になり、明示的に足した total-blocking-time と categories:performance のassertで失敗しました。presetが出したのは max-potential-fid のwarnだけです。
ここから言えるのは、presetを入れただけの状態は「画像やCSSの指摘は厳しく、体感速度の悪化には甘い」という偏りを持つことです。性能を門番にするなら、TBTとカテゴリスコアのassertを自分で足してください。実行1と2・3の違いは表の「assert」列のとおりで、2・3で足した3項目は前章の assertions の例(categories:performance を median-run で0.9以上、LCP 2,500ms以下、TBT 200ms以下)と同じです。
GitHub Actionsでの実行とステータスチェック
公式ドキュメントの例は actions/checkout@v3 とNode.js 16のままです。2026年10月時点の現行版(checkout v7・setup-node v7、Node.js 24 LTS)に置き換えると次のようになります。@lhci/cli は package.json の開発依存で版を固定しておき、npx で呼びます。assertが失敗すればジョブが落ちるので、合否の判定はトークン無しでも働きます。PRにLHCIのステータスチェックを付けるには、次節のトークンに加えて upload.target をfilesystem以外にします。
name: Lighthouse CI
on:
pull_request:
push:
branches: [main]
jobs:
lhci:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
with:
ref: ${{ github.event.pull_request.head.sha }}
- uses: actions/setup-node@v7
with:
node-version: 24
cache: npm
- run: npm ci
- run: npm run build
- run: npx lhci autorun
env:
LHCI_GITHUB_APP_TOKEN: ${{ secrets.LHCI_GITHUB_APP_TOKEN }}
ref にPRのheadコミットを渡すのは、LHCIがgitのハッシュや祖先コミットから比較対象を推定するためです。pull_request イベントの既定ではマージ用の一時コミットがチェックアウトされます。setup-nodeの入力やキャッシュの既定はactions/setup-nodeのv7の入力とキャッシュ設定にまとめています。
ステータスチェックのトークン:GitHub AppかPATか
PRに合否のチェックを付けるトークンは2種類です。
- GitHub App(推奨):
github.com/apps/lighthouse-ciをリポジトリに入れ、表示されたトークンをLHCI_GITHUB_APP_TOKENに設定します。Appを運営するのはEris Ventures LLCで、同社の利用規約への同意が前提です。 - Personal Access Token:
repo:statusスコープのトークンをLHCI_GITHUB_TOKENに設定します。チェックは作成したユーザーの名義で付きます。
どちらもトークンを持つ人がステータスを書き込めるので、リポジトリのSecretsに置きます。置き場所の選び方はGitHub ActionsのSecrets管理を参照してください。なお upload.target がfilesystemのときはステータスチェックは付きません。
treosh/lighthouse-ci-actionの入力と計測回数
CLIを自分で呼ばずに、Marketplaceの treosh/lighthouse-ci-action(最新12.6.2・2026年3月12日、Lighthouse 12.6系)を使う方法もあります。入力は urls(改行区切り)、configPath、budgetPath、uploadArtifacts、temporaryPublicStorage、runs などです。
注意点は runs の既定が1回なことです。CLIの numberOfRuns の既定3回より少ないため、揺れを均したいなら明示的に3以上にします。uploadArtifacts: true にするとレポートはワークフローのアーティファクトとして残り、保持期間はリポジトリ設定に従います(GitHub Actionsのアーティファクト)。
GitLab CIでの実行
公式のGitLab CI例は次のとおりです。
image: cypress/browsers:node16.17.0-chrome106
lhci:
script:
- npm install
- npm run build
- npm install -g @lhci/[email protected]
- lhci autorun --upload.target=temporary-public-storage --collect.settings.chromeFlags="--no-sandbox" || echo "LHCI failed!"
そのまま写すと品質ゲートになりません。末尾の || echo "LHCI failed!" がLHCIの失敗を握りつぶし、ジョブは常に成功します。ビルドを止めたいならこの部分を消します。イメージもNode.js 16・Chrome 106と古いので、Node.js 18.20以上とChromeを含む現行イメージに替えてください。--no-sandbox はコンテナ内のChromeを起動するために要りますが、公式もセキュリティ上のトレードオフを確認するよう注記しています。
結果の保存先:公開ストレージ・ファイル・LHCIサーバー
upload.target は lhci(既定)・temporary-public-storage・filesystem の3つです。何を測るかで選びます。
| target | 保存先 | 公開範囲 | 向く場面 |
|---|---|---|---|
| temporary-public-storage | Eris Ventures運営の公開ストレージ | 誰でも閲覧可 | OSSや公開済みサイトの試用 |
| filesystem | outputDir にHTMLとJSON |
CIの成果物の範囲 | 社内ページ・履歴が要らない場合 |
| lhci | 自前のLHCIサーバー | サーバーの設定次第 | 履歴の推移と差分を見たい場合 |
temporary-public-storageの公開性と保持期間
サービス規約は、アップロードしたデータを「世界中の誰でも、いつでも、どんな理由でもアクセスできる公開情報」とし、保持期間を空き容量に応じて3日〜5週間としています。treoshのActionのREADMEは「7日後に自動削除」と書いていますが、運営者の規約の方が優先です。期間は一定ではないと考えてください。
レポートにはURL、スクリーンショット、読み込まれたリソースの一覧が入ります。ステージングや管理画面を測るなら、このtargetは使わないでください。autorun ではアップロード失敗は既定で警告に留まり、ビルドは落ちません(failOnUploadFailure で変更可)。
LHCIサーバーの構築:DBとDocker・Basic認証
LHCIサーバーは結果の履歴をグラフで見せ、ビルド間の監査の差分を比べる画面を持ちます。保存先はsqlite・mysql・postgresqlのいずれかです。手元で試すなら次の1行で起動し、既定の http://localhost:9001/ で開きます。
npm install -D @lhci/cli @lhci/server sqlite3
npx lhci server --storage.storageMethod=sql --storage.sqlDialect=sqlite --storage.sqlDatabasePath=./db.sql
Dockerなら公式レシピのイメージ patrickhulce/lhci-server を使います(lhci/server ではありません)。
docker container run --publish 9001:9001 --mount='source=lhci-data,target=/data' --detach patrickhulce/lhci-server
サーバーを立てたら手元で lhci wizard を実行してプロジェクトを作ります。発行されるのはビルドトークンと管理トークンの2つです。CIにはビルドトークンだけを upload.token か環境変数 LHCI_TOKEN で渡し、プロジェクトのデータを編集・削除できる管理トークンはCI環境に置きません。LHCI設定の upload は、target を lhci に、serverBaseUrl をサーバーのURLに書き換えます(serverBaseUrl は target=lhci のとき必須)。運用で押さえる点は3つあります。
- 既定では閲覧制限が無い:既存プロジェクトへのアップロードにはビルドトークン、編集・削除には管理トークンが要りますが、HTTPで到達できる人は誰でもレポートを閲覧でき、新しいプロジェクトも作れます。サーバー側に
basicAuth.usernameとbasicAuth.passwordを設定し、upload側にも同じ値を渡します。 - オリジンを丸ごと使う前提:サブパスで動かすなら、公式のプロキシ設定例(lhci-server-vpn-proxy)に従います。
- ドキュメントの要件表記が古い:要件は「Node v16 LTS」のままですが、同梱のLighthouse 12.6.1はNode.js 18.20以上を求めます。
Lighthouse CIを入れない方がよい場面と運用の注意
LHCIは「同じ条件で前回より悪くなったか」を見る道具で、ユーザーの体感を保証するものではありません。次の場面では導入より先に別の手段を選びます。
- 実ユーザーの遅さが問題になっている:Search Consoleの評価はCrUXのフィールドデータで決まります。先にコアウェブバイタルの3指標を実測で確認し、LHCIは再発防止に回します。
- ログインが必要なページが中心:
puppeteerScriptで認証を通せますが、セッション管理のコードを保守し続けることになります。 - 共有ランナーで絶対値を判定したい:ランナーの負荷で数値が揺れます。
maxNumericValueは余裕を持って置き、medianで判定します。
導入する場合も、既存の違反が残るサイトで最初から lighthouse:all を当てると、ほぼ全監査がerrorなので初回からビルドが落ち続けます。公式も、スコアがまだ低いサイトではassertを後回しにするよう勧めています。まずfilesystemで数週間の値を集め、実際の分布を見てから閾値を決める順番が現実的です。
よくある質問
Lighthouse CIとLighthouseの違いは何ですか?
Lighthouseは1ページを1回監査するツールで、Lighthouse CIはそれをCIで複数回実行し、assertで合否を判定してビルドを止める仕組みです。計測エンジンは同じですが、LHCI 0.15.1が使うLighthouseは12.6.1に固定されています。
@lhci/cliの最新バージョンはいくつですか?
2026年10月2日時点で0.15.1(2025年6月公開)です。同梱のLighthouseは12.6.1で、Lighthouse単体の最新13.5.0とは異なります。npm install -D --save-exact @lhci/[email protected] で版を固定して使います。
categories:performanceで閾値を指定するには?
assertに "categories:performance": ["error", {"minScore": 0.9}] を書きます。CLIなら lhci assert --assertions.categories:performance=error、autorun 経由なら --assert. を前に付けます。閾値や集計方法まで指定するなら設定ファイルに書く方が確実です。複数回計測では aggregationMethod に median-run を指定すると代表の1回で判定されます。
GitLab CIでLighthouse CIを動かすには?
Chromeを含むNode.jsイメージで npm install -g @lhci/[email protected] を入れ、lhci autorun --collect.settings.chromeFlags="--no-sandbox" を実行します。公式例の末尾にある || echo "LHCI failed!" は失敗を握りつぶすので、品質ゲートにするなら削除します。
Lighthouse CIサーバーは必須ですか?
必須ではありません。合否判定だけならfilesystemやtemporary-public-storageで足ります。サーバーが要るのは、スコアの推移をグラフで追う場合と、ビルド間の監査結果の差分を画面で比べたい場合です。