Safe to throw 404 error in request filter?

While I don’t see particular problem with this, you do kind of break out of code flow at early point.

I would try to go for setting query to is_404 instead and let it reach template processing and 404 in template hierarchy naturally.