Our 403s describe what permissions precisely the user is missing.
Our 400s describe what is malformed about the request.
A 404 would indicate 'not found' at the transport layer e.g. a bad proxy configuration or you didn't hit the right server at all. You definitely wouldn't confuse it with '${my_widget} not found'.
A couple of weeks ago there was an interesting post about this topic here in HN.
Why should '${my_widget} not found' be different?
So if you return 404 on this layer you are saying that whatever file or endpoint you searched is not there.
If you ask for a resource at an endpoint that does not exist e.g. via an parameter in the url and return 404 it is not clear if the endpoint does not exist e.g. the endpoint.php is missing or the resource you are looking for. Leading to questions like: is the endpoint down, is my internet wonky, did I misspell the name, and business logic related questions: is there no such widget, is the widget out of stock, should I call another endpoint?
404 does not indicate non existence but it indicates an inability to be found by the server. It's a nuance and it only matters when it does and then it bites...
Why should the error code for /product/foo/thumbnails/123.jpg be different if served with nginx serving a static file or an application server that dynamically generates it based on the product id?
But if the route is ID-agnostic, like `/cgi-bin/generate_thumbnails.pl` or `/api/json_rpc.php` then you could justify a 200 OK that the endpoint itself was found, regardless of the parameters. In this design the RPC is definitely above the HTTP level.