1

I have Nginx working as reverse proxy and Django application behind it. Django app is connected to Nginx through UWSGI. Django app is deployed using Docker and gets down for a second or so during deployment. I would like to instruct Nginx to return custom 503 error page during that period.

Below is my Nginx config:

upstream server_django {
    server 0.0.0.0:8000;
}

server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    server_name server.com;

    location / {
        # required to serve static assets
        alias    /data/server.com/website_root/;
        try_files $uri @django;
    }

    location @django {
        uwsgi_pass  server_django;
        include     /etc/nginx/uwsgi_params;
    }
}

I have tried adding following config inside "server" block:

uwsgi_intercept_errors on;
error_page 502 503 504 =503 @django_is_down;
location @django_is_down {
    root /error_pages/server.com/;
    add_header Retry-After 10 always;
    index 503.html;
}

but it didn't help since Nginx stared serving default 404 page when backend is down.

How do I detect when Django is down in Nginx and serve custom error page when that happens?

Termos
  • 664
  • 1
  • 7
  • 31
  • Your `@django_is_down` location used an original request URI, and your 404 error is indicating it cannot find neither `$uri` nor `$uri/503.html`. You can try `location @django_is_down { root /error_pages/server.com; add_header Retry-After 10 always; rewrite ^ /503.html break; }` – Ivan Shatsky Feb 01 '22 at 07:58
  • Do you want Django to handle the error and serve the error page? Or serve static HTML on error? – Mtxz Feb 03 '22 at 14:07
  • @Mtxz I want Nginx to serve static predefined 503 error page when Django backend is down (it happens when it's being deployed) – Termos Feb 04 '22 at 07:26
  • Are you sure of the HTTP code sent back by Dango when deploying? – Mtxz Feb 06 '22 at 16:58
  • @Mtxz I am sure that Django doesn't reply with any HTTP code at all during ~3 sec interval. This means that Django is "down" and is not able to respond at all. I would like NGINX to detect such situation and respond with custom 503 error page – Termos Feb 07 '22 at 15:25
  • Could you try a syntax like this:https://stackoverflow.com/questions/7796237/custom-bad-gateway-page-with-nginx (Christoph answer) or this: https://stackoverflow.com/questions/50026821/nginx-how-to-redirect-on-504-gateway-timeout - with `error_page 500 502 503 504 /50x.html;`, and a specific `location =` value matching. Also, another try without the index but a real 50x.html in the location root. Also this https://trac.nginx.org/nginx/ticket/824 maybe – Mtxz Feb 07 '22 at 15:42
  • @Termos I checked the configuration I suggested in the first comment and can confirm that it is workable. If for some reason you can't manage to make it work for you, you could at least write some feedback in response? – Ivan Shatsky Feb 07 '22 at 19:35

1 Answers1

2

When you are using named location as an error handler, it uses an original request URI (whatever it is), so assuming you've got a request like http://your.domain/some/route/ it will use /some/route/ URI and would try to find an 503.html index file under the /error_pages/server.com//some/route directory. For /some/route request (without a trailing slash) it would try to find the /error_pages/server.com//some/route file and won't using whatever you specify as the index file at all. That means the only two requests which will be ended with the 503.html file being served are http://your.domain/ and http://your.domain/503.html, everything else will give you an HTTP 404 error. And if you don't get your custom 503.html even for those two, maybe the /error_pages isn't a top level directory and you mean something like /var/www/error_pages/server.com instead?

Back to the question. If you want to use a named location as an error handler, you should rewrite any original request URI to a new value:

location @django {
    error_page 502 503 504 =503 @django_is_down;
    uwsgi_pass  server_django;
    include     /etc/nginx/uwsgi_params;
}

location @django_is_down {
    root /error_pages/server.com;
    add_header Retry-After 10 always;
    rewrite ^ /503.html break;
}

Alternatively you can change your URI using error_page directive itself and an internal location:

location @django {
    error_page 502 503 504 =503 /503.html;
    uwsgi_pass  server_django;
    include     /etc/nginx/uwsgi_params;
}

location = /503.html {
    internal;
    root /error_pages/server.com;
    add_header Retry-After 10 always;
}

If you want to reserve /503.html URI for your django app, you can use whatever else you want, e.g.

location @django {
    error_page 502 503 504 =503 /django_is_down/503.html;
    uwsgi_pass  server_django;
    include     /etc/nginx/uwsgi_params;
}

location = /django_is_down/503.html {
    internal;
    root /error_pages/server.com;
    add_header Retry-After 10 always;
}

(but for that case you should place your 503.html file under the /error_pages/server.com/django_is_down directory).

Ivan Shatsky
  • 13,267
  • 2
  • 21
  • 37