What just happened
You got this warning โ and probably a blank page or broken app โ because PHP couldn't find the file you told it to include:
Warning: include(config.php): Failed to open stream: No such file or directory in /var/www/html/index.php on line 3
PHP couldn't resolve config.php to any real file on disk. Nine times out of ten, one of three things is wrong: the file doesn't exist at that path, the current working directory isn't what you assumed, or the path itself has a typo or is just incorrect.
Debug process
Step 1: Figure out what path PHP is actually looking for
The warning only shows the argument you passed โ not the full resolved path PHP tried to open. Add a quick debug line above the include to see what's really happening:
<?php
echo __DIR__ . '/' . 'config.php' . PHP_EOL;
include('config.php');
Or use realpath() to check whether the file exists at all:
<?php
$path = __DIR__ . '/config.php';
var_dump(file_exists($path), $path);
Run this, look at the output, then compare it against what's actually on disk.
Step 2: Check the current working directory
A relative path like include('config.php') resolves against the current working directory (CWD), not relative to the file containing the include. These are often different. This catches people out when calling a script from another directory, using a framework front controller, or running via CLI.
<?php
echo getcwd(); // Print the current working directory
Expected /var/www/html but got /var/www? There's your problem.
Step 3: Verify the file actually exists
# On Linux/macOS
ls -la /var/www/html/config.php
# Hunt for typos โ Linux is case-sensitive
find /var/www/html -name "config*"
On Linux, Config.php and config.php are two different files. Windows is case-insensitive, so a typo that works fine on XAMPP will blow up the moment you push to a Linux server.
Solutions
Fix 1: Use DIR for reliable relative paths (recommended)
This fixes most cases. __DIR__ is a magic constant that resolves to the directory of the current file โ not wherever PHP happened to be invoked from.
<?php
// BEFORE (fragile โ breaks when CWD changes)
include('config.php');
include('../lib/helpers.php');
// AFTER (reliable โ always relative to this file)
include(__DIR__ . '/config.php');
include(__DIR__ . '/../lib/helpers.php');
Switch every bare relative include/require in your project to use __DIR__. That single change eliminates the vast majority of include path bugs.
Fix 2: Use require instead of include for critical files
When config.php is essential โ database credentials, app settings, anything your app can't survive without โ use require or require_once instead of include.
The key difference: include emits a Warning and keeps running. require throws a Fatal Error and stops immediately. Letting PHP continue silently with a missing config file almost always produces a second, harder-to-trace error 20 lines later.
<?php
// Stops execution immediately if config.php is missing
require __DIR__ . '/config.php';
Fix 3: Check and fix the actual file path
CWD is correct, but the file lives somewhere else? Fix the path:
<?php
// File is at: /var/www/html/includes/config.php
// Your script is at: /var/www/html/index.php
// Wrong
include('config.php');
// Right
include __DIR__ . '/includes/config.php';
Fix 4: The file genuinely doesn't exist โ create it
Sometimes the simplest explanation is the right one. Fresh checkouts often exclude config.php via .gitignore. Look for a template:
# Check for an example config
ls /var/www/html/config.php.example
ls /var/www/html/config.example.php
# Copy and fill in your values
cp config.php.example config.php
nano config.php
Fix 5: File permissions (less common, but real)
The file exists but PHP can't open it:
# Check who owns it and what the permissions are
ls -la /var/www/html/config.php
# PHP typically runs as www-data โ give it read access
chown www-data:www-data /var/www/html/config.php
chmod 644 /var/www/html/config.php
Here's the sneaky part: PHP reports "No such file or directory" for both missing files and permission-denied errors. If the file definitely exists but the warning persists, permissions are the next thing to check.
Fix 6: include_path issues
Some older codebases rely on PHP's include_path setting (configured in php.ini or via set_include_path()). To check what's currently configured:
<?php
// See current include_path
echo get_include_path();
// Add your directory
set_include_path(get_include_path() . PATH_SEPARATOR . '/var/www/html/includes');
include 'config.php'; // PHP now searches includes/ as well
That said, don't lean on include_path for your own application files โ it's brittle. Use explicit __DIR__ paths and reserve include_path for library or autoload setups.
Verification
After applying a fix, confirm it actually worked before removing debug code:
<?php
$file = __DIR__ . '/config.php';
if (!file_exists($file)) {
die('File not found: ' . $file);
}
if (!is_readable($file)) {
die('File not readable: ' . $file);
}
require $file;
echo 'Config loaded OK';
Once it prints "Config loaded OK", strip out the debug lines. Then check the PHP error log to confirm the warning is gone:
# Apache
tail -f /var/log/apache2/error.log
# Nginx + PHP-FPM
tail -f /var/log/php8.x-fpm.log
# Or wherever your php.ini points error_log
Quick reference: include vs require
includeโ Warning on failure, execution continuesrequireโ Fatal Error on failure, execution stopsinclude_once/require_onceโ Same behavior, but skips if the file was already included
For config files, database connections, or anything load-bearing: always use require. Reserve include for optional template fragments.
Lessons learned
The underlying confusion is almost always the same: where PHP was invoked from is not the same as where the file containing the include lives. Use __DIR__. Every time. No exceptions.
Write require __DIR__ . '/something.php' instead of require 'something.php' and you'll never see this warning again.
Inheriting a codebase full of bare relative includes? This grep will show you the full scope of the cleanup:
grep -rn "include\|require" /var/www/html --include="*.php" | grep -v "__DIR__" | grep -v "vendor/"
Every line in that output is a potential path bug waiting to happen.

