PHP · PRACTICAL GUIDE

Common PHP File-Path Problems and How to Fix Them

Understand why a file loads from one page but fails from another, then make the path rules explicit.

A developer follows connected routes between a browser, golden folders at different depths and a server.
Conceptual illustration accompanying this article.

A PHP include that works on the homepage can fail when copied into an article three directories deeper. A text file can exist on the server while a script reports that it cannot be opened. These problems often look mysterious because the code contains something that resembles the right address, but the address belongs to the wrong context.

The first useful question is whether the string describes a browser URL or a filename on the server. The second is which directory supplies the starting point. Once those two questions have clear answers, most ordinary path problems become much easier to reproduce and fix. This guide uses a directory-based PHP site with shared layout files and article folders.

A leading slash has different meanings

In an HTML link, /images/example.jpg refers to a path under the current website’s public origin. In a typical Unix filesystem operation, the same leading slash begins at the operating system’s filesystem root. PHP does not automatically interpret that slash as the website’s document root.

That difference explains why a browser can load an image while file_exists() says a similar-looking path is missing. One request is being resolved through the web server; the other is checking a local filename. Keep the public URL in one variable and the filesystem path in another instead of trying to use one string for both jobs.

$imageUrl = '/images/example.jpg';
$imageFile = $siteRoot . '/images/example.jpg';

if (is_file($imageFile)) {
    echo '<img src="' . htmlspecialchars($imageUrl, ENT_QUOTES, 'UTF-8')
        . '" width="600" height="400" alt="Example illustration">';
}

Here, $siteRoot must already contain a configured absolute filesystem path. It is not a value supplied by a visitor. The example also assumes the public image really has the stated dimensions; path correctness does not validate the content of a file.

Anchor a local include to the file containing it

A bare relative filename can depend on the current working directory and, for includes, PHP’s include-path behavior. That is fragile when entry points live at different depths or when a script is run from the command line. Anchoring a path to __DIR__ makes its base visible in the code.

require __DIR__ . '/body.php';

// In /programming/php/example/index.php:
$siteRoot = dirname(__DIR__, 3);
require $siteRoot . '/layout/render.php';

The first statement loads a file beside the current PHP file. The second example walks up three directory levels because the article folder is three levels below the site root. Count those levels for the actual file location rather than copying the number into every page. A file moved to another folder may need a different anchor even if its public title stays the same.

Inside an included component, __DIR__ refers to that component’s own directory. It does not change to the directory of whichever page included it. That makes it useful for a layout file loading its neighboring components, and it is a detail worth checking when you reorganize shared templates.

Treat the document root as deployment information

For a straightforward web request, $_SERVER['DOCUMENT_ROOT'] can be a convenient starting point. But it is supplied by the server environment, may not be present in a command-line run, and may not represent the application root in a subdirectory deployment. It should not silently become a universal definition of where all project files live.

A bootstrap file can establish a known application root once and make it available to other components. For a small hand-built site, explicit dirname() calls in the page entry files can also be perfectly manageable. The important property is consistency: one include should not assume a domain-root deployment while another assumes the application sits under a subfolder.

Check the file you intend to read

When reading data, distinguish a missing file from a directory, an unreadable file and a failed read. These cases suggest different fixes. A preliminary check makes the intent clear, but the actual read can still fail if the file changes or permissions differ by the time it happens. Handle the operation’s result as well.

$filename = __DIR__ . '/items.txt';
if (!is_file($filename) || !is_readable($filename)) {
    throw new RuntimeException('The item list is unavailable');
}
$contents = file_get_contents($filename);
if ($contents === false) {
    throw new RuntimeException('The item list could not be read');
}

During development, inspect the resolved filename through a private log or local terminal. On a public page, use an ordinary failure message rather than displaying full server paths to visitors. A path that works under your own login can still fail under the web server’s account, so compare the relevant ownership and permission settings.

Use realpath for existing paths, not imaginary ones

realpath() can resolve an existing filename to a canonical filesystem path, including dot segments and symbolic links. It can also return false. That makes it useful for diagnosis or a carefully designed containment check, but unsuitable as a magical fix for every string that fails to load.

A new file you intend to create may not exist yet, so resolving that complete filename with realpath() can fail even when its parent directory is valid. Resolve or configure the parent and then construct the destination according to your application’s rules. Also remember that resolving a path does not establish that a visitor is allowed to access it.

Avoid building include filenames from arbitrary requests

A route identifier and a local PHP filename should not be interchangeable. If a request chooses a view, map a small set of allowed identifiers to fixed local files. That prevents unexpected directory traversal and makes the permitted set reviewable. It also avoids turning a page parameter into a remote include address.

$views = [
    'overview' => __DIR__ . '/views/overview.php',
    'details' => __DIR__ . '/views/details.php',
];
$requested = $_GET['view'] ?? 'overview';
if (!is_string($requested) || !isset($views[$requested])) {
    http_response_code(404);
    exit('Page not found');
}
require $views[$requested];

This example deliberately accepts named views rather than paths. If your actual requirement is downloading user files or handling uploads, it needs a separate design with ownership and access rules. A string replacement that removes one spelling of dot-dot is not an equivalent substitute.

Make path checks part of the upload routine

Confirm that every referenced file was uploaded, including capitalization. A case-insensitive development machine can hide a mismatch that fails on a case-sensitive server. Verify the depth of new article folders, the location of shared layout files and any deployment-specific root settings. These are small checks that prevent an otherwise finished article from failing at its first include.

Continue with reading text files in PHP and relative versus absolute URLs. The PHP manual documents magic constants, realpath() and is_readable(). Use those references to confirm function behavior while keeping your application’s own path policy explicit.