Nginx does not drop connections on bad requests

My issue:
I have configured a default server for port 80 with simply a “return 444;” statement in it. I expected nginx to close all connections that are matched by this server, but it doesn’t. If the client sends a bad request, nginx will return the bad request page instead of dropping the connection.

How I encountered the problem:
I see in the logs that there are not many 444 responses returned, but mostly 400 responses. The correct server block seems to be matched as it logs to its own file.

Solutions I’ve tried:
I have the return statement directly in the server block and I’ve tried in a location / block, but it still does not trigger as expected.

My config:

http {
    include       mime.types;
    default_type  application/octet-stream;
    index         index.html index.htm;

    log_format  main  '$host $remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  logs/httpblock-default.log  main;

    sendfile           on;
    tcp_nopush         on;
    keepalive_timeout  65;
    server_tokens      off;

    server {
        listen       80 default_server;
        listen       [::]:80 default_server;
        server_name  _;
        access_log   logs/server80-default.log  main;

        return 444;
    }

    server {
        listen          80;
        listen          [::]:80;
        server_name     example.com www.example.com;
        access_log      logs/server80-domain.log  main;

        return 301 https://$host$request_uri;
    }
}

Log examples:

# Remove host: header from request
curl -v -H 'Host:' http://127.0.0.1
# Result:
_ 127.0.0.1 - - [16/Apr/2025:12:48:59 +0100] "GET / HTTP/1.1" 400 150 "-" "curl/8.10.1" "-"

# Set blank host: header on request
curl -v -H 'Host: ' http://127.0.0.1
# Result (user agent suddenly not logged):
_ 127.0.0.1 - - [16/Apr/2025:12:49:08 +0100] "GET / HTTP/1.1" 400 150 "-" "-" "-"

# Requesting a host that doesn't exist works as expected:
curl -v -H 'Host: wrong-host' http://127.0.0.1
# Result ok:
wrong-host 127.0.0.1 - - [16/Apr/2025:12:49:31 +0100] "GET / HTTP/1.1" 444 0 "-" "curl/8.10.1" "-"
1 Like

Can you try the example listed here:

server {
    listen      80;
    server_name "";
    return      444;
}

Thanks for the reply! I tried just now, and it did not seem to have any effect unfortunately.

I tried with this setup first:

    server {
        listen       80 default_server;
        listen       [::]:80 default_server;
        server_name  _;
        access_log   logs/server80-default.log  main;

        return 444;
    }

    server {
        listen      80;
        server_name "";
        access_log  logs/server80-nohost.log  main;
        return      444;
    }

curl -v -H 'Host:' http://127.0.0.1 was matched by second server block according to the logs but returned the 400 error like previously:
127.0.0.1 - - [28/Apr/2025:20:29:17 +0100] "GET / HTTP/1.1" 400 150 "-" "curl/8.10.1" "-"

Sending a blank host: curl -v -H 'Host: ' http://127.0.0.1 matched the default server block and also returned the 400 error:
_ 127.0.0.1 - - [28/Apr/2025:20:31:35 +0100] "GET / HTTP/1.1" 400 150 "-" "-" "-"

Removing the default server block, both requests are matched by the nohost server, but still returns 400 errors as before:

 127.0.0.1 - - [28/Apr/2025:20:35:14 +0100] "GET / HTTP/1.1" 400 150 "-" "curl/8.10.1" "-"
 127.0.0.1 - - [28/Apr/2025:20:35:32 +0100] "GET / HTTP/1.1" 400 150 "-" "-" "-"