How to prevent direct access to a php page but can be accessed when redirected

Viewed 651

I want to prevent direct access to my error page which is 404.php but I want that this page can be accessed when redirected from the other page.

Like my website name is example.com/index.php and if any user mistyped in any way like example.com/ind.php it will be redirected to my 404.php to do that in my .htaccess file I have used.

Also, suppose in my index.php file I am showing some products when clicked on any product it is taking me to product.php?pid=4 where pid contains the id, now I have put a condition that if id!=4 or it does not exist then redirect it to 404.php.

Everything up to this working very fine but if I access example.com/404.php it is showing me the page, I just want that the user when will direct use example.com/404.php it will redirect to index.php file. That's it.

# enable url rewriting
RewriteEngine On 
ErrorDocument 404 http://localhost/news_21_off/404.php

Index.php

$sql = "select * from products";
$res = mysqli_query($co,$sql);
$rs = mysqli_fetch_assoc($res);

$res_id = $rs['id'];

<a href="products.php?pid=<?php echo $res_id; ?>">Product</a>

Products.php


if(isset($_GET['pid']) && $_GET['pid']=='4'){
echo 'correct';
}else{

header('location:404.php');
}

404.php

<h1>My 404 page</h1>

So far I have found no way of doing it.

1 Answers

Everything up to this working very fine...

Actually, I don't think it is...

...prevent direct access to a php page but can be accessed when redirected

This isn't just any PHP page. It is your 404 "Not Found" error document.

You should not be externally 3xx redirecting to your error document in the first place. The error document should be served in the same request (an "internal subrequest"). This is the root cause of your problem.

The redirected request is an entirely separate request, mostly indistinguishable from a "direct request", which will naturally make reliably blocking direct requests "impossible". However, do you really need to block "direct requests" to your 404 error document? (See below...)

ErrorDocument 404 http://localhost/news_21_off/404.php

By specifying an absolute URL (ie. with a scheme + hostname) in the ErrorDocument directive, Apache will trigger a 302 external redirect to the stated URL. The user sees a 302 response (not a 404), followed by a 200 "OK" (not a 404) from the redirected request, unless you manually send the 404 "Not Found" status in your PHP code (there is no evidence of that here).

Just because Apache allows you to "redirect" to the error document, doesn't mean you should. As noted in the Apache docs:

Note that when you specify an ErrorDocument that points to a remote URL (ie. anything with a method such as http in front of it), Apache HTTP Server will send a redirect to the client to tell it where to find the document, even if the document ends up being on the same server. This has several implications, the most important being that the client will not receive the original error status code, but instead will receive a redirect status code. This in turn can confuse web robots and other clients which try to determine if a URL is valid using the status code.

In addition, all the helpful PHP superglobals (passed from Apache) that ordinarily relate to the URL that triggered the 404 are lost (eg. $_SERVER['REDIRECT_STATUS'], $_SERVER['REDIRECT_URL'], etc.) because, as mentioned above, the redirected request is an entirely separate request.

99.99% of the time you should be using a root-relative URL-path in the ErrorDocument directive, so that Apache triggers an internal subrequest for the error document, for example:

ErrorDocument 404 /news_21_off/404.php
header('location:404.php');

Likewise, you should not be "redirecting" to your error document in your PHP code either.

In PHP, there are various ways to resolve this. You could simply include() the error document at the point in your code you wish to serve the 404.

For example, in your products.php script:

<?php
$pid = $_GET['pid'] ?? null;
if ($pid == '4') {
    echo 'correct';
} else {
    // 404 Not Found
    include($_SERVER['DOCUMENT_ROOT'].'/news_21_off/404.php');
    exit();
}

However, the above won't send a 404 HTTP status code to the client. In your PHP 404 error document you should set the HTTP status to ensure that a 404 status is sent by PHP and when/if the document is accessed directly by the user.

For example, in your 404.php script:

<?php
http_response_code(404);
?>
<h1>My 404 page</h1>

Incidentally, setting the HTTP status code in your PHP script will override any status that Apache might have set when serving an error document.

Now, back to your initial question...

I want to prevent direct access to my error page which is 404.php

Why?

Having implemented the changes above, if a user does "directly access" your 404 error document they are simply served a 404 and see the same response as if they had requested any other document that didn't exist.

So, unless you have a specific requirement, there is nothing more you need to do here.

Related