「Swagger入れといて〜」って言われて、何を入れればいいの?って固まったこと、ありませんか?
依存関係を追加したのにSwagger UIが出ない、URL開いても404で詰む、さらにSpring Security入ってたらログイン画面に飛ばされる…新人あるあるです。
でも大丈夫です。
この記事は、最短で“動く”ところまで持っていくのに全振りしています。
今日やることは、これだけです👇
- Swagger UIを表示できるようにする(まずは画面を出して勝つ)
- 最低限の説明を書けるようにする(「何するAPI?」が1行入る)
- 本番ではOFFにできるようにする(安心して運用できる)
「とにかく今すぐ動かしたい(Do)」も、「仕組みもざっくり知りたい(Know)」も、両方かないます。
まずは用語の混乱を1分で止めてから、依存関係追加 → 起動 → URLを開くでサクッと成功体験いきましょう。
まずこれだけ:springdocとSwaggerの関係
OpenAPIは「説明書の型」、Swagger UIは「見やすい画面」
まず用語で迷子になりがちなので、たとえ話で考えましょう。
OpenAPIは「説明書のテンプレ(型)」です。
APIが「何を受け取って、何を返すか」を同じ形で書けるルール、みたいなものです。
一方でSwagger UIは「その説明書を“見やすい画面”で表示するやつ」です。
説明書そのものじゃなくて、読むためのビューアだと思ってください。
Spring Bootでの役割分担(自動生成/表示)
Spring Boot側で大事なのは、「説明書を作る担当」です。そこで出てくるのが springdoc-openapi。
コントローラを見て、OpenAPIの定義(JSON)を自動で作って、ついでにSwagger UIで見せてくれます。
結論:Spring BootでSwagger UIを出したいなら、基本はspringdoc-openapiを入れればOKです。
この記事のゴール(最短で動かす→最低限整える)
この記事は、①最短導入でまず表示 → ②最低限の説明を整える → ③Securityで見れない問題 → ④本番ではOFF → ⑤トラブル即解決、の順で進みます。
学習・確認ツール(紹介するならこの辺)

3分で動く!最短導入(Maven/Gradle)
依存関係はこれ(WebMVC / WebFluxの選び方)
まずは「自分どっち?」をここで決めちゃいましょう。使ってるstarter名で判断するのが一番ラクです。
| いま入ってるもの | 選ぶspringdoc | 依存関係(UIあり) |
|---|---|---|
spring-boot-starter-web | WebMVC | springdoc-openapi-starter-webmvc-ui |
spring-boot-starter-webflux | WebFlux | springdoc-openapi-starter-webflux-ui |
Maven(どっちか1つだけ入れてください)
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<version>2.8.14</version>
</dependency>
<!-- WebFluxならこっち
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webflux-ui</artifactId>
<version>2.8.14</version>
</dependency>
-->
Gradle(どっちか1つだけ)
implementation "org.springdoc:springdoc-openapi-starter-webmvc-ui:2.8.14"
// WebFluxなら:implementation "org.springdoc:springdoc-openapi-starter-webflux-ui:2.8.14"
起動して確認する「2つのURL」
起動したら、見るのはこの2つだけです(ここで勝ちます)。
コピペ用:最小Controllerを1本作る
「何も出ない…」を防ぐために、GETを1本だけ作ります。
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HelloController {
@GetMapping("/hello")
public String hello() {
return "hello";
}
}
これでSwagger UIに /hello が見えたら、導入は完了です。
バージョン選びで迷わないコツ(互換表の見方)
バージョンで沼りがちなので、ルールを固定します。
開発支援ツール(紹介するならこの辺のカテゴリ/価格感)
Swagger UIで試しつつ、外部ツールでも叩ける状態にしておくと提出が強いです。


