.NET Core, Linux, Nginx and app_offline.htm

This is continuation of Running .NET Core app on AWS Lightsail Linux instance.

One of the useful features I always use for .NET and .NET Core application in IIS is special file app_offline.htm. If this found exist in application’s directory, then .NET module for IIS will return contents of that file for every request. Usually, it used for maintenance. For example, I put some text that web site is under maintenance and that it will be finished in few minutes. Then I am updating all necessary files that are impossible to update while web site is running. When I finished, I will rename it to app_offline.hml_ until next maintenance.

I found like to implement something like that, and I found that for Linux it done in similar way, but there is no standard file name for this. As result I decided to use what is familiar to me because there is no standard anyway. It is quite simple to implement. Open file /etc/nginx/sites-available/www.example.com and change it contents to this:

server {
    server_name   example.com *.example.com;
    root          /var/www/www.example.com;
    if ($host = example.com) {
        return 301 https://www.$host$request_uri;
    location / {
        if (-f $document_root/app_offline.htm) {
            error_page 503 @offline;
            return 503;
        proxy_pass         http://localhost:5000;
        proxy_http_version 1.1;
        proxy_set_header   Upgrade $http_upgrade;
        proxy_set_header   Connection keep-alive;
        proxy_set_header   Host $host;
        proxy_cache_bypass $http_upgrade;
        proxy_set_header   X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header   X-Forwarded-Proto $scheme;
    location @offline {
        try_files /app_offline.htm =503;

    listen 443 ssl; # managed by Certbot
    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem; # managed by Certbot
    include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
}server {
    if ($host = www.example.com) {
        return 301 https://$host$request_uri;
    } # managed by Certbot

    if ($host = example.com) {
        return 301 https://$host$request_uri;
    } # managed by Certbot

    listen        80;
    server_name   example.com *.example.com;
    return 404; # managed by Certbot

Update from 5 Apr 2024:This will work only for IPv4. If you want to support IPv6 as well you need to copy/paste listen lines and [::]: before port. For example "listen [::]:80" but without quotes.

As you can see, I added root /var/www/www.example.com to let know nginx where to find files. if (-f $document_root/app_offline.htm) checks if file exists in root of that server and if it does define location for 503 error page and return error code 503. location @offline sets named location and will try to find file /app_offline.htm and return it contents or return 503 if file is not found. Quite simple.

Then I copy my typical app_offline.htm to application root. But there was only one problem with that solution: it does not work. But everyone suggested pretty much exactly the same solution. And people suggesting this solution for many years and it does not look like it was changed just recently.

And after short brainstorming I got it. As you remember that permissions for /var/www/www.example.com only allows to read and execute for www.example.com user but nginix runs from www-data user. That user has no access to any file in that directory including app_offline.htm. It is simple to fix and just give read permissions to this file for others. Just run this command:

sudo chmod 504 /var/www/www.doceo.club/app_offline.htm

But it still did not work. Then I tried to execute this command to see content of that file:

cat /var/www/www.doceo.club/app_offline.htm

And I got this error:

cat: /var/www/www.doceo.club/app_offline.htm: Permission denied

And I understand that to check if file exists, directory that contains that file needs read permission. So, I added this command:

sudo chmod 504 /var/www/www.doceo.club

But I still cannot read file. After some investigation I understand that that directory also needs execute permission: “For directories, execute permission allows you to enter the directory (i.e., cd into it), and to access any of its files”. And this command solved issue:

sudo chmod 505 /var/www/www.doceo.club

And after that I can read this file and nginx is working correctly.

I use command to put web site under maintenance:

sudo mv /var/www/www.example.com/app_offline.htm_ /var/www/ www.example.com /app_offline.htm

and this command when I finished:

sudo mv /var/www/www.example.com/app_offline.htm /var/www/ www.example.com /app_offline.htm_

And everything works exactly the same as it used under IIS.

I hope it helps someone.