12 人が閲覧(直近 30 日) Spring

Thymeleaf(タイムリーフ)とは?読み方とSpring Bootでの使い方・th:属性を実例で解説

Thymeleaf(タイムリーフ)とは?読み方とSpring Bootでの使い方・th:属性を実例で解説

Thymeleafの読み方は「タイムリーフ」です。Javaでサーバー側からHTMLを組み立てて返すテンプレートエンジンで、Spring Bootには公式のスターターがあります。この記事では、Spring Boot 4.1での導入、th:fieldなどの主要属性、共通レイアウト、Spring Security連携、エラーページまでを、Spring Boot 4.1.1+Thymeleaf 3.1.5で動かした出力付きで解説し、3.1.3以下に残る脆弱性と3.1の式の制限も扱います。

まとめ:Thymeleafの要点

  • 読み方は「タイムリーフ」。公式FAQの発音表記は /ˈtaɪmˌlɪːf/ で、thyme(ハーブのタイム)+leaf。
  • 最新は3.1.5.RELEASE(2026年4月21日リリース・Java SE 8以上)。Spring Boot 4.1系・4.0.6以降・3.5.14以降がこの版を管理している。
  • 3.1.3以下にはcriticalの脆弱性(CVE-2026-40477ほか)があり、3.1.4以下にもCVE-2026-41901が残る。Spring Boot 3.5.13以前・4.0.5以前は3.1.3を引き込むので、Bootごと上げる。
  • Spring Bootではspring-boot-starter-thymeleafを足し、src/main/resources/templates/にHTMLを置けば動く。設定は不要。
  • ここでは基本となる4種類を紹介します。${...}=変数、*{...}=th:objectで選んだ対象のプロパティ、@{...}=URL、#{...}=メッセージ。
  • Spring Boot 4(Spring Security 7)でも、認証情報の表示にはthymeleaf-extras-springsecurity6を使う。7系のアーティファクトは2026年10月時点で公開されていない。
  • Thymeleaf 3.1で#request・#sessionなどが使えなくなった。これらを使う画面は移行後の描画時にエラーになるので、上げる前に洗い出す。

Thymeleafの読み方と名前の由来

Thymeleafは「タイムリーフ」と読みます。公式サイトのFAQには「’Thymeleaf’ is pronounced like this: /ˈtaɪmˌlɪːf/」と発音記号つきで書かれています(Thymeleaf公式FAQ)。英語のthyme(ハーブのタイム)は先頭のthを「タ」と発音するため、綴りから「シームリーフ」「サイムリーフ」と読むのは誤りです。公式ロゴもタイムの葉です。

日本語表記は「タイムリーフ」、英字表記は「Thymeleaf」です。「タイムリー府」「タイムリーふ」と書かれていても、指しているものは同じです。検索するときや社内で表記を揃えるときは、英字の「Thymeleaf」を正としてください。

Thymeleafとは:Javaのサーバーサイドテンプレートエンジン

Thymeleafは、Javaのオブジェクトの値をHTMLに差し込んで画面を作るテンプレートエンジンです。ライセンスはApache License 2.0で、最新版は2026年4月21日リリースの3.1.5.RELEASE、必要なJavaはJava SE 8以上です(公式ダウンロードページ)。Spring MVCのビューとして使うのが最も一般的な用途で、メールのHTML本文の生成にも使えます。

設計の中核は「ナチュラルテンプレート」です。式は独自のタグではなく、HTMLの属性として書きます。

<p th:text="${message}">ここはダミーのテキストです</p>

このファイルをブラウザで直接開くと「ここはダミーのテキストです」と表示され、サーバーで描画するとmessageの値に置き換わります。同じファイルが「デザイナーが確認できる静的HTML」と「動くテンプレート」を兼ねるため、モックとテンプレートを二重に管理する必要がありません。JSPの<% %>のようにHTMLとして壊れた記法を混ぜないことが、Thymeleafを選ぶ最大の理由です。

なお、検索上位に出る公式の日本語チュートリアルは3.0版です。3.1版の日本語訳(Tutorial: Using Thymeleaf 3.1 (ja))も公開されているので、後述する3.1の変更点を踏まないためにも3.1版を参照してください。