404で詰まらない:Swagger UIのURL早見表
まず試すURL(よくある順)
迷ったら、この順でOKです(上から順番に叩くのがコツです)。
| 目的 | まず試すURL | 期待するもの |
|---|---|---|
| API定義 | /v3/api-docs | JSONが出る(出たら生成は成功) |
| UI | /swagger-ui.html | UIにリダイレクトされること多め |
| UI本体 | /swagger-ui/index.html | 画面が出る |
context-pathを変えた時の落とし穴
server.servlet.context-path=/api みたいに付けてたら、URLも 全部先頭に/api が付きます。
例:/api/v3/api-docs、/api/swagger-ui/index.html です。ここ、めっちゃ忘れがちです。
静的index.htmlとぶつかる問題
React/VueのSPAを置いてて、/ が常に index.html を返す設定だと、Swaggerをルート配下に寄せた時にUIが飲み込まれます。
対策はシンプルで、Swaggerは/swagger-ui/配下に置く(=変にパスをいじらない)か、SPA側のルーティングから除外します。
「APIは出るのにUIだけ無い」切り分け
最低限のカスタムだけ先にやる(やりすぎない)
タイトル/説明/バージョンだけ整える
まずはここだけでOKです。これが入ってるだけで「誰向けのAPIで、今どの版?」が一瞬で伝わって、チーム提出で恥ずかしくないです。
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.info.Info;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class OpenApiConfig {
@Bean
public OpenAPI openAPI() {
return new OpenAPI()
.info(new Info()
.title("サンプルAPI")
.description("社内向けの検証用APIです。まずはGETから。")
.version("v1"));
}
}
サーバーURL(開発・検証)を表示したい時
server URLは「必要な時だけ」でOKです(環境で変わるので、無理に固定すると逆に混乱します)。別環境で試す人が多い時だけ入れましょう。
import io.swagger.v3.oas.models.servers.Server;
// openAPI()の中で…
.addServersItem(new Server().url("http://localhost:8080"))
タグで見やすくする(画面で迷子にしない)
エンドポイントが増えると迷子になります。Controllerごとにタグを付けるのが手っ取り早いです。
import io.swagger.v3.oas.annotations.tags.Tag;
@Tag(name = "ユーザー", description = "ユーザー関連のAPI")
@RestController
class UserController { /* ... */ }

書き方テンプレ:説明文・パラメータ・レスポンス例
まずはこれだけ:@Operationで1行説明
Swaggerは「説明があるだけ」で一気に読みやすくなります。まずは1行だけ入れましょう。
@Operation(summary = "ユーザーを1人取得します(社内ツール用)")
@GetMapping("/users/{id}")
public UserResponse getUser(@PathVariable Long id) { ... }
1行テンプレ(迷ったらこれ)
「何をする?/誰向け?/注意は?」の順です。
パラメータの説明(必須/任意・例・注意)
次はパラメータです。意味・必須・例・制約が入ると親切です。
@GetMapping("/users/{id}")
public UserResponse getUser(
@Parameter(description = "ユーザーID(1以上)", example = "123", required = true)
@PathVariable Long id,
@Parameter(description = "詳細を含めるか", example = "true", required = false)
@RequestParam(required = false) Boolean detail
) { ... }
レスポンスの例(成功・失敗の2つは書く)
最低でも「成功」と「失敗」を1個ずつ置くと、使う人が迷いません。
@Operation(summary = "ユーザーを1人取得")
@ApiResponses({
@ApiResponse(responseCode = "200", description = "成功"),
@ApiResponse(responseCode = "404", description = "見つからない")
})
@GetMapping("/users/{id}")
public UserResponse getUser(...) { ... }
例(見た目のイメージ):
エラー設計(400/401/404/500)を同じ形で書く
ここが超大事です。エラーは毎回バラバラにしないで、同じ形にします。
{
"code": "BAD_REQUEST",
"message": "入力が不正です",
"details": ["idは1以上にしてください"]
}
最低セットはこれでOKです:
実装は @RestControllerAdvice でまとめると、説明も運用もラクになります(「全部この形で返す」が守れます)。
「内部用は隠す」基本(非表示のやり方)
管理用やデバッグ用は、最初から見せないのが安全です。
@Hidden
@GetMapping("/internal/health-detail")
public String internalOnly() { ... }
// もしくは
@Operation(summary = "内部用", hidden = true)
API仕様のレビュー/共有ツール(紹介するならこのカテゴリ)
Spring SecurityでSwaggerが見れない問題を解決
Swagger UIだけ通す(permitの最小ルール)
Securityを入れると、Swagger UIがログイン画面に飛ぶ/403になるのが定番です。
まずは「Swagger関連のパスだけ例外」にします(最小でOKです)。
@Bean
SecurityFilterChain security(HttpSecurity http) throws Exception {
http.authorizeHttpRequests(auth -> auth
.requestMatchers("/swagger-ui/**", "/swagger-ui.html", "/v3/api-docs/**").permitAll()
.anyRequest().authenticated()
);
return http.build();
}
まずこれで UIが開ける状態 を作ってから、全体の認可ルールを育てるのが安全です。
Bearer(JWT)入力を出す(Authorizeボタン)
「Swagger画面からJWT付きで試したい」なら、OpenAPIにBearer設定を足します。これでUIにAuthorizeボタンが出ます。
@Bean
OpenAPI openAPI() {
return new OpenAPI()
.components(new Components().addSecuritySchemes("bearerAuth",
new SecurityScheme().type(SecurityScheme.Type.HTTP).scheme("bearer").bearerFormat("JWT")))
.addSecurityItem(new SecurityRequirement().addList("bearerAuth"));
}
※全部に必須を付けたくない場合は、必要なAPIだけ @SecurityRequirement(name="bearerAuth") を付ける運用がラクです。
CSRF・ログイン画面に飛ぶ時の考え方

