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.phpWhat 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
- 400 Bad Request
- 401 Unauthorized
- 402 Payment Required
- 403 Forbidden
- 404 Not Found
- 405 Method Not Allowed
- 406 Not Acceptable
- 407 Proxy Authentication Required
- 408 Request Timeout
- 409 Conflict
- 410 Gone
- 411 Length Required
- 412 Precondition Failed
- 413 Content Too Large
- 414 URI Too Long
- 415 Unsupported Media Type
- 416 Range Not Satisfiable
- 417 Expectation Failed
- 418 (Unused)
- 419 Page Expired
- 421 Misdirected Request
- 422 Unprocessable Content
- 423 Locked
- 424 Failed Dependency
- 425 Too Early
- 426 Upgrade Required
- 428 Precondition Required
- 429 Too Many Requests
- 431 Request Header Fields Too Large
- 451 Unavailable For Legal Reasons
- 494 Request Header Too Large
- 495 SSL Certificate Error
- 496 SSL Certificate Required
- 497 HTTP Request Sent to HTTPS Port
- 499 Client Closed Request