Custom error pages may help you to unify look of all content served by your website no matter if it is served by Tomcat, by Apache or both. In our environment error pages are either served by Apache webserver or by your application server (Tomcat in below examples) depending on mapping scheme you set.
In Java Control Panel you can define which URLs are served by webserver and which ones by application server (let's refer to it as Tomcat from now on). By default, when Tomcat is down then generic error message will be produced by Apache. In case of WHM/cPanel server generic messages are defined in /etc/httpd/conf/includes/errordocument.conf
.
Let's review most common error messages:
503 - Service Temporarily Unavailable This is generic message from webserver when Apache cannot contact Tomcat to proxy requests to it.
404 - The requested resource is not available Can be generated by Tomcat or by Apache depending on which of them is set to serve the request.
403 - Forbidden Generated by Apache if permissions do not allow access to the resource.
Defining custom error pages for paths/URLs served by Apache
To change the generic messages to your own ones login to cPanel and choose Advanced - Error Pages. For example we can change the generic 503 shown when our Tomcat is down or overloaded to "We seem to be overloaded. Check again in 15 minutes or better report the problem to user at mailbox dot com".
This action will save 503.shtml
in your public_html
directory. But if all your requests are proxied to Tomcat (catch-all mapping for root URL '/' is our default setting) this file will be unreachable. In such case you need to additionally ensure /503.shtml
is unmapped from being proxied to Tomcat. Go to Java Control Panel - Mappings and add /503.shtml
to Exclude section.
Now shutdown your Tomcat and access your website. You should get the message that you defined in cPanel in custom 503.shtml
page. You can also edit the file directly in public_html.
Defining error pages for paths/URLs served by Tomcat
In Tomcat, error pages are defined in web.xml
- either global one or application specific one. Before Servlet 3.0 specification (Tomcat 7.0.x) you could define error page for either exception or response code using error-page
element this way:
<error-page>
<exception-type>java.lang.Throwable</exception-type>
<!-- it may point to a handler -->
<location>/ErrorHandler</location>
</error-page>
<error-page>
<error-code>404</error-code>
<location>/ErrorHandler</location>
<!-- <location>/error/404.html</location> -->
</error-page>
Starting from Servlet 3.0 (Tomcat 7) you can also use general error page/handler:
<error-page>
<location>/ErrorHandler</location>
</error-page>
To verify it, add error-page
element to webapps/ROOT/WEB-INF/web.xml
and create webapps/ROOT/error/404.html
with your content. Then access a non-existent URL on a path served by your Tomcat to see your custom message.
An example of custom exception and error handler servlet
Save the below code into webapps/ROOT/WEB-INF/classes/com/jvmhost/ErrorHandler.java
package com.jvmhost;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class ErrorHandler extends HttpServlet {
private static final long serialVersionUID = 1L;
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { processError(request, response); }
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { processError(request, response); }
private void processError(HttpServletRequest request, HttpServletResponse response) throws IOException {
Integer statusCode = (Integer)request.getAttribute("javax.servlet.error.status_code");
Throwable throwable = (Throwable)request.getAttribute("javax.servlet.error.exception");
String servletName = (String)request.getAttribute("javax.servlet.error.servlet_name");
if (servletName == null) { servletName = "Undetermined"; }
String requestUri = (String) request.getAttribute("javax.servlet.error.request_uri");
if (requestUri == null) { requestUri = "Undetermined"; }
response.setContentType("text/html");
PrintWriter out = response.getWriter();
out.write("<html><head><title>Exception or Error</title></head><body>");
if(statusCode != 500){ // not Exception
out.write("<h1>Error</h1>");
out.write("<b>Status Code:</b>"+statusCode+"<br>");
out.write("<b>Request URI:</b>"+requestUri);
}else{
out.write("<h1>Exception</h1>");
out.write("<b>Servlet Name:</b>"+servletName+"<br/>");
out.write("<b>Exception Name:</b>"+throwable.getClass().getName()+"<br/>");
out.write("<b>Request URI:</b>"+requestUri+"<br/>");
out.write("<b>Exception Message:</b>"+throwable.getMessage());
}
out.write("<br><br></body></html>");
}
}
Enter the jvmhost
directory and compile the code, for example with
javac -cp ~/appservers/apache-tomcat-7.0.39/lib/servlet-api.jar ErrorHandler.java
Remove any other error-page
elements and insert into webapps/ROOT/WEB-INF/web.xml
:
<error-page>
<location>/ErrorHandler</location>
</error-page>
<servlet>
<servlet-name>ErrorHandler</servlet-name>
<servlet-class>com.jvmhost.ErrorHandler</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>ErrorHandler</servlet-name>
<url-pattern>/ErrorHandler</url-pattern>
</servlet-mapping>
Restart Tomcat and access a non-existent URL on a path served by your Tomcat
Other hints
If you would like to receive only error messages defined on Apache level (either generic or custom) you may request adding ProxyErrorOverride On
by support. This way you will not get 404 message (and few other ones) from Tomcat but from Apache. It may help you to unify look of your error messages as you will only be defining them on Apache level.
For SEO purposes you may also want to add <meta name="robots" content="noindex, nofollow" />
to your custom error pages.
Feel free to add any comments or questions to the article.