本番でSwagger UIを出していい?安全な出し分け
結論:原則「開発だけON」が安心
結論から言うと、Swagger UIは原則「開発環境だけON」が一番安全です。
画面が見える=APIの入口や仕様が分かるので、本番でうっかり公開すると、攻撃のヒントを渡しやすいです。
例外は「社内VPN内だけ」「IP制限済み」「認証もガチガチ」みたいに、外から触れない前提が作れる時ですね。
UIだけOFF/API定義もOFF(どっちを止める?)
「社内ツールが定義JSONを読む」みたいな事情がなければ、基本は 両方OFF寄りでOKです。
profileで切り替える(dev/prodの例)
application-dev.yml(開発だけON)
springdoc:
swagger-ui:
enabled: true
api-docs:
enabled: true
application-prod.yml
springdoc:
swagger-ui:
enabled: false
api-docs:
enabled: false
これで devは見える、prodは見えない がキレイに作れます。
代わりに「定義ファイルを配る」選択肢
本番で画面を出さなくても、OpenAPIのJSON/YAMLを成果物として共有すれば困りません。
たとえばCIで /v3/api-docs を叩いてファイル化→社内Wikiやリポジトリに置く、みたいな運用が堅いです。
安全策のカテゴリ(紹介するなら):社内VPN/リバースプロキシのアクセス制御/WAF・IP制限(サービスにより価格いろいろ)。
よくあるエラー集(症状→原因→直し方)
ここは“救急箱”です。
今出てる症状に近いところだけ見て、そのまま直しに行ってください。
404になる(URL・依存関係・パス競合)
真っ白/定義を読めない(api-docs側の確認)
No operations defined(Controller検出・パッケージ)
起動失敗(依存の不一致・バージョン)
CORSで叩けない(別ポート・別ドメイン)
最後のチェックリスト&次の一歩(CTA)
まず合格ライン(新人の提出OK)
提出前に、ここだけチェックすればOKです👇
この4つできてたら、新人の「まず合格ライン」は超えてます。
チームで育てる改善ロードマップ
次にやるならこの順がラクです。
- タグで整理して迷子を消す
- 成功/失敗のレスポンス例を増やす
- 認証(JWTなど)をSwagger画面から試せるようにする
- エラー形式をControllerAdviceで完全統一する
- APIをグループ分けして、画面をさらに見やすくする
参考リンク(公式/互換表/サンプル)
最後に、困ったらここを見れば戻れる、って場所だけ貼っておきます。
よくある質問
- QSwaggerとOpenAPIって、結局なにが違うんですか?
- A
OpenAPIは「APIの説明書の型」で、Swagger UIは「その説明書を見やすく表示する画面」です。
Spring Bootだと、説明書を自動で作ってくれる役が springdoc-openapi です。
- QSpring BootでSwagger UIを出すには、何を入れればいいですか?
- A
基本は springdoc-openapi の “UI付きスターター” を入れればOKです。
「UIなし」を入れると、/v3/api-docsは出ても画面が出ないことがあります。
- QWebMVCとWebFlux、どっちの依存を選べばいいですか?
- A
目安はこれです。
混ぜるとハマるので、どっちか1つに寄せてください。
- QSwagger UIのURLはどれを開けばいいですか?(404になります)
- A
まずこの順で試してください。
/v3/api-docs(JSONが出るか)/swagger-ui.html/swagger-ui/index.html
最優先は
/v3/api-docsが出るかです。ここが出ればだいぶ勝ちです。
- Q
/v3/api-docsが404です。何が原因ですか? - A
よくあるのはこの3つです。
- QUIは開くのに真っ白です。どう直せばいいですか?
- A
まず
/v3/api-docsをブラウザで直に開いてください。
ここが 500だったり、JSONが返ってないなら、UIの問題じゃなくて「定義を作る側」がコケてます。
- Q「No operations defined」って出ます。なにそれですか?
- A
「APIが0件」扱いになってます。原因はだいたいこれです。
最小のGETを1本作って、出るか確認すると早いです。
- QSpring Security入れたらSwaggerが見れません(ログイン画面/403)
- A
あるあるです。Swagger関連のパスだけ通す(permit)のが最短です。
通す候補はこのへん:まずUIを開ける状態を作ってから、認可を固めるのが安全です。
- QSwagger UIに「Authorize(JWT入力)」を出したいです
- A
OpenAPI側に Bearer認証の設定を足すと出せます。
すると画面からトークンを入れて、そのままAPIを試せるようになります。
「全部に必須」じゃなくて、必要なAPIだけ必須にする運用もできます。
- Q本番でSwagger UIを出してもいいですか?
- A
原則は 本番はOFF が安心です。
どうしても出すなら、社内VPN内・IP制限・認証強めみたいに「外から触れない前提」を作るのが基本です。
運用としては profileで devはON / prodはOFF が一番ラクです。
