タイトル: 独自のエラーページを定義する方法
SEOタイトル: 独自のエラーページを定義する方法 (Servlet / Spring Boot / Laravel / Apache)
| この記事の要点 |
|
1. Servlet / JSP (web.xml)
従来の Java Web アプリは WEB-INF/web.xml でステータスコードや例外単位にエラーページを指定:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee" version="6.0">
<!-- ステータスコード別 -->
<error-page>
<error-code>404</error-code>
<location>/WEB-INF/jsp/error/404.jsp</location>
</error-page>
<error-page>
<error-code>500</error-code>
<location>/WEB-INF/jsp/error/500.jsp</location>
</error-page>
<!-- 例外クラス別 -->
<error-page>
<exception-type>java.lang.Throwable</exception-type>
<location>/WEB-INF/jsp/error/500.jsp</location>
</error-page>
<error-page>
<exception-type>com.example.BusinessException</exception-type>
<location>/WEB-INF/jsp/error/business.jsp</location>
</error-page>
</web-app><%-- 404.jsp --%>
<%@ page isErrorPage="true" pageEncoding="UTF-8" %>
<!DOCTYPE html>
<html>
<head><title>404 Not Found</title></head>
<body>
<h1>ページが見つかりません</h1>
<p>URL: <%= request.getAttribute("javax.servlet.error.request_uri") %></p>
<p>ステータス: <%= request.getAttribute("javax.servlet.error.status_code") %></p>
<a href="${pageContext.request.contextPath}/">トップへ</a>
</body>
</html>
2. Spring Boot のエラーページ
Spring Boot は規約に従ってファイルを置くだけで自動的にカスタムエラーページが有効化:
src/main/resources/
├── templates/
│ └── error/
│ ├── 404.html ← 404 専用
│ ├── 500.html ← 500 専用
│ ├── 4xx.html ← 4xx 全般
│ └── 5xx.html ← 5xx 全般
└── static/
└── error/
└── 404.html ← 静的エラーページ (Thymeleaf 不使用時)<!-- templates/error/404.html (Thymeleaf) -->
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head><title>404 Not Found</title></head>
<body>
<h1>ページが見つかりません</h1>
<p>URL: <span th:text="${path}">/foo</span></p>
<p>ステータス: <span th:text="${status}">404</span></p>
<p>メッセージ: <span th:text="${error}">Not Found</span></p>
<a th:href="@{/}">トップへ</a>
</body>
</html>
application.properties でカスタマイズ:
# エラー情報の出力制御
server.error.include-stacktrace=never # 本番はスタックトレース非表示
server.error.include-message=always
server.error.include-binding-errors=always
server.error.include-exception=false
server.error.whitelabel.enabled=false # 既定の Whitelabel を無効化
# エラーパス変更
server.error.path=/error
3. Spring Boot の @ControllerAdvice
例外単位で柔軟に処理を分けたい場合は @ControllerAdvice:
@ControllerAdvice
@Slf4j
public class GlobalExceptionHandler {
// 業務例外 → 専用画面へ
@ExceptionHandler(BusinessException.class)
public String handleBusiness(BusinessException ex, Model model) {
model.addAttribute("message", ex.getMessage());
return "error/business";
}
// 404 系 → カスタム 404 ページ
@ExceptionHandler(NoHandlerFoundException.class)
@ResponseStatus(HttpStatus.NOT_FOUND)
public String handle404(NoHandlerFoundException ex) {
return "error/404";
}
// 想定外 → 500 ページ
@ExceptionHandler(Exception.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public String handleAll(Exception ex, Model model) {
log.error("予期せぬエラー", ex);
model.addAttribute("traceId", MDC.get("traceId"));
return "error/500";
}
}
// REST API の場合は @RestControllerAdvice + ResponseEntity
@RestControllerAdvice
public class ApiExceptionHandler {
@ExceptionHandler(EntityNotFoundException.class)
public ResponseEntity<Map<String, String>> notFound(EntityNotFoundException ex) {
return ResponseEntity.status(404)
.body(Map.of("error", "not_found", "message", ex.getMessage()));
}
}
404 を Spring Boot で捕まえるには下記設定も必要:
spring.mvc.throw-exception-if-no-handler-found=true
spring.web.resources.add-mappings=false
4. Laravel のエラービュー
Laravel は resources/views/errors/ 配下に対応するステータスコードのファイルを置くだけ:
resources/views/errors/
├── 404.blade.php
├── 419.blade.php ← CSRF expired
├── 500.blade.php
└── 503.blade.php ← メンテナンス{{-- resources/views/errors/404.blade.php --}}
@extends('layouts.app')
@section('title', '404 Not Found')
@section('content')
<div class="text-center py-20">
<h1 class="text-6xl">404</h1>
<p>お探しのページが見つかりません</p>
<a href="{{ url('/') }}" class="btn btn-primary">トップへ</a>
</div>
@endsection
{{-- $exception で例外オブジェクトにアクセス可 --}}
{{-- {{ $exception->getMessage() }} --}}
柔軟に処理を変えたい場合は app/Exceptions/Handler.php:
public function register(): void
{
$this->renderable(function (NotFoundHttpException $e, $request) {
if ($request->is('api/*')) {
return response()->json(['error' => 'not_found'], 404);
}
return response()->view('errors.404', [], 404);
});
}
5. Apache / nginx のエラーページ
# .htaccess または httpd.conf
ErrorDocument 404 /errors/404.html
ErrorDocument 500 /errors/500.html
ErrorDocument 503 /errors/maintenance.html
# 外部 URL にリダイレクト
ErrorDocument 404 https://example.com/notfoundserver {
error_page 404 /404.html;
error_page 500 502 503 504 /50x.html;
location = /404.html {
internal;
root /usr/share/nginx/html;
}
# API のみ JSON で返す
location /api {
error_page 404 = @json_404;
}
location @json_404 {
default_type application/json;
return 404 '{"error":"not_found"}';
}
}
表示確認のコツ
| 確認方法 | コマンド / 操作 |
|---|---|
| 404 を出す | 適当な URL /notexist をブラウザで叩く |
| 500 を出す | テスト用 endpoint /test/error で throw new RuntimeException() |
| Status を確認 | curl: curl -I http://localhost/notexist |
| JSON / HTML の出し分け | curl: -H "Accept: application/json" で差分確認 |
FAQ
Q: Spring Boot で 404 が error/404.html に行かない
A: spring.web.resources.add-mappings=false を設定して静的リソースの自動マッピングを無効化。さらに throw-exception-if-no-handler-found=true を有効化。
Q: 本番でスタックトレースが表示されてしまう
A: Spring Boot は server.error.include-stacktrace=never。Laravel は .env の APP_DEBUG=false。
Q: メンテナンス画面を出したい
A: nginx で 503 と Retry-After ヘッダ。Laravel なら php artisan down --render="errors::503"。