Skip to content
Control Plane Labs

444 No Response

Client error response defined by nginx.org.

Last updated July 27, 2026

Common causes at a glance

  • An explicit return 444 in a location block dropping scanner traffic
  • A default server block silently discarding requests for unknown hostnames
  • A rate-limit or GeoIP rule mapping unwanted clients to 444
  • A WAF or fail2ban integration configured to drop rather than reject

Reproduce it

curl -sv https://www.example.com/wp-login.php

What 444 No Response means

Every other status code in this reference is something a client receives. 444 is the opposite. When a location or server block hits return 444, nginx drops the connection on the spot. The client sees a TCP close, not an HTTP response, which is why curl reports Empty reply from server and exits with code 52.

The point is to spend as little as possible on traffic you have already decided is worthless: scanner probes hunting for admin panels, requests for a hostname you do not serve. Returning 400 costs a response and confirms something is listening. Closing silently costs neither.

Where it comes from

444 is not registered with IANA and no client implements it. It is defined by nginx’s own return directive, which documents that the non-standard code 444 closes a connection without sending a response header. The core module’s reset_timedout_connection directive references it too, as a case it can close with a TCP reset rather than a graceful shutdown. Because nothing is transmitted, RFC 9110 has nothing to say about how a client should treat it. 444 appears only in your own access.log, so a dashboard counting it measures a decision you made, not an error your users hit.

Why you are seeing it

If 444 shows up in your logs, you or a previous operator put it there. The most common placement is the catch-all server block on default_server, soaking up requests whose Host header matches no real site — almost entirely bots scanning IP ranges. The second is a location matching nuisance paths such as /wp-login.php. Watch for over-broad matching: a 444 rule that accidentally covers a health check produces a connection failure with no diagnostic anywhere except the nginx log, and the team owning that client will be chasing network faults rather than your configuration.

How to debug it

You cannot observe 444 from the client, only the absence of a response. Confirm the shape of the failure first:

curl -sv https://www.example.com/wp-login.php ; echo "exit=$?"

Exit code 52 with Empty reply from server after a completed TLS handshake means the server accepted the connection and hung up, which is what 444 looks like. Connection refused or a TLS error means something else is wrong.

Server-side, filter the access log with awk '$9 == 444' and confirm the rule matches what you intended rather than something you still serve.

Working examples

curl

# 444 is invisible to the client. Watch for the empty reply and exit code 52.
curl -sv https://www.example.com/wp-login.php ; echo "exit=$?"

# Expected:
#   * Connected to www.example.com
#   * TLSv1.3 handshake completed
#   * Empty reply from server
#   exit=52

# The nginx side that produces it:
#   location = /wp-login.php { return 444; }

Python (requests)

import requests

# nginx sends nothing, so requests raises rather than returning a response.
try:
    resp = requests.get("https://www.example.com/wp-login.php", timeout=10)
    print(resp.status_code)
except requests.exceptions.ConnectionError as exc:
    # urllib3 surfaces this as RemoteDisconnected / ProtocolError.
    print("connection closed with no response:", exc)

# A control request to a path that is not dropped should return normally.
print(requests.get("https://www.example.com/", timeout=10).status_code)

Node (fetch)

// fetch() rejects with a TypeError; the cause carries the socket-level detail.
try {
  const res = await fetch("https://www.example.com/wp-login.php");
  console.log(res.status);
} catch (err) {
  console.log("no response:", err.cause?.code ?? err.message);
  // e.g. UND_ERR_SOCKET / ECONNRESET — never a status code
}

// Compare against a path the 444 rule does not match.
console.log((await fetch("https://www.example.com/")).status);

Reference and tooling

The authoritative list of assigned status codes is theIANA HTTP Status Code Registry. For a searchable copy with the semantics summarised, use theHTTP status code reference on this site. To see exactly what a server is sending, theheader inspector shows the raw status line and response headers, and thecurl builder assembles the request flags for you.

Other 4xx codes