1. Problem Definition: Inconsistent Error Screens and Security Risks
In enterprise and microservices web architectures, when an error occurs, inconsistent screens are displayed depending on the layer where the error originated. In an environment with an Nginx reverse proxy at the front and a Spring Boot application embedded in Tomcat behind it, the following fragmented screens may appear.
-
Spring Boot Whitelabel Error Page: The HTTP status code, timestamp, and error message are displayed raw in the browser.
-
Apache Tomcat Default Error Page: When a 404/500 error occurs at the servlet container level, Tomcat's characteristic grayscale screen appears.
-
Nginx Default Error Page: When the backend application is down or times out, a crude 502 Bad Gateway / 504 Gateway Time-out text screen is displayed.
This fragmentation not only harms the user experience (UX), but also causes security vulnerabilities (CWE-209: Information Exposure). Server Fingerprinting, in which Tomcat version information or framework stack traces are exposed, provides attackers with clues for analyzing system vulnerabilities.

Spring Boot Error Page
Apache Tomcat Error Page
Nginx Error Page
2. Technical Limitations: Limitations of Spring Boot's Internal Processing
Spring Boot's @ExceptionHandler or ErrorController works only when the application (JVM/Tomcat) process is running normally and able to generate a response.
In real-world production environments, critical failures occur in situations where the application code cannot even be executed.
-
JVM OOM (Out of Memory) Crash: When the process is forcibly terminated because the heap memory is exhausted (502 Bad Gateway)
-
Tomcat Thread Pool / DB Connection Pool Exhaustion: When Tomcat cannot establish new connections due to massive traffic (504 Gateway Time-out)
-
Rolling Update Interruption During Zero-Downtime Deployment: Requests that arrive during the brief moment before a new container starts
Because Spring Boot code cannot be executed in these infrastructure-level failure situations, Nginx's raw error screen is exposed. To achieve true high-availability error control, error handling must be centralized at the Nginx layer, the first gateway for traffic.
3. Solution Design: Centralized Control Based on Nginx proxy_intercept_errors
The key mechanism for transforming Nginx into the front-line error controller is the proxy_intercept_errors directive. When this directive is enabled, Nginx intercepts the response body when the HTTP response code returned by the Upstream (Tomcat/Spring Boot) is 300 or higher and internally reroutes it to a predefined error_page path.
-
Security Isolation: Regardless of which exception occurs in the backend, the response body is discarded and only Nginx's sanitized static file is served, preventing the exposure of internal information.
-
Infrastructure-Level Fallback: Provides a consistent service information screen regardless of whether Spring Boot is running.
-
Blocking Direct Access: The internal directive prevents users from directly accessing /error/500.html by entering it in the browser's address bar.
4. Practical Production nginx.conf Configuration Example
This is a core nginx.conf configuration example that intercepts Tomcat and Spring Boot errors and routes them to custom error screens.
# 1. [Content Negotiation] 요청 헤더(Accept) 기반 응답 포맷 판별
# React 비동기 API 요청(JSON)과 일반 웹 브라우징(HTML)을 구분합니다.
map $http_accept $error_response_type {
default "html";
~*application/json "json";
~*text/json "json";
}
server {
listen 80;
server_name service.example.com;
# 2. [핵심] 뒷단 WAS(Tomcat/Spring Boot)의 3xx/4xx/5xx 에러를 가로챕니다.
proxy_intercept_errors on;
# 3. HTTP 상태 코드별 내부 에러 디스패처로 라우팅
error_page 404 = /handle_error_404;
error_page 500 = /handle_error_500;
error_page 502 = /handle_error_502;
error_page 504 = /handle_error_504;
# 기본 비즈니스 프록시 패스
location / {
proxy_pass http://backend_app;
# WAS 장애 시 빠른 Nginx 에러 핸들러 전환을 위한 타임아웃
proxy_connect_timeout 3s;
proxy_read_timeout 10s;
}
# 4. [내부 디스패처] 헤더에 따라 JSON vs HTML 분기 반환
# internal 지시어로 외부 사용자의 직접 URL 호출을 차단합니다.
location = /handle_error_500 {
internal;
if ($error_response_type = "json") {
default_type application/json;
return 500 '{"status": 500, "code": "INTERNAL_SERVER_ERROR", "message": "서버 오류가 발생했습니다."}';
}
rewrite ^ /error/500.html break;
}
# 5. [WAS 다운 대응] Spring Boot 프로세스 다운(OOM 등) 시 502 처리
location = /handle_error_502 {
internal;
if ($error_response_type = "json") {
default_type application/json;
return 502 '{"status": 502, "code": "SERVICE_UNAVAILABLE", "message": "현재 서비스 점검 중입니다."}';
}
rewrite ^ /error/502.html break;
}
# 6. [정적 파일 격리] 커스텀 HTML 에러 페이지 서빙 (직접 접근 불가)
location /error/ {
internal;
alias /usr/share/nginx/html/error/;
add_header Cache-Control "no-store, no-cache, must-revalidate";
}
}
Nginx.conf Example Code
5. Advanced Design: The White Screen Issue in React Integration and Its Solution
Simply applying proxy_intercept_errors on; works normally for regular web pages, but causes a critical runtime crash in a React SPA environment.
-
Problem (SyntaxError and White Screen):
When the backend returns a 500 error or Tomcat goes down (502) while React is calling an API with axios/fetch, Nginx returns the HTML document specified in error_page. The frontend attempts to parse it as JSON, causing SyntaxError: Unexpected token '<' and making the entire screen stop on a white screen. -
Solution (Nginx Content Negotiation):
Use Nginx's map module to determine the Accept value in the request header, routing regular browsing requests (text/html) to an HTML screen and immediately returning a standardized JSON error schema for React asynchronous API requests (application/json). -
Improved Results:
Even when Spring Boot is completely down, React's global Axios interceptor can receive a standardized JSON response and safely display a "Server under maintenance" modal or toast notification, achieving a complete infrastructure-level Fallback.
6. Conclusion: Infrastructure Reliability Created by Layer Separation
Proxy-layer error control using Nginx is more than simple design customization; it is a core architecture that separates the concerns of the infrastructure (Nginx) and the application (Tomcat/Spring Boot) (Separation of Concerns).
By having the application focus on pure business logic and domain exceptions, while the front-line proxy intercepts infrastructure failures and downtime and responds in a standardized format, it is possible to ensure service stability and security simultaneously.
TK