Creating a 503 error page server with Nginx


I needed to serve a custom 503 page for every request to any URL for a domain, and since we serve things using Traefik, I needed a backend service which served the same 503 image for any request.

The vhost configuration for Nginx ended up being like this -

server {
    listen       80;
    server_name  localhost;

    #access_log  /var/log/nginx/host.access.log  main;

    root   /usr/share/nginx/html;

    # Convert all error codes to 503 and specify file
    error_page 404 500 502 503 504 =503 /index.html;

    # Add the Retry-After header
    add_header Retry-After "86400" always;

    # Don't allow external index.html access (otherwise it would give a 200 for index.html)
    location = /index.html {
        internal;
    }

    location / {
        try_files $uri =503;
    }
}

Of note is the error_page directive on line 10, which throws a 503 for any other kind of error. On line 16, we also signify requests to /index.html as internal, so that Nginx doesn’t return a 200 for it. On line 21, we have a try_files directive which tries to serve the file if it exists, otherwise returns a 503. I’m guessing that a try_files =503 might also work, and might be a bit cleaner, but maybe it’s good to keep it on the if we ever need to actually serve anything.

If it isn’t evident enough, the index.html file is the custom 503 page we are serving for all requests.

The docker-compose.yml for the Nginx container ended up looking like this, we use the Docker provider and store routing config in the labels -

  notmy-503:
    image: nginx:1.29.4
    volumes:
      - ./notmy-503/vhost.conf:/etc/nginx/conf.d/default.conf
      - ./notmy-503/webroot:/usr/share/nginx/html
    restart: unless-stopped
    labels:
      - "traefik.enable=true"
      - "traefik.http.services.notmy-503.loadbalancer.server.port=80"

And my Traefik dynamic configuration had the router defined like so -

    ### Notmy
    notmy-503-redirect-router:
      entryPoints:
        - https
      rule: "Host(`notmy.org`, `www.notmy.org`)"
      service: ndltd-503@docker
      tls:
        certResolver: letsencrypt-http