26

I want to configure the server to show a maintenance page when it exist. I tried this code and works:

location / {
    try_files /maintenance.html $uri $uri/ @codeigniter;
}

But I noticed it would be served with a 200 status code, and it can cause confusion to search engines. I think the best practice would be returning a 503 status code. On google I find several relevant pages about it, like this. However, they use if to make the redirect and according to nginx documentation it isn't safe to use ifs.

Is there a way of doing it without using if? Is safe to use if in this case?

Thanks.

NeDark
  • 393
  • 1
  • 5
  • 10

4 Answers4

31

Here is what I do.

            if (-f $document_root/maintenance.html) {
                    return 503;
            }
            error_page 503 @maintenance;
            location @maintenance {
                    rewrite ^(.*)$ /maintenance.html break;
            }

If the file is there it will show the maintenance page. Once you remove the file you will go back to normal.

Mike
  • 21,910
  • 7
  • 55
  • 79
  • 2
    Yeah, that is the same code that is on the link in the question. I'm actually asking if it's safe to use `if`s in this case since it should not be used according to [documentation](http://wiki.nginx.org/IfIsEvil). – NeDark Sep 13 '11 at 19:43
  • 1
    And that same documentation: `In some cases it's also possible to move ifs to server level (where it's safe as only other rewrite module directives are allowed within it).` The maintenance error_page as Mike showed is normally set in server {} context. – Regan Nov 13 '13 at 13:18
  • 1
    I did the same except just did a 'return 503' without checking the existence of the file. That way I can just enable/disable the site (using Debian's "sites-available"/"sites-enabled" layout) by symlinking and turn on the maintenance page. – Asfand Qazi Jul 14 '15 at 11:18
  • 6
    Sounds like this would be a performance hit: NGINX will need to check for the existance of a file for every request... – Marc Sep 14 '17 at 07:32
  • 2
    Marc, it isn't since commonly accessed files are stored in filesystem cache which is in memory. – Mike Feb 19 '18 at 03:00
  • Every examples I've stumbled upon using the `if (-f $document_root/maintenance.html) {` claims this works. My `if (-f ...)` statement never seems to work even when the maintenance.html page exists. I've read about the "If is Evil" (https://www.nginx.com/resources/wiki/start/topics/depth/ifisevil/) article, but didn't shed any light as to why it doesn't work on my end. I tried removing the "503" error_page on the main `nginx.conf`, and still keep getting a `500 Internal Server Error`. Any ideas? – chamberlainpi Nov 25 '20 at 13:53
  • @Mike I am not convinced, but since this is a simple solution, I will use it. – Obay Abd-Algader Jul 18 '21 at 12:02
10

I think the best practice would be returning a 500 status code.

I think you mean 503 instead of 500.

they use if to make the redirect and according to nginx documentation it isn't safe to use ifs.

No. Only return is 100% safe inside if in location context.

According to nginx documentation, you can specify an HTTP status code as the last argument to try_files. I've tried this but it didn't work.

quanta
  • 50,327
  • 19
  • 152
  • 213
  • 1
    A possible problem with returning 503 is that you don't know if the shutdown was for maintenance purposes, or there is an issue with the web application. Am I right? – Obay Abd-Algader Jul 18 '21 at 12:04
7

Yes, it's important to use HTTP 503 for temp. redirects. That's the way I've solved it:

server {
        listen      80;
        server_name joergfelser.at;
        root    /var/www/joergfelser.at/;

        location / {
            if (-f $document_root/maintenance.html) {
                return 503;
           }
            ... # rest of your config, it's important to have 
            ... # the maintenance case at the very top
         }

        error_page 503 @maintenance;
        location @maintenance {
                rewrite ^(.*)$ /maintenance.html break;
        }
}

I've also written a blog post on that topic:
https://www.joergfelser.at/redirecting-to-a-custom-nginx-maintenance-page/

Happy maintenancing ;)

jo3rg
  • 71
  • 1
  • 1
  • Could you provide an example of what would go in the `# rest of your config, it's important to have`? I keep getting an error `500 Internal Server Error` and have no idea if it's something to do with what follows my `if (-f ...)` statement. It seems it never gets executed / true, even though the file exists. – chamberlainpi Nov 25 '20 at 13:56
1

If the maintenance page only has HTML

No CSS, JS, ...

    # use RegExp because it has higher priority
    # if you don't have other `location`, you can use `location /`
    location ~ ^ {
        root /usr/share/nginx/html/maintenance/;
        error_page 503 /503.html;

        # return all request with status 503
        # use the `if ($status != 503)` to check if it is internal redirect
        if ($status != 503) {
            return 503;
        }
    }

Without using if

    location @503 {
        root /usr/share/nginx/html/maintenance/;

        # `break` will stop internal redirect, just return the "/503.html"
        rewrite ^ "/503.html" break;
    }

    # use RegExp because it has higher priority
    location ~ ^ {
        error_page 503 @503;

        # return all request with status 503
        return 503;
    }

For maintenance page that has CSS / JS / ...

The flow of this is:

  • If URI is /, return status 503
  • If URI is a file (HTML, CSS, JS, ...) under root, return content with status 200
  • If not found, redirect to /
    location @index {
        # use redirect because if URI has sub-folder ($uri == /path/path/),
        # the relative path for static contents (CSS, JS, ...) will be incorrect
        rewrite ^ "/" redirect;
    }

    # use RegExp because it has higher priority
    location ~ / {
        root /usr/share/nginx/html/maintenance/;

        # 503 page URI
        error_page 503 /503.html;

        # if URI is index page, return 503
        if ($uri = "/") {
            return 503;
        }

        # redirect all URI to index (503) page (except static contents)
        try_files $uri @index;
    }

You can put the config in a maintenance.conf, include maintenance.conf when in maintenance.

  • I will not use if (-f <file>) because it will try to check if the file exist on every request

Ref: Nginx Maintenance

Steely Wing
  • 281
  • 2
  • 4