Spring Boot Actuatorを入れたのに /actuator/metrics が404になる、management.endpoint.health.enabled を書いたらIDEに非推奨と出る、Spring Boot 4に上げたら HealthIndicator のimportが通らない。原因は順に、公開設定(exposure)、3.4で入ったアクセス制御(access)、4.0のモジュール分割です。この記事では、Spring Boot 4.1.1でビルド・起動して確かめた設定とコードで、公開・無効化、healthの詳細とプローブ、Spring Securityでの保護、3系から上げると壊れる箇所を説明します。
まとめ:Spring Boot Actuator設定の要点
- HTTPで公開されるのは初期状態で
healthだけです。他はmanagement.endpoints.web.exposure.includeに列挙します。 - エンドポイントを止める・読み取り専用にするのは
management.endpoint.<id>.access(none/read-only/unrestricted)です。management.endpoint.<id>.enabledは3.4.0で非推奨になりました。両方を書くと起動に失敗します。 - healthの内訳は
show-details=when-authorizedとrolesで見せる相手を絞ります。4.0からliveness・readinessグループが既定で有効です。 - env・configpropsの値は3.0から既定で
******に伏せられます。見せるならshow-values=when-authorizedにします。 - Spring Boot 4では
HealthIndicatorがorg.springframework.boot.health.contributor、EndpointRequestがorg.springframework.boot.security.autoconfigure.actuate.web.servletに移りました。 - Spring SecurityのCSRF保護が有効だと、loggersへのPOSTは403になります。ブラウザを使わない管理用クライアントだけなら、Actuatorのパスを
ignoringRequestMatchersで除外します。
Spring Boot Actuatorのエンドポイント一覧と初期状態
Actuatorは、稼働状態・メトリクス・設定値・ログレベルなどを /actuator 配下のエンドポイントとして返すモジュールです。ロードバランサやKubernetesの死活監視、Prometheusへのメトリクス提供、障害時の設定確認に使います。エンドポイントには「アクセスできるか(access)」と「HTTPに公開するか(exposure)」の2つのスイッチがあり、両方が開いて初めて応答します。Spring Boot 4.1.1での初期状態は次のとおりです。
| ID | 返すもの | 初期access | HTTP公開 |
|---|---|---|---|
| health | 稼働状態と依存先の状態 | unrestricted | 公開 |
| info | アプリ情報 | unrestricted | 非公開 |
| metrics | Micrometerのメトリクス | unrestricted | 非公開 |
| prometheus | Prometheus形式のメトリクス | unrestricted | 非公開 |
| env/configprops | プロパティ値(既定で伏字) | unrestricted | 非公開 |
| beans/mappings | Bean一覧/URL対応 | unrestricted | 非公開 |
| loggers | ログレベルの参照・変更 | unrestricted | 非公開 |
| httpexchanges | 直近のHTTP送受信 | 要Repository Bean | 非公開 |
| heapdump/threaddump | メモリ/スレッドの内容 | none/unrestricted | 非公開 |
| sbom | ソフトウェア部品表 | unrestricted | 非公開 |
| shutdown | アプリの停止 | none | 非公開 |
全エンドポイントの一覧と各IDの依存条件は、公式リファレンスのEndpointsにあります。shutdownとheapdumpは初期accessが none なので、公開に加えても404のままです(4.1.1で確認)。
Actuatorの導入依存関係(Maven・Gradle)
依存は spring-boot-starter-actuator の1つです。版はSpring BootのBOMが決めるので書きません。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
implementation 'org.springframework.boot:spring-boot-starter-actuator'
Spring Boot 4では、Web MVCのスターター名が spring-boot-starter-web から spring-boot-starter-webmvc に変わりました。旧名も残っていますが非推奨です(Spring Boot 4.0 Migration Guide)。4.1.1のActuatorスターターは、内部で spring-boot-health と spring-boot-starter-micrometer-metrics を引き込みます。4.1系の依存名の変わり方はSpring Bootとは|4.1系の依存名の変更と最小Web APIの作り方で整理しています。
エンドポイントを公開・無効化する設定
management.endpoints.web.exposure.includeによるHTTP公開設定
「Actuatorを入れたのに /actuator/metrics が404」の場合は、まずHTTP公開設定を確認します。HTTP公開の初期値は health だけなので、使うIDを列挙します。
# HTTPに公開するIDを列挙する(初期値は health のみ)
management.endpoints.web.exposure.include=health,info,metrics,prometheus
# 全公開して一部だけ隠す書き方。exclude が include より優先される
# management.endpoints.web.exposure.include=*
# management.endpoints.web.exposure.exclude=env,beans,heapdump
application.ymlで * を書くときは、YAMLの特殊文字なので "*" と引用符で囲みます。本番で * を使うと、依存を足しただけで新しいエンドポイントが外に出るので、IDを列挙する書き方を勧めます。JMXの公開も既定は health だけで、こちらは management.endpoints.jmx.exposure.include で変えます。
management.endpoint.health.enabledの3.4での非推奨化とaccessへの移行
エンドポイントそのものを止めるプロパティは、Spring Boot 3.4.0でon/offの enabled から3段階の access に置き換わりました(Spring Boot 3.4 Release Notes)。
| 3.3までの書き方 | 3.4以降の書き方 |
|---|---|
| management.endpoint.<id>.enabled | management.endpoint.<id>.access |
| management.endpoints.enabled-by-default | management.endpoints.access.default |
| @Endpoint(enableByDefault = …) | @Endpoint(defaultAccess = …) |
| (なし) | management.endpoints.access.max-permitted |
access の値は none(無効)、read-only(読み取り操作のみ許可)、unrestricted(書き込み・削除も可)の3つです。read-only が新しく入ったことで、「loggersのレベルは見せるが変えさせない」という設定がプロパティ1行で書けます。
# health を止める(旧 management.endpoint.health.enabled=false)
management.endpoint.health.access=none
# loggers は参照だけ許す。POST は 405 Method Not Allowed になる
management.endpoint.loggers.access=read-only
# 既定を閉じて、使うものだけ開ける書き方
management.endpoints.access.default=none
management.endpoint.health.access=read-only
management.endpoint.info.access=read-only
# 個別設定より優先される上限。read-only にすると全エンドポイントの書き込みが止まる
management.endpoints.access.max-permitted=read-only
移行で気をつけたいのは2点です。まず、4.1.1でも旧キーの management.endpoint.info.enabled=false は効き(infoが404になる)、警告ログも出ません。動くので気づきにくいのですが、IDEの補完からは消えています。次に、同じIDに enabled と access を両方書くと、「The following configuration properties are mutually exclusive」で起動が止まります。プロファイル別のファイルで片方だけ書き換えたときに起きやすいので、移行は一括で置換します。
base-pathの変更と管理ポートの分離
公開パスの接頭辞とポートは別々に変えられます。ポートを分けると、アプリ用のロードバランサにはActuatorのポートを登録せず、監視基盤だけが到達できる構成にできます。
# /actuator を /manage に変える
management.endpoints.web.base-path=/manage
# Actuatorだけ 8081 で待ち受ける
management.server.port=8081
# 主ポート(server.port)でも /livez と /readyz を返す
management.endpoint.health.probes.add-additional-paths=true
管理ポートだけを見るプローブは、アプリ側のポートが詰まっていても成功を返すことがあります。Kubernetesのプローブは、add-additional-paths で主ポートに出る /livez・/readyz に向ける方が、実際の受け口の状態を反映します。ただし独自の SecurityFilterChain を書いていると、この2つのパスはアプリ用のチェーンに当たり、4.1.1では401になりました。後述の設定例のように permitAll() を足すと200に変わります。
healthエンドポイントの詳細表示とプローブ
show-detailsとrolesによるhealth詳細の表示制御
Spring Boot 4.1.1ではプローブが既定で有効なため、正常時のhealth応答には status に加えて liveness・readiness のグループ名も含まれます。DB・ディスク容量・独自チェックの内訳は show-details で出します。値は never(既定)、when-authorized、always の3つです。roles はロール名を書くプロパティで、when-authorized のときに「どのロールなら内訳を見られるか」を決めます。roles を書かなければ、認証済みのユーザー全員が対象です。
management.endpoint.health.show-details=when-authorized
management.endpoint.health.roles=ADMIN
この設定で後述の SecurityFilterChain と組み合わせて確かめると、匿名のcurlには {"groups":["liveness","readiness"],"status":"UP"} だけが返り、ADMINロールのユーザーには diskSpace・ping・ssl・独自の externalApi などの内訳が返りました。show-components を別に指定すると、内訳の値は伏せたまま構成要素名だけ見せることもできます。
liveness・readinessとHTTPステータス
Spring Boot 4.0から、liveness・readinessのプローブが既定で有効になりました。3.5までは management.endpoint.health.probes.enabled の既定が false で、KubernetesやCloud Foundry上で動かしたときだけ自動で有効になっていました。4.0以降は環境を問わず /actuator/health/liveness と /actuator/health/readiness が応答します。不要なら同じプロパティを false にします。
# Kubernetes の Pod 定義(抜粋)
livenessProbe:
httpGet:
path: /actuator/health/liveness
port: 8080
readinessProbe:
httpGet:
path: /actuator/health/readiness
port: 8080
HTTPステータスは全体の状態から決まり、DOWN と OUT_OF_SERVICE が503、それ以外は200です。HTTPヘルスチェックで503を異常と判定するロードバランサでは、設定した失敗閾値に達すると対象が切り離されます。独自のマッピングを1つでも設定すると既定の2つが消えるため、503を残すなら一緒に書き直す必要があります(公式リファレンス)。livenessに外部DBの状態を混ぜると、DB障害で全Podが再起動を繰り返します。livenessとreadinessに何を入れるかはLivenessとReadinessの分離と閾値設計、停止時の順序はグレースフルシャットダウンの停止順序と猶予時間で扱っています。
独自HealthIndicatorの実装とSpring Boot 4のimport
外部APIへの疎通など、自動構成が知らない依存はHealthIndicatorで足します。Spring Boot 4では Health と HealthIndicator が新しいモジュール spring-boot-health の org.springframework.boot.health.contributor パッケージに移りました。3.xの org.springframework.boot.actuate.health のままではコンパイルできません。
import org.springframework.boot.health.contributor.Health;
import org.springframework.boot.health.contributor.HealthIndicator;
import org.springframework.stereotype.Component;
@Component
public class ExternalApiHealthIndicator implements HealthIndicator {
@Override
public Health health() {
if (!isReachable()) {
return Health.down().withDetail("api", "unreachable").build();
}
return Health.up().withDetail("api", "reachable").build();
}
private boolean isReachable() {
// 実際の疎通確認を書く。時間がかかる処理はタイムアウトを短く
return true;
}
}
クラス名から HealthIndicator を除いた externalApi が、healthの内訳の名前になります。
info・metrics・loggersの使い方
info:info.*が空で返るときの設定
info.app.name を書いたのに {} が返るのは、info.* を読む仕組み(envコントリビュータ)が2.6から既定で無効だからです。4.1.1でも既定は false のままです。
management.info.env.enabled=true
info.app.name=order-service
この2行で、/actuator/info は {"app":{"name":"order-service"}} を返しました。版番号やコミットハッシュは手で書くより、spring-boot-maven-plugin の build-info ゴールやGit情報のプラグインで生成すると、リリースのたびに更新されます。
metrics・prometheus:Micrometerによるメトリクス出力
/actuator/metrics は名前の一覧を、/actuator/metrics/jvm.memory.used のように名前を付けると個別の値を返します。業務の件数は MeterRegistry からカウンターを作って数えます。
import io.micrometer.core.instrument.Counter;
import io.micrometer.core.instrument.MeterRegistry;
import org.springframework.stereotype.Component;
@Component
public class OrderMetrics {
private final Counter orderCounter;
public OrderMetrics(MeterRegistry registry) {
this.orderCounter = Counter.builder("orders.created")
.description("作成された注文の件数")
.register(registry);
}
public void recordOrder() {
orderCounter.increment();
}
}
Prometheusに取り込むなら io.micrometer:micrometer-registry-prometheus を依存に足し、exposure.include に prometheus を加えます。/actuator/prometheus がテキスト形式で jvm_info などを返すので、そこをスクレイプ先にします。Prometheus側の設計と保持期間の見積もりはPrometheusで監視を構築する手順にまとめています。
loggers:実行中のログレベル変更
再起動せずにログレベルを変えるには、loggersへPOSTします。以下は、loggersをHTTP公開し、accessをunrestrictedに設定したうえで、ADMINロールを持つ検証用ユーザーadmin(パスワードpw)を別途登録した場合の例です。レベルを null にすると、そのロガーに明示したレベルが解除され、親ロガーのレベルを継承します。設定ファイルに書いた元の値を復元する操作ではありません。
# com.example を DEBUG にする(204 No Content が返る)
curl -u admin:pw -X POST http://localhost:8080/actuator/loggers/com.example \
-H "Content-Type: application/json" \
-d '{"configuredLevel": "DEBUG"}'
# 明示したレベルを解除し、親ロガーのレベルを継承する
curl -u admin:pw -X POST http://localhost:8080/actuator/loggers/com.example \
-H "Content-Type: application/json" \
-d '{"configuredLevel": null}'
Spring SecurityのCSRF保護を有効のままにしていると、このPOSTはADMINで認証していても403になります。Actuatorのパスを除外すると204が返りました(次の章の設定)。access=read-only にしてあり、認証・認可とCSRFのチェックを通過した場合は405が返ります。403だけでは、認可拒否とCSRFエラーを区別できません。
env・configprops・httpexchangesによる設定値とHTTP履歴の確認
env・configpropsの値が******になる理由
envとconfigpropsは、どの設定ファイルの値が採用されたかを確かめるのに使います。ただ、3.0からは値が既定で ****** に置き換えられます(Spring Boot 3.0 Migration Guide)。2系のキー名で伏せる方式はなくなり、ロールで見せる方式になりました。
# ADMIN ロールにだけ実際の値を見せる
management.endpoint.env.show-values=when-authorized
management.endpoint.env.roles=ADMIN
4.1.1で /actuator/env/spring.application.name を引くと、show-values=never ではADMINでも "value":"******"、when-authorized では実際の値が返りました。ロールがUSERのユーザーは、後述のセキュリティ設定によりそもそも403です。always は誰にでも平文を返すので、パスワードやAPIキーを環境変数で渡している本番では使いません。beansは登録済みBeanの一覧、mappingsはURLとハンドラの対応を返し、自動構成の上書きや404の原因を探すときに使います。
httpexchanges:旧httptraceからの改称と有効化条件
2系の httptrace は3.0で httpexchanges に改称されました。記録先の HttpExchangeRepository を自分でBean定義しないと、エンドポイント自体が作られません。
import org.springframework.boot.actuate.web.exchanges.InMemoryHttpExchangeRepository;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class HttpExchangesConfig {
@Bean
public InMemoryHttpExchangeRepository httpExchangeRepository() {
return new InMemoryHttpExchangeRepository();
}
}
InMemoryHttpExchangeRepository は直近100件をメモリに持つだけで、再起動で消えます。本番の解析はアクセスログやトレースに任せ、これは開発中の確認用と割り切ります。
Spring SecurityによるActuatorエンドポイントの保護
heapdumpはメモリの中身ごと持ち出せ、envは show-values の設定次第で接続情報を返します。外部に開いた管理用パスが攻撃の入口になった事例と、外から点検する手順はミスターマックスの不正アクセスと最大173万人の流出|サーバーの公開機能を点検する手順で扱っています。Spring Securityを入れると、独自の設定がなくてもhealth以外は認証が必要になりますが、ロールで分けるなら SecurityFilterChain を書きます。独自の SecurityFilterChain を1つでも定義すると、Spring Bootの既定のチェーンは使われなくなります。Actuator用のチェーンだけを書くと、Actuator以外のURLがどのチェーンにも当たらず無防備になるため、アプリ用のチェーンも並べて @Order で順序を決めます。次のCSRF除外は、ブラウザを使わない監視ツールやcurlから呼ぶ前提の設定です。
import org.springframework.boot.security.autoconfigure.actuate.web.servlet.EndpointRequest;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.SecurityFilterChain;
@Configuration
public class ActuatorSecurityConfig {
// Actuatorのパスだけを受け持つチェーン(先に評価する)
@Bean
@Order(1)
public SecurityFilterChain actuatorSecurity(HttpSecurity http) throws Exception {
http
.securityMatcher(EndpointRequest.toAnyEndpoint())
.authorizeHttpRequests(auth -> auth
.requestMatchers(EndpointRequest.to("health")).permitAll()
.anyRequest().hasRole("ADMIN"))
.httpBasic(Customizer.withDefaults())
// loggers などへの POST を CSRF トークンなしで受ける
.csrf(csrf -> csrf.ignoringRequestMatchers(EndpointRequest.toAnyEndpoint()));
return http.build();
}
// それ以外のURLを受け持つチェーン
@Bean
@Order(2)
public SecurityFilterChain applicationSecurity(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(auth -> auth
// add-additional-paths で主ポートに出したプローブ用パス
.requestMatchers("/livez", "/readyz").permitAll()
.anyRequest().authenticated())
.httpBasic(Customizer.withDefaults());
return http.build();
}
}
4.1.1で動かした結果は、匿名のhealthが200、匿名のinfoが401、USERロールのenvが403、ADMINのenvが200、Actuator以外の / は匿名で401でした(ユーザーはテスト用に InMemoryUserDetailsManager で登録)。EndpointRequest はbase-pathを変えても追随するので、パスを文字列で書くより安全です。このクラスは4.0で spring-boot-security モジュールへ移り、3.xの org.springframework.boot.actuate.autoconfigure.security.servlet.EndpointRequest のままではコンパイルできません。WebSecurityConfigurerAdapter を継承する書き方はSpring Security 6(Boot 3.0)で使えなくなっています。SecurityFilterChain の仕組みはSecurityFilterChainの解説、認証全体の流れはSpring Securityの基本ガイドを参照してください。
インターネットから到達できるポートでは、healthだけを無認証にし、heapdump・env・threaddumpは公開リストにも入れません。認証をかけても、パスワードが漏れれば中身ごと持ち出されるからです。これらが必要なら管理ポートを分け、社内ネットワークや踏み台からだけ届くようにします。監視の設計や運用の受け皿まで含めて相談したい場合は、システムの保守運用・内製化支援で対応しています。
独自Actuatorエンドポイントの実装と公開設定
機能フラグの状態など、標準にない情報は @Endpoint を付けたBeanで足せます。参照は @ReadOperation、更新は @WriteOperation です。
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
import org.springframework.boot.actuate.endpoint.annotation.ReadOperation;
import org.springframework.stereotype.Component;
import java.util.Map;
@Component
@Endpoint(id = "features")
public class FeatureEndpoint {
@ReadOperation
public Map<String, Boolean> features() {
return Map.of("newCheckout", true, "betaFilter", false);
}
}
exposure.include に features を加えると、/actuator/features が {"newCheckout":true,"betaFilter":false} を返します。こちらは4.1.1でもパッケージは org.springframework.boot.actuate.endpoint.annotation のままです。初期状態で閉じておきたいエンドポイントは、3.4以降 @Endpoint(id = "features", defaultAccess = Access.NONE) と書きます。
Spring Boot 2・3から上げると動かなくなるActuator設定
検索で見つかるActuatorの解説には2系や3.3以前の書き方が多く、そのまま写すと今の版では動きません。版ごとに壊れる箇所を並べます。
| 古い書き方 | 今の書き方 | 変わった版 |
|---|---|---|
| management.security.enabled | SecurityFilterChain | 2.0 |
| info.* がそのまま出る | management.info.env.enabled=true | 2.6 |
| httptrace | httpexchanges+Repository Bean | 3.0 |
| WebSecurityConfigurerAdapter | SecurityFilterChain のBean | 3.0 |
| env の値がそのまま出る | show-values(既定 never) | 3.0 |
| management.endpoint.<id>.enabled | management.endpoint.<id>.access | 3.4 |
| org.springframework.boot.actuate.health | org.springframework.boot.health.contributor | 4.0 |
| …actuate.autoconfigure.security.servlet | …security.autoconfigure.actuate.web.servlet | 4.0 |
| probes はKubernetes上だけ有効 | probes が既定で有効 | 4.0 |
4.0はimportの書き換えが多いので、まず spring-boot-starter-classic を足してクラスパスを3系に近い状態に戻し、壊れたimportを直してから外す、という2段階の移行が公式に用意されています(Spring Boot 4.0 Migration Guide)。Maven Centralでの公開日は、4.0.0が2025年11月20日、4.1.0が2026年6月10日、本記事で使った4.1.1が2026年8月20日です。spring.ioのサポート表では、3.5系のOSSサポートは2026年6月30日に終了し、4.1系は2027年7月31日までです。これから組むなら4.1系を選びます。4系全体の変更点はSpring Boot 4の変更点まとめ、版ごとのサポート期限はSpring Bootのバージョン一覧とサポート期限にまとめています。
よくある質問
Actuatorのエンドポイントにアクセスできないのはなぜですか?
よくある原因は、HTTPで初期公開されるのが health だけであることです。management.endpoints.web.exposure.include に対象IDを書きます。公開しても404なら access が none になっていないか、Spring Securityを入れているなら401・403でないかを確かめます。
management.endpoint.health.enabledは今も使えますか?
3.4.0で非推奨になり、management.endpoint.health.access に置き換わりました。4.1.1でも旧キーは効きますが、同じIDに enabled と access を両方書くと起動に失敗します。無効化は access=none で書きます。
spring boot actuatorのrolesは何を設定するものですか?
management.endpoint.health.roles は、show-details=when-authorized のときに内訳を見せるロールを指定します。envやconfigpropsにも同名の roles があり、show-values=when-authorized と組み合わせて使います。エンドポイントへのアクセス可否そのものは SecurityFilterChain で決めます。
httptraceエンドポイントが見つからないのはなぜですか?
3.0で httpexchanges に改称されたためです。InMemoryHttpExchangeRepository などの HttpExchangeRepository をBean定義し、公開リストに httpexchanges を加えると使えます。
management.security.enabledを書いても効かないのはなぜですか?
このプロパティは2.0で廃止され、今は無視されます。認証とアクセス制御は SecurityFilterChain と EndpointRequest で書きます。