
In this article I want to tell you about the two possibilities of NGINX Ingress, associated with the display of personalized pages with errors, as well as the limitations in them and ways to circumvent them.
1. Change the default backend
By default, NGINX Ingress uses the default backend, which performs the corresponding function. This means that when requesting Ingress with the indication of a host that is not in the Ingress resources, we get the following page with the response code 404:
')

However, more and more often our clients come with a request instead of the standard 404 to show their page with a company logo and other amenities. For this, NGINX Ingress has a
built-in ability to override the
default-backend-service
. We pass the
namespace/servicename
format entry as the argument of the same name option. The service port should be 80.
To do this, you need to create your own pod (deployment) and 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>
Thus, all domains that are not explicitly created through YAML with
kind: Ingress
fall into default-backend. In the listing above,
sadsdasdas
become such a domain.
2. Processing HTTP errors in the application by default backend
Another situation is when HTTP errors end (404, 500, 502 ...) with requests to an application that does not handle such situations (the corresponding beautiful pages are not generated). This may also be caused by the desire of developers to give the same error pages in many applications.
To implement this case on the server side, we need:
- Follow the instructions above from the item about the default backend;
- In the nginx-ingress configuration ConfigMap add the
custom-http-errors
key, for example, with the value 404,503
(obviously, it corresponds to the error codes to which the new rule applies).
The expected result is achieved: when the client application is running and receiving an error with a response code of 404 or 503, the request will be automatically redirected to the new default backend ...
However, when developing an application for default backend and custom-http-errors you need to take into account an important feature:
!!! 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, there will be useful information in the headers with the previous response code and additional information (the full list is available
here ).
This means that you yourself must
take care of the correct response code .
Here is an example from the documentation how it works.
Different applications - different default backend
In order for the solution not to be global for the entire cluster, but only for specific applications, first you need to check the Ingress version. If it is
0.23 or higher , use the Ingress “native” annotations:
- We can override the
default-backend
for each Ingress using the annotation ; - We can override
custom-http-errors
for each Ingress using the annotation .
As a result, the Ingress resource will look something like this:
apiVersion: extensions/v1beta1 kind: Ingress metadata: name: {{ .Chart.Name }}-app2 annotations: kubernetes.io/ingress.class: "nginx" nginx.ingress.kubernetes.io/custom-http-errors: "404,502" nginx.ingress.kubernetes.io/default-backend: error-pages spec: tls: - hosts: - app2.example.com secretName: wildcard-tls rules: - host: app2.example.com http: paths: - path: / backend: serviceName: {{ .Chart.Name }}-app2 servicePort: 80
In this case, errors 404 and 502 will be redirected to the error-pages service with all the necessary headers.
In
previous versions of Ingress, there was no such possibility (the
fateful commit in 0.23 ). And if you have 2 completely different applications in your cluster and you want to specify different default-backend-service and handling different error codes for each of them - you will have to use workarounds, which we have two.
Ingress <0.23: first approach
This option is more simple. As an application that gives its pages, we have plain HTML, which is not able to look at the headers and give the correct response codes. Such an application rolls out with Ingress with url
/error-pages
, and in the
ws
directory there will be HTML returned.
Illustration in YAML:
apiVersion: extensions/v1beta1 kind: Ingress metadata: name: {{ .Chart.Name }}-app2 annotations: kubernetes.io/ingress.class: "nginx" ingress.kubernetes.io/server-snippet: | proxy_intercept_errors on; error_page 500 501 502 503 504 @error_pages; location @error_pages { rewrite ^ /error-pages/other/index.html break; proxy_pass http://error-pages.prod.svc.cluster.local; } spec: tls: - hosts: - app2.example.com secretName: wildcard-tls rules: - host: app2.example.com http: paths: - path: / backend: serviceName: {{ .Chart.Name }}-app2 servicePort: 80
The service for this deployment should 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 contents:
nginx.ingress.kubernetes.io /server-snippet: | proxy_intercept_errors on; error_page 500 501 502 503 504 @error_pages; location @error_pages { rewrite ^ /error-pages/ws/index.html break; proxy_pass http://error-pages.prod.svc.cluster.local; }
Ingress <0.23: second approach
An option for an application that can handle headers ... Anyway, this is a more correct path, borrowed from custom-http-errors. Using it manually (copying) will not change the global settings.
The steps are as follows. We create
the same deployment with an application that knows how to listen to the right headers and respond correctly. Add server-snippet applications with the following contents to Ingress:
nginx.ingress.kubernetes.io /server-snippet: | proxy_intercept_errors off; error_page 404 = @custom_404; error_page 503 = @custom_503; location @custom_404 { internal; proxy_intercept_errors off; proxy_set_header X-Code 404; proxy_set_header X-Format $http_accept; proxy_set_header X-Original-URI $request_uri; proxy_set_header X-Namespace $namespace; proxy_set_header X-Ingress-Name $ingress_name; proxy_set_header X-Service-Name $service_name; proxy_set_header X-Service-Port $service_port; proxy_set_header Host $best_http_host; rewrite ^ /error-pages/ws/index.html break; proxy_pass http://error-pages.prod.svc.cluster.local; } location @custom_503 { internal; proxy_intercept_errors off; proxy_set_header X-Code 503; proxy_set_header X-Format $http_accept; proxy_set_header X-Original-URI $request_uri; proxy_set_header X-Namespace $namespace; proxy_set_header X-Ingress-Name $ingress_name; proxy_set_header X-Service-Name $service_name; proxy_set_header X-Service-Port $service_port; proxy_set_header Host $best_http_host; rewrite ^ /error-pages/ws/index.html break; proxy_pass http://error-pages.prod.svc.cluster.local; }
As you can see, for each error that we want to handle, you need to make your location, where all the necessary headers will be inserted, as in the “native”
custom-error-pages . So we can create different personalized pages with errors, even for individual locations and servers.
PS
Other K8s series tips & tricks:
Read also in our blog: