If the edge is where you save bandwidth, the server is where you decide what exists at all. This layer costs nothing, depends on nobody else, and is the one we would keep if we had to choose only one.
403 marks a refusal and 444 closes the connection, and that choice is part of the policy you publish.
What this layer is
- Refuse the paths that should never resolve: credential files, version-control directories, admin panels, framework endpoints.
- Remove or never install the default endpoints that scanners look for in the first place. A static site cannot leak a database configuration it does not have.
- Answer consistently: the same request should get the same treatment on every hostname and every protocol version.
What we run, and what it produced
Our own path rules refuse .env, .git, /wp-, /graphql, /actuator, /instance/config and their relatives; extensions we never serve are refused too. Measured on the day of writing: 40,263 refusals in a 24-hour window, 33,133 of them matching known scan paths.
Where we disagree with the textbook
The common hardening advice is return 444 — close the connection without a response, so scanners learn nothing and you spend no bytes. nginx supports it (nginx.org), and we understand the appeal. We kept 403 instead, for one reason that matters more to us: we publish our refusal behaviour. Our llms.txt, our JSON definitions and our test suite all state that scan paths are refused with HTTP 403. Switching to a dropped connection would make our own published definition false, and it would make the behaviour harder for anyone (including us) to verify from outside.
If your site does not publish such a contract, 444 is a reasonable choice. Ours does, so it is not.
How to verify
- Fetch a probe path yourself and read the status:
/.envand/graphqlanswer 403 on this host. - Compare the refusal counts against their published definitions in /data/live.json.
- Read the policy files themselves: robots.txt and llms.txt.
return and its non-standard 444 code (nginx.org); RFC 9309 for how robots.txt is interpreted by crawlers (rfc-editor.org).
Is 444 better than 403?
It is cheaper and reveals less, which is why people recommend it. It is also unobservable, which is a real cost if you publish what you do. Pick per your own contract.
Do I need a WAF if my server refuses these paths?
No, but the volumes differ: the server still sees the traffic and pays for the answer, while the edge does not.
What is the strongest hardening?
Having nothing to exploit. A static site with no admin panel, no database and no upload path removes whole categories of probing.
Related reading: Five Layers of Scanner Defence, Tested on Our Own Site · Layer 5: Closing the Loop in Public
More on this site: live signals · dated reports · how to verify us
