4

I would like to make a php script output like a real 404 page (as set in the Apache ErrorDocument directive) if certain conditions are not met. I'm not sure how I can / if it's possible to access this value from PHP..

if(!@$_SESSION['value']){
 header($_SERVER["SERVER_PROTOCOL"]." 404 Not Found");
 echo $default_page['404'];
 exit();
}
echo 'Welcome to a secret place.';

I understand the ErrorDocument value can be overwritten, but I'm particularly interested in the 'default' value hardcoded by Apache. If it's possible to know the value which is overwitten (eg by a .htaccess file), then that's a bonus :)

http://httpd.apache.org/docs/2.0/mod/core.html#ErrorDocument

edit: to be clear, I'd like to send the content the default 404 page (or 403, etc) of Apache from PHP. If I only use header on its own then nothing is output to the client/user (at least in FF/Chrome, IE has its own built in pages which are shown).

aland
  • 1,824
  • 2
  • 26
  • 43
  • I think the default page shows up when there is no `ErrorDocument` set at all. Not sure whether this is possible – Pekka Jan 31 '11 at 21:40
  • Many thanks to both mario and RoUS, both answers are correct. A HTTP library is necessary.. which is a shame because I'll never get these libaries installed on the 'budget' hosting I'm using for my current hosting, oh well - a fun experiment. – aland Feb 08 '11 at 00:28
  • 1
    You might want to double-check; `curl` may already be built into the version of PHP your host is using. Try creating a file `phpinfo.php` containing just ``, then navigate to that page and see if Curl is listed. – RoUS Feb 20 '11 at 21:08

3 Answers3

6

The recommended way to set the response code from PHP is as @mario suggested:

Header('Status: 404 Not Found');

If you want to get the body of the page the server would ordinarily provide for a 404, and don't care about the URL getting rewritten in the user's browser, you could use something like:

$uri_404 = 'http://'
    . $_SERVER['HTTP_HOST']
    . ($_SERVER['HTTP_PORT'] ? (':' . $_SERVER['HTTP_PORT']) : '')
    . '/was-nowhere-to-be-seen';
Header("Location: $uri");

(When you're directly frobbing the Location header field, you need to supply a full URI.) The result of this is that the user's browser's will end up pointing to that bogus location, which may not be what you want. (Probably isn't.) So then you could actuall collect the contents of the page yourself and combine the two, in effect:

Header('Status: 404 Not Found');

$uri_404 = 'http://'
    . $_SERVER['HTTP_HOST']
    . ($_SERVER['HTTP_PORT'] ? (':' . $_SERVER['HTTP_PORT']) : '')
    . '/was-nowhere-to-be-seen';
$curl_req = curl_init($uri);
curl_setopt($curl_req, CURLOPT_MUTE, true);
$body = curl_exec($curl_req);
print $body;
curl_close($curl_req);

That should fetch the contents of the 404 page the server reports for the bogus URI, and you can then copy it and use it yourself. This should properly handle any ErrorDocument 404 handler output.

DaveRandom
  • 87,921
  • 11
  • 154
  • 174
RoUS
  • 1,888
  • 2
  • 14
  • 29
  • Thanks, this also works.. I only had to change the CURLOPT_MUTE const to CURLOPT_RETURNTRANSFER – aland Feb 08 '11 at 00:19
3

There is no default method for that. But you can always cheat:

header("Status: 404 Not There");

// no worky
#readfile("http://$_SERVER[HTTP_HOST]/non-existant-url");

// doesn't work either (Apache detects the fraud)
#virtual("/non-existant-url");

// otherwise a HTTP class, this one works but is seldomly available
$request = new HttpRequest("http://localhost/error", 1);
$answer = $request->send();
print $answer->getBody();

This triggers a subrequest. Since it's on the local server, I would disregard any bigger performance penalties. But it might be worth to investiage using the Apache/mod_php virtual() function.

Notably this method depends on knowing a certain location of a 404 resource. If there is already url rewriting in place, then another trick is necessary in your .htaccess:

RewriteRule  ^non-existant-url$  error  [E=404,L]

This forces the resource to generate the expected result, thus making the default errordocument available.

mario
  • 144,265
  • 20
  • 237
  • 291
  • I think this is the only way. – Pekka Feb 05 '11 at 23:58
  • This looks promising, although I couldn't get it to work straight away. Will try again in the morning, thanks :) – aland Feb 06 '11 at 01:12
  • I still don't get much result from this, there no output – aland Feb 06 '11 at 17:14
  • @aland: Obvious oversight. PHPs readfile() and stream wrappers return no content if they encounter a HTTP error. So you would need to use a HTTP library for that. Or is `virtual()` available on your server? – mario Feb 06 '11 at 17:17
  • I'm not getting much result with readfile, still nothing is being output - other than a PHP warning. Warning: readfile(http://192.168.2.155/no-exist): failed to open stream: HTTP request failed! HTTP/1.1 404 Not Found in /var/www/test/tests/errortest.php on line 3 edit:should have refreshed the comments haha.. I'll look into virtual().. – aland Feb 06 '11 at 17:34
  • Same basic problem when using virtual(), but I'm not familar with this function so perhaps I'm doing it wrong.. Warning: virtual(): Unable to include 'http://192.168.2.155/no-exist' - error finding URI in /var/www/test/tests/errortest.php on line 6 – aland Feb 06 '11 at 18:05
  • 1
    @aland: No, then that's actually a concludent result. Then as last resort, you have to use one of the HTTP libraries ([PEAR](http://pear.php.net/package/HTTP_Request/) or [Zend](http://framework.zend.com/manual/en/zend.http.html) for example), maybe even curl. I've tried PHPs HttpRequest, which works well for that task, but it's not available on most servers. – mario Feb 06 '11 at 18:23
0

Your example seems good. You need to use the php header function to do that:

<?php
header("HTTP/1.0 404 Not Found");
?>

Like that you're saying that this page is a 404, and you can then fill in the contents with what you want

A real 404 is just any piece of code that send's a 404 error code back to the browser, so ErrorDocument or header(..404...) are equivalent in this regard.