Spring Boot 4.1でThymeleafを動かす手順

Thymeleafスターターの依存関係

以下はJava 17以上のSpring Boot 4.1.1プロジェクトを前提とします。Thymeleaf本体のJava SE 8以上という要件とは異なります。Spring Bootでは、スターターを追加するとThymeleafの自動設定が有効になります。バージョンはSpring Bootの依存管理に任せ、個別に書かないのが原則です。

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>

Gradleならimplementation 'org.springframework.boot:spring-boot-starter-thymeleaf'の1行です。Spring Boot 4ではWeb MVC用のスターターとしてspring-boot-starter-webmvcが用意されたので、画面を返すアプリではこちらも合わせて入れます(4系の依存名の変更はSpring Bootとは|4.1系の依存名の変更と最小Web APIの作り方で整理しています)。

Spring Bootの版によって、引き込まれるThymeleafの版が違います。Spring Bootの依存管理表とMaven Centralのspring-boot-dependenciesで確認した値は次のとおりです。

Spring Boot Thymeleaf Layout Dialect 脆弱性
3.5.0〜3.5.13 3.1.3 3.4.0 CVE 3件の対象
3.5.14〜3.5.16 3.1.5 3.4.0 修正済み
4.0.0〜4.0.5 3.1.3 3.4.0 CVE 3件の対象
4.0.6〜4.0.8 3.1.5 3.4.0 修正済み
4.1.0〜4.1.1 3.1.5 4.0.1 修正済み

Spring Boot自体のサポート期限と最新版はSpring Bootバージョン一覧とサポート期限(EOL)、4系の変更点はSpring Boot 4とは?最新バージョン4.1の変更点・新機能にまとめています。

コントローラとHTMLテンプレートの実装例

テンプレートの探索先はsrc/main/resources/templates/、拡張子は.htmlが既定です。コントローラが返した文字列が、このディレクトリ配下のファイル名に対応します。@Controllerとビューの関係はSpring MVCとは?spring-webmvcの役割で詳しく扱っています。

@Controller
public class HelloController {

    @GetMapping("/hello")
    public String hello(Model model) {
        model.addAttribute("message", "こんにちは、Thymeleaf");
        return "hello";
    }
}
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head><meta charset="UTF-8"><title>Hello</title></head>
<body>
  <h1 th:text="${message}">プレースホルダ</h1>
</body>
</html>

return "hello"でtemplates/hello.htmlが描画されます。xmlns:thの宣言はIDEの補完と検証のためのもので、無くても描画は動きます。

テンプレート更新とキャッシュ設定(spring.thymeleaf.cache)

テンプレートキャッシュと、編集したファイルが実行時のクラスパスへ反映されているかを確認します。spring.thymeleaf.cacheの既定値はtrueで、spring-boot-devtoolsを入れて開発実行している間はfalseに切り替わります(Spring Boot公式 DevTools)。DevToolsを入れていないなら、開発用のプロファイルで明示的に切ります。

spring.thymeleaf.cache=false

本番は既定のtrueのままにします。falseだと毎リクエストでテンプレートをパースし直すため、応答時間に響きます。

${}・*{}・@{}・#{} の使い分け

4種類の記号は評価する対象が違うだけで、1つのテンプレートの中で混ぜて使えます。

記法 名称 評価対象 典型例
${...} 変数式 Modelに入れた値 ${user.name}
*{...} 選択変数式 th:objectで選んだオブジェクト *{name}
@{...} リンクURL式 コンテキストパスを補ったURL @{/users/{id}(id=${user.id})}
#{...} メッセージ式 messages.propertiesの文言 #{home.welcome}

Spring Bootと組み合わせた場合、${}の中身はSpring EL(SpEL)で評価されます。*{}はth:objectで選んだオブジェクトを起点にするので、次の2行は同じ結果になります。

<div th:object="${user}">
  <p th:text="*{name}"></p>
  <p th:text="${user.name}"></p>
</div>

