Apache 2.4.10 on Windows. I have a stable Zend Framework 1 PHP application. I just decided to add an ErrorDocument
directive to .htaccess
. I'm completely baffled by the three different behaviors I get with 404's, depending on how they're triggered.
My .htaccess file contains:
ErrorDocument 404 "Page not found"
RedirectMatch 404 /uploads/(.*/)?private/
If I simply type a url for a non-existent page in the address bar, I get the error page produced by Zend Framework's ErrorController
.
If I enter /uploads/private/foo
(matching the RedirectMatch
), then I get the ErrorDocument
output, as expected.
But if in my PHP code I do
http_response_code(404);
exit(0);
then I get the browser's standard error page. This is the one I totally fail to understand. If I use the Network tab in Chrome Developer tools, I can see that all three requests give a 404 status. I guess I can imagine that the Zend Framework's error handling setup would trap the non-existent url, and do its thing, bypassing the ErrorDocument
processing. But I don't understand how an immediate exit
from PHP code with a status of 404 would get past the ErrorDocument
.
What PHP code could I write to trigger the ErrorDocument
directive?
EDIT:
Based on the answer from Tim Fountain, I should provide more context: As noted above, my ZF ErrorController
page is set up to look the way I want - I see that page when I enter a URL for a non-existent page. In this particular situation, I'm actually running code from a WordPress mu-plugin
(WP is installed below the ZF public
directory). The .htaccess
file naturally does not direct requests for existing files below public
to ZF. Requests for anything below public/wp
instead go to public/wp/index.php
The mu-plugin
bootstraps ZF (but does not call the bootstrap's run()
method) and then examines the request. If it's not permitted (according to ZF's Auth session variables), I want to give a 404 (not 403). Previously, I did this by actually redirecting to a non-existent URL, and that showed the ErrorController
page just fine - but it also left the address bar displaying the non-existent URL. I decided to add the ErrorDocument
directive so that the address bar would still show the URL typed by the user. In my actual code, I did not have a simple string on the directive, but rather this URL: /default/error/error?error_handler[type]=404
. I changed it to a simple string just to make sure there wasn't something about the URL causing a problem. Note that when I enter a URL for something within a private
directory triggering the RedirectMatch
, I do see the ZF ErrorController
page, so I know that the URL in itself is not a problem. I just wanted to reduce the complexity for this posting.
Tim's answer says that my problem code is sending an empty 404 page directly to the browser, and the browser is changing it to explanatory text. That's clearly what's happening!
Does this mean my problem is that I need to produce some non-zero-length page content and use header()
to set the status, because http_response_code()
sends empty content directly to the browser? But what's unclear in his answer is why the ErrorDocument
directive would be bypassed.
The request URL causing this behavior is http://www.example.com/wp/foo
. The DocumentRoot
is .../public
. This is not rewritten by .htaccess
to go to ZF, but goes directly to public/wp/index.php
. In which case I would have thought that the ErrorDocument
would be triggered...
EDIT 2:
Or wait, maybe Tim's point isn't really about ZF Error handling, but that from Apache's point of view, the incoming request was satisfied (by going to public/wp/index.php
and running PHP), so it's up to the PHP code to handle it, I can't get back to Apache's ErrorDocument
after that! I guess I had a really fundamental misunderstanding of how ErrorDocument works. I need to figure out some way to invoke the ErrorController
code directly to display the page I want with a 404 status! Duh!
I guess I'll accept Tim's answer, though if he wants to clarify/generalize his point about when ErrorDocument
processing comes into play, that would be nice :-)
EDIT 3:
Just to wrap this up. I accepted Tim's answer because it revealed my fundamental misunderstanding of how the Apache ErrorDocument
directive works. But it didn't directly show me how to solve the problem I was experiencing, with discovering that I wanted to return a 404 status using my Zend Framework custom status page, but I was running in a WordPress query having bootstrapped ZF without having started the MVC application running under the bootstrap. I had no experience mucking around with ZFs front controller, but after digging into the documents a bit, I came up with this solution, which has the exact same effect as the full ErrorDocument
directive I had wanted to trip.
$error_url = $this->serverUrl() . '/default/error/error?error_handler[type]=404';
$request = new Zend_Controller_Request_Http($error_url);
$front = Zend_Controller_Front::getInstance();
$front->returnResponse(true);
$response = $front->dispatch($request);
$response->setRawHeader('HTTP/1.1 404 Not Found');
$response->sendResponse();
exit(0);