Kubernetes tips & tricks: custom error pages in NGINX Ingress
In this article, I want to talk about two features of NGINX Ingress related to displaying personalized error pages, as well as their limitations and ways to get around them.
1. Change the default backend
By default, NGINX Ingress uses the default backend, which performs the corresponding function. This means that when requesting an Ingress with a host that is not in the Ingress resources, we get this page with a 404 response code:
However, more and more often, our clients come with a request to show their page with a company logo and other amenities instead of the standard 404. For this, NGINX Ingress has built-in capability redefine default-backend-service. We pass the format record as an argument to the option of the same name namespace/servicename. The service port must be 80.
To do this, you need to create your own pod (deployment) and a service with your application (example implementation in YAML from the ingress-nginx repository), which will be given instead of the default backend.
Here is a small illustration:
~$ curl -i -XGET http://sadsdasdas.kube-cloud.my/
HTTP/1.1 404 Not Found
Date: Mon, 11 Mar 2019 05:38:15 GMT
Content-Type: */*
Transfer-Encoding: chunked
Connection: keep-alive
<span>The page you're looking for could not be found.</span>
So all domains that are not explicitly created via YAML with kind: Ingress, fall into the default-backend. In the listing above, this domain became sadsdasdas.
2. Handling HTTP errors in the application by the default backend
Another situation is requests ending with HTTP errors (404, 500, 502…) to an application that does not handle such situations (corresponding beautiful pages are not generated). It may also be caused by the desire of developers to return the same error pages in multiple applications.
To implement this case on the server side, we need:
Follow the instructions above from the paragraph about the default backend;
Add the key to the configuration ConfigMap nginx-ingress custom-http-errors, for example, with the value 404,503 (obviously matches the error codes covered by the new rule).
The expected result is achieved: when the client application is running and receiving an error with a 404 or 503 response code, the request will be automatically redirected to the new default backend ...
However, when developing an application for the default backend and custom-http-errors, an important feature must be taken into account:
!!! Important The custom backend is expected to return the correct HTTP status code instead of 200. NGINX does not change the response from the custom default backend.
The fact is that when the request is redirected, the headers will contain useful information with the previous response code and additional information (their full list is available here).
This means that you must take care of the correct response code. Here's an example from the documentation how it works.
Different applications - different default backend
So that the solution is not global for the entire cluster, but applies only to specific applications, first you need to check the version of Ingress. If it matches 0.23 or higher, use the native Ingress annotations:
We can redefine default-backend for each Ingress with annotation;
We can redefine custom-http-errors for each Ingress with annotation.
As a result, the Ingress resource will look something like this:
In this case, 404 and 502 errors will be redirected to the error-pages service with all the necessary headers.
В previous versions of Ingress did not have this feature. (fateful commit in 0.23). And if you have 2 completely different applications running in your cluster and you want to specify different default-backend-services and handling different error codes for each of them, you will have to use workarounds for this, of which we have two.
Ingress < 0.23: approach one
This option is simpler. As an application that renders its pages, we have plain HTML, which does not know how to look at headers and return correct response codes. Such an application rolls out with Ingress with url /error-pages, and in the directory ws will be given HTML.
The service for this deployment must be of type ClusterIP.
At the same time, in the application where we will handle the error, in Ingress we add server-snippet or configuration-snippet with the following content:
A variant for an application that can process headers ... And in general, this is a more correct way, borrowed from custom-http-errors. Using it manually (copying) will allow you not to change the global settings.
The steps are as follows. We create same deployment with an application that can listen to the right headers and respond correctly. We add server-snippet applications to the Ingress with the following content:
As you can see, for each error that we want to process, we need to make our own location, where all the necessary headers will be substituted, as in "native" custom error pages. This way we can create different personalized error pages even for individual locations and servers.