@{}を使う理由は、アプリを/appのようなサブパスに配置してもリンクが壊れないことです。href="/users"と直書きするとコンテキストパスが抜けて404になりますが、@{/users}なら実行時に補われます。@{/s(q=${param.q})}のように書いたクエリパラメータは、自動でURLエンコードされます。

主要なth:属性とコード例

th:text と th:utext(テキストの表示)

th:textはタグの中身を式の結果で置き換え、値をHTMLエスケープします。エスケープせずにHTMLとして出すのがth:utextです。<b>x</b>という文字列を渡すと、出力は次のように分かれます。

<p th:text="${html}"></p>   →  <p>&lt;b&gt;x&lt;/b&gt;</p>
<p th:utext="${html}"></p>  →  <p><b>x</b></p>

th:utextはXSSの入口です。ユーザー入力を含む値には使わず、サニタイズ済みと保証できる値に限ります。迷ったらth:textを選んでください。

th:if / th:unless / th:switch(条件分岐)

th:ifは条件が真のときだけタグごと出力し、th:unlessはその逆です。注意が要るのは真偽の判定で、Thymeleaf 3.1.5で試すと次の結果になりました。

値 th:ifの判定
null false
数値の0 false
文字列'off'・'no'・'false' false
空文字'' true
文字列'0' true
空のList true

空文字と空のListが真になる点が落とし穴です。「未入力なら出さない」「0件なら出さない」と書く場合は、th:unless="${#strings.isEmpty(name)}"やth:unless="${#lists.isEmpty(items)}"のように明示的に判定します(th:ifで書くならnotで否定します)。3分岐以上ならth:switchとth:caseが読みやすく、どれにも当たらない場合はth:case="*"が拾います。

<div th:switch="${user.role}">
  <p th:case="'ADMIN'">管理者</p>
  <p th:case="'STAFF'">スタッフ</p>
  <p th:case="*">一般</p>
</div>

th:each(繰り返し処理)

ListやMapを反復します。2つ目の変数にステータスを受けると、index(0始まり)・count(1始まり)・size・first・last・even・oddが使えます。

<tr th:each="item, stat : ${items}" th:class="${stat.odd} ? 'odd' : 'even'">
  <td th:text="${stat.count}">1</td>
  <td th:text="${item.name}">商品名</td>
</tr>

th:href / th:src / th:value / th:classappend(属性の書き換え)

