nginx: error_page directive is silently ignored

I'm trying to return a custom 400 error page for non-HTTPS requests in nginx. I'm new to nginx, but I've read many pages of examples on custom error pages and nothing seems to work for me. My config:

server {
    listen 80;

    error_page 400 /400.html;
    location = /400.html {
        root /var/www/html;
    }

    return 400;
}

The return is working, because if I change the code (e.g. to 401 or 500) I get the appropriate default nginx error page. I know that the file exists, because if I take out the return it will serve my 400.html page directly with no problems. But nothing I've tried has resulted in my custom page showing up when I return the error code.

What am I missing?

Update: Other variants I've tried to no avail:

server {
    listen 80;

    error_page 400 /400.html;
    location = /400.html {
        root /var/www/html;
    }

    return 400;
}

server {
    listen 80;

    root /var/www/html;

    error_page 400 /400.html;
    location = /400.html {
        allow all;
    }

    return 400;
}

server {
    listen 80;

    root /var/www/html;
    error_page 400 /400.html;

    return 400;
}

Solution 1:

I found this question on Stackoverflow that accomplishes what I was looking for.

I guess for whatever reason even though a return works inside of a server block, it does not respect any error_page directives. This may be a bug, or an intentional behavior I don't understand. The solution is to put the return inside of a location block:

server {
    listen 80;

    root /var/www/html;

    error_page 400 /400.html;
    location = /400.html {
        internal;
    }

    location / {
        return 400;
    }
}