th:hrefとth:srcは@{}と組み合わせ、パス変数はカッコで渡します。th:valueはvalue属性だけを書き換え、値はエスケープされます(a"bはvalue="a&quot;b"になります)。クラスは既存のclassを残して追記するth:classappendを使います。

<a th:href="@{/users/{id}(id=${user.id})}">詳細</a>
<img th:src="@{/images/logo.png}" alt="ロゴ">
<input type="text" name="q" th:value="${q}">
<span class="badge" th:classappend="${item.soldOut} ? 'badge-gray'">在庫</span>

th:inlineによるJavaScriptへの値の受け渡し

<script th:inline="javascript">の中では、[[...]]に書いた値が、その型に応じたJavaScriptの値として出力されます。文字列は文字列リテラルに、数値・真偽値・配列・オブジェクトは対応する形式に変換されます。O'Reilly</script>という値を渡すと、出力は"O'Reilly<\/script>"となり、クォートと</がエスケープされます。

<script th:inline="javascript">
  const userName = /*[[${user.name}]]*/ 'ダミー';
</script>

/*[[...]]*/とコメントで囲む書き方にすると、テンプレートをブラウザで直接開いたときはコメント外の'ダミー'が使われ、ナチュラルテンプレートの性質を保てます。

フォームの入力・バインディングとエラー表示

th:objectとth:fieldによるフォーム属性の生成

Spring MVCのフォームでは、th:objectでフォームオブジェクトを選び、各入力欄をth:fieldで結びつけます。th:fieldには選択変数式*{}を使います。

<form th:action="@{/form}" th:object="${form}" method="post">
  <input type="text" th:field="*{name}">
  <input type="checkbox" th:field="*{agree}">
  <select th:field="*{plan}">
    <option th:each="p : ${plans}" th:value="${p}" th:text="${p}">x</option>
  </select>
</form>

Spring Boot 4.1.1で描画した結果が次です(planの初期値は"pro")。この検証では出力を見やすくするためCSRF保護を無効にしており、本番での無効化を勧めるものではありません。Spring SecurityのCSRF保護が有効な場合は、POSTフォームにCSRF用の隠し項目も追加されます。

<form action="/form" method="post">
<input type="text" id="name" name="name" value="">
<input type="checkbox" id="agree1" name="agree" value="true"><input type="hidden" name="_agree" value="on"/>
<select id="plan" name="plan"><option value="free">free</option><option value="pro" selected="selected">pro</option></select>
</form>

th:fieldはid・name・valueをまとめて生成し、選択肢には現在値に合わせてselectedを付けます。チェックボックスには_agreeという隠し項目が自動で付きます。チェックを外したチェックボックスはブラウザが何も送らないため、Spring MVCはこの隠し項目を見て「falseが送られた」と判断します。th:valueはvalueしか書き換えないので、フォームの入力欄はth:fieldで書くのが基本です。checkboxのidがagree1と連番になる点は、<label for>を手書きするときに注意してください。

th:errorsによるバリデーションエラーの表示

以下の入力検証には、org.springframework.boot:spring-boot-starter-validationの依存関係が必要です。@Validと@NotBlankはjakarta.validation系のクラスを使用し、フォームのnameに@NotBlankを付けます。

th:errorsは、コントローラの@ValidとBindingResultで検出したエラーメッセージを描画します。

<span th:if="${#fields.hasErrors('name')}" th:errors="*{name}">エラー</span>
@ModelAttribute("plans")
public java.util.List<String> plans() {
    return java.util.List.of("free", "pro");
}

@PostMapping("/form")
public String post(@Valid @ModelAttribute("form") Form form, BindingResult result) {
    if (result.hasErrors()) {
        return "form";
    }
    return "redirect:/done";
}

@NotBlankを付けたnameを空で送ると、日本語ロケールのJVMでは<span>空白は許可されていません</span>が出力されました。メッセージはBean Validationの既定文言なので、画面に合わせるならValidationMessages.propertiesで上書きします。フォーム全体のエラーをまとめて出すならth:errors="*{all}"、有無だけ見るなら${#fields.hasAnyErrors()}です。入力検証をどこで何をするかの設計はバリデーションとは?入力検証の目的・種類・実装パターンで扱っています。

フラグメントによる部品の共通化(th:fragment / th:insert / th:replace)

ヘッダーやフッターのように複数画面で使う部分は、th:fragmentで名前を付けて切り出します。引数も渡せます。

<!-- templates/parts.html -->
<footer th:fragment="foot(year)" class="f">&copy; <span th:text="${year}">y</span></footer>

呼び出し側のth:insertとth:replaceは、ホストタグを残すかどうかが違います。実際の出力は次のとおりです。

<div id="ins" th:insert="~{parts :: foot(2026)}">a</div>
→ <div id="ins"><footer class="f">&copy; <span>2026</span></footer></div>

<div id="rep" th:replace="~{parts :: foot(2026)}">b</div>
→ <footer class="f">&copy; <span>2026</span></footer>

ホスト要素を残す必要がないのに入れ子のdivが生じている場合は、th:replaceを使うべき箇所でth:insertを使っています。共通部品の差し込みはth:replaceを既定にしてください。古いth:includeは3.1で非推奨になり、使うと警告が出ます(Thymeleaf ChangeLog)。既存コードはth:insertかth:replaceへ置き換えます。

Layout Dialectによる共通レイアウトの構成

フラグメントは「部品を呼び出す」方向の仕組みです。「共通の外枠に各画面の中身をはめ込む」構成も標準のフラグメントで実現できますが、外枠への差し込みやhead要素の統合を専用の属性で扱いたいなら、Thymeleaf Layout Dialect(nz.net.ultraq.thymeleaf:thymeleaf-layout-dialect)を使います。Spring Bootの依存管理に含まれているので、バージョンを書かずに追加でき、追加すれば自動で有効になります。

<dependency>
    <groupId>nz.net.ultraq.thymeleaf</groupId>
    <artifactId>thymeleaf-layout-dialect</artifactId>
</dependency>

外枠のlayout.htmlに差し込み口をlayout:fragmentで作り、各画面はlayout:decorateで外枠を指定します(Layout Dialect公式リポジトリ)。

<!-- templates/layout.html -->
<html xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout">
<head><title layout:title-pattern="$CONTENT_TITLE | Site">Site</title></head>
<body><header>HEADER</header><main layout:fragment="content">default</main><footer>FOOTER</footer></body>
</html>

<!-- templates/page.html -->
<html layout:decorate="~{layout}" xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout">
<head><title>Page</title></head>
<body><main layout:fragment="content"><p>PAGE BODY</p></main></body>
</html>

pageを描画すると、外枠のheader・footerの間に<main><p>PAGE BODY</p></main>が入り、titleはtitle-patternに従ってPage | Siteになりました。

導入前に知っておくべき点が2つあります。1つは版の違いで、Spring Boot 4.1は4.0.1、4.0系と3.5系は3.4.0を管理しています。もう1つは依存の重さで、4.0.1を入れると、Layout Dialect本体を含む4つのJARが実行可能JARに加わり、そのうちGroovy 5.0.8は約8MBありました。画面が数枚でヘッダーとフッターを揃えたいだけなら、th:replaceのフラグメントで足ります。外枠のtitleやCSS・JSの差し替えを画面ごとに管理したくなった段階で入れるのが妥当です。

Spring Securityとの連携(sec:authorize / sec:authentication)

ログイン中のユーザー名の表示や、ロールに応じたメニューの出し分けにはthymeleaf-extras-springsecurity6を使います。Spring Boot 4.1.1はSpring Security 7.1.1を引き込みますが、Thymeleaf側のアーティファクトは6のままです。Spring Bootの依存管理表に載っているのもthymeleaf-extras-springsecurity6 3.1.5.RELEASEで、thymeleaf-extras-springsecurity7は2026年10月20日時点でMaven Centralに存在しません。7系のモジュールはThymeleaf 3.2.0で追加する予定として公式Issue #1065に挙がっている段階です。

<dependency>
    <groupId>org.thymeleaf.extras</groupId>
    <artifactId>thymeleaf-extras-springsecurity6</artifactId>
</dependency>
<html xmlns:sec="http://www.thymeleaf.org/extras/spring-security">
<p sec:authorize="isAuthenticated()">logged in as <span sec:authentication="name">n</span></p>
<p sec:authorize="hasRole('ADMIN')">ADMIN MENU</p>
<p sec:authorize="isAnonymous()">guest</p>
</html>

未ログインではguestだけが、ADMINロールのユーザーでログインするとlogged in as taroとADMIN MENUが出力されました。sec:authorizeは表示を出し分けるだけで、URLへのアクセスは止めません。管理画面のURL自体はSecurityFilterChainのauthorizeHttpRequestsで必ず保護してください。認可設定の基本はSpring Securityとは?認証・認可の仕組みと基本設定を7.1で解説にあります。

カスタムエラーページの配置(templates/error/404.html)

Spring Bootは、errorディレクトリにステータスコード名のファイルを置くと、それをエラーページとして使います。名前は404.htmlのような完全なコードか、5xx.htmlのような系列でも構いません(Spring Boot公式 Custom Error Pages)。Thymeleafで書くならsrc/main/resources/templates/error/に置きます。

<!-- templates/error/404.html -->
<h1>カスタム404</h1><p th:text="${status}">s</p><p th:text="${path}">p</p>

存在しない/nopeをAccept: text/htmlで開くと、ステータス404でカスタム404・404・/nopeが描画されました。同じURLをAccept指定なしのcurlで叩くと、テンプレートは使われず{"status":404,"error":"Not Found","path":"/nope"}というJSONが返ります。「curlで確かめたらエラーページが出ない」のは設定ミスではなく、この振り分けのためです。

Thymeleafの脆弱性と3.1の式の制限

3.1.3以下・3.1.4以下に残るcriticalの脆弱性

2026年4月に、Thymeleaf本体(thymeleaf・thymeleaf-spring5・thymeleaf-spring6)でサーバーサイドテンプレートインジェクション(SSTI)につながる脆弱性が3件公表されました。いずれも深刻度はcriticalです。

CVE 公表日 影響する版 修正版
CVE-2026-40477 2026-04-15 3.1.3以下 3.1.4
CVE-2026-40478 2026-04-15 3.1.3以下 3.1.4
CVE-2026-41901 2026-04-29 3.1.4以下 3.1.5

前述の表のとおり、Spring Boot 3.5.13以前と4.0.5以前は3.1.3を引き込むため、3件すべての対象です。Thymeleafだけを上書きするより、Spring Bootを3.5.14以降・4.0.6以降・4.1系へ上げて、依存管理ごと3.1.5に揃えるのが確実です。mvn dependency:tree | grep thymeleafで実際に入っている版を確認できます。

3.1で入った式の制限(restricted mode)

Thymeleaf 3.1は、テンプレートの式から触れられる範囲を絞っています。3.1.5で試すと次のように動きました。

テンプレートの式 結果
${T(java.lang.Math).max(1,2)} 2(許可)
${T(java.util.UUID).randomUUID()} UUIDを出力(許可)
${T(java.lang.Runtime).getRuntime()...} 500(Access is forbidden)
${T(java.lang.System).getProperty(...)} 500(Access is forbidden)
__${param.q}__(前処理) 500(forbidden in this context)
${#strings.toUpperCase(param.q)} ABC(q=abcのとき・通常の式なら可)

java.*を一律に禁じるのではなく、MathやUUIDなどの許可リストにあるクラスだけを通し、RuntimeやSystemのような危険なクラスはAccess is forbidden for type 'java.lang.Runtime' in this expression context.で止めます。前処理__...__やth:utextなどrestricted modeが働く箇所では、${param.*}でリクエストパラメータに触れること自体が禁止されます(Thymeleaf 3.1公式チュートリアル)。

ただし公式チュートリアルは、アプリケーションがModelに入れた値がユーザー入力由来かどうかをThymeleafは追跡できない、とも明記しています。リクエストの値をコントローラで受けてModelに詰め直せば、この制限は効きません。ビュー名やフラグメント名をリクエストの値から組み立てない、th:utextにユーザー入力を渡さない、の2点はアプリ側の責任です。XSS対策をブラウザ側でも重ねるならコンテンツセキュリティポリシー(CSP)とは?nonce方式の設定とXSS防御が参考になります。

Thymeleaf 3.1へ上げたときに踏む落とし穴(#request・#sessionの廃止)

Spring Boot 2からBoot 3以降へ移行する際は、既存テンプレートの式オブジェクトを確認してください。Thymeleaf 3.1で、テンプレートから直接使えていた#request・#response・#session・#servletContextが使えなくなりました(ChangeLogの記載は「Removed web-API based expression security objects」)。3.0系にあった#httpServletRequest・#httpSessionは、3.1では名前ごと無くなっています。

テンプレートに${#request.requestURI}と書いて3.1.5で開くと、ステータス500になり、ログに次の例外が出ます。

java.lang.IllegalArgumentException: The 'request','session','servletContext' and 'response' expression utility objects are no longer available by default for template expressions and their use is not recommended. In cases where they are really needed, they should be manually added as context variables.

コンパイルは通り、その画面を開いた瞬間に初めて落ちるので、テストで通っていない画面から本番で表面化します。共通ヘッダーのフラグメントにログインユーザー名を${#session.user}で書いていると、全画面が落ちます。対処の一つは、コントローラで必要な値だけをModelに詰めることです。セッション属性の参照なら、3.1でも利用できる${session.user}への置換も選べます。廃止された#sessionと、属性参照用のsessionは別物です。

@GetMapping("/mypage")
public String mypage(HttpSession session, Model model) {
    model.addAttribute("loginUser", session.getAttribute("user"));
    return "mypage";
}

上げる前に、テンプレート全体をgrep -rnE "#(request|response|session|servletContext|httpServletRequest|httpSession)" src/main/resources/templatesで洗い出しておくのが最短です。ログインユーザーの表示が目的なら、前述のsec:authenticationに置き換えるとコントローラ側の詰め替えも要りません。

JSP・FreeMarkerとの違いとThymeleafを選ぶ基準

項目 Thymeleaf JSP FreeMarker
記法 HTML属性(th:) 独自タグ・スクリプトレット 独自記法(<#if>等)
ブラウザで直接表示 静的モックを作成可能 動的部分は未処理 動的部分は未処理
Spring Bootのスターター あり なし(WAR前提) あり
向く用途 画面付きWebアプリ 既存資産の保守 メール・帳票

新規のSpring Bootアプリでサーバー側から画面を返すなら、Thymeleafを選んで問題ありません。JSPは実行可能JAR(組み込みTomcat)では動かない制約があり、WARでの配置が前提になるため、新規に採用する理由がありません。同じJavaの画面技術でもコンポーネント指向のJakarta Facesは考え方が別物で、比較はJSF(Jakarta Faces)とは?仕組み・使い方で扱っています。

Thymeleafを選ぶべきでない場面もはっきりしています。画面をReactやVueで作り、サーバーはJSONを返すAPIに徹する構成なら、Thymeleafの依存は不要です。HTML生成に使わない依存関係を残すと、更新・脆弱性管理の対象が増えます。依存関係があるだけで、画面用のコントローラやURLが自動作成されるわけではありません。判断の基準は「Spring Bootだから入れる」ではなく「サーバーがHTMLを組み立てて返すかどうか」です。業務システムで画面とAPIのどちらに寄せるかを含めて構成から相談したい場合は、フルスクラッチ開発の支援も受け付けています。

よくある質問

Thymeleafの読み方は何ですか?

「タイムリーフ」です。公式FAQの発音表記は /ˈtaɪmˌlɪːf/ で、thyme(タイム)+leafの名前です。

JSPとThymeleafの違いは何ですか?

Thymeleafは式をHTMLの属性として書くため、ダミーの内容や静的資源へのパスを用意すれば、ブラウザで直接確認できる静的モックを作れます。JSPはHTMLとして壊れた記法を混ぜるうえ、Spring Bootの実行可能JARでは動きません。新規開発ならThymeleafです。

Thymeleafにはどんな脆弱性がありますか?

2026年4月に、SSTIにつながるcriticalの脆弱性3件(CVE-2026-40477ほか)が公表され、3.1.5で修正されました。Spring Boot 3.5.13以前と4.0.5以前は3.1.3を引き込むので、Spring Bootごと上げてください。

Spring Boot 3に上げたらテンプレートが500エラーになりました。原因は?

Thymeleaf 3.1で#request・#sessionなどを使用していることが原因の一つとして考えられます。ログに「expression utility objects are no longer available」と出ていれば該当し、値をコントローラでModelに詰めて渡せば直ります。

テンプレートを修正しても画面に反映されないのはなぜですか?

テンプレートキャッシュ(spring.thymeleaf.cache、既定値true)が原因の一つです。編集したHTMLが実行時のクラスパスへコピーされていることも確認します。キャッシュが原因なら、開発時だけfalseにするか、spring-boot-devtoolsを入れてください。

関連記事

お気に入りに入れた記事の一覧

この記事は以下の記事からリンクされています

資料請求

今日のトレンド記事 直近 24 時間で、いつもより多く読まれている記事

  1. 2026.10.23 テックブログ Renta!不正アクセスでサービス停止:再開未定の現状と運営側の停止判断・証拠保全・段階的再開
  2. 2026.10.21 テックブログ ふるさとチョイス不正アクセスで445万件流出:対象確認・パスワード変更と再開時期
  3. 2026.10.24 テックブログ カプコン不正アクセス:CAPCOM ID 1,177万件漏えいと196件の乗っ取りへの対処
  4. 2026.10.24 テックブログ ブラストメール不正アクセス:配信先288件の漏えいと利用企業の報告・通知の手順
  5. 2026.10.14 テックブログ ローソンID不正アクセス:215万件漏えいの対象確認と本物メールの見分け方

RELATED POSTS 関連記事

目次