Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

iTechGuides is reader-supported. When you buy through links on our site, we may earn an affiliate commission. As an Amazon Associate I earn from qualifying purchases. Learn more

If an Apache site loads index.php but returns 404 or 403 for CSS, JavaScript, and images, PHP is usually not the root problem. The request is most often reaching the wrong virtual host, mapping to the wrong DocumentRoot, being denied by a <Directory> rule, or failing to resolve the directory index. Fix those layers first; then verify the PHP handler and, if applicable, the PHP-FPM filesystem path.

What “Apache only serves PHP files” actually means

Apache does not have a special mode in which PHP files work and static files are ignored. Each request is processed through several independent decisions:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Layer What Apache must decide Typical symptom when it is wrong
Virtual-host selection Which <VirtualHost> matches the request address, port, and Host header The edited configuration has no effect, or another site appears
Filesystem mapping Which file path results from the URL and the selected DocumentRoot PHP exists under one tree while CSS and images are looked for under another; static requests return 404
Directory-index resolution Which file is opened when the URL names a directory such as / /index.php works, but / shows a listing or a directory error
Static-file authorization Whether the requested directory and file are allowed by Apache and the operating system Images, stylesheets, or scripts return 403
PHP execution Whether a PHP request is handed to mod_php or PHP-FPM PHP downloads as text, returns 502/503, or fails after the handoff

A PHP-only proxy rule can be perfectly normal: a rule matching *.php deliberately sends only PHP requests to a PHP handler. CSS, JavaScript, fonts, and images should remain ordinary files served from the selected document root. If those files fail, broadening the PHP rule is not the fix.

Start with the three checks that locate most faults

1. Prove which virtual host received the request

Apache chooses a virtual host using the listening address and port, then the request’s Host header. Within a matching address and port, ServerName and ServerAlias determine the match. If none matches, the first listed virtual host for that address and port can act as the default.

  • Check that the browser hostname exactly matches ServerName or one of the ServerAlias values.
  • Check that the vhost listens on the port the client actually uses, such as *:80 for HTTP or *:443 for HTTPS.
  • Inspect Apache’s active vhost dump (commonly apachectl -S or the equivalent command supplied by your distribution) rather than relying only on the file you edited.
  • Test the same address with the intended Host header when DNS or a reverse proxy is involved. A request sent to an IP address can select a different vhost than a request sent to the site name.

If the wrong vhost is selected, its DocumentRoot, access rules, directory index, and PHP handler explain all of the symptoms. Correct the name, alias, address, port, or enabled-site order before changing application files.

2. Verify the selected DocumentRoot

After choosing a vhost, Apache maps a URL beneath that vhost’s DocumentRoot. For example, with DocumentRoot /srv/www/example/public, a request for /css/site.css maps to /srv/www/example/public/css/site.css. The public directory must contain the complete web tree, not only the PHP entry point.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
  • Confirm that the PHP entry file, stylesheets, JavaScript, images, fonts, and any upload or asset directories intended to be public are beneath the same root.
  • Check the path letter-for-letter. Linux filesystems are case-sensitive, so /Images/logo.png and /images/logo.png are different requests.
  • Look for a deployment mistake in which PHP files were copied to the configured root but the asset directory remained in a project, release, or old web-root directory.
  • Make sure a container, chroot, or mounted volume exposes the same directory to Apache that you inspected on the host.

3. Check the directory index and PHP handler separately

A request for / is a directory request. Apache’s mod_dir checks the resources named by DirectoryIndex. Its documented default is index.html, so an application whose entry point is index.php should set the PHP file explicitly.

PHP execution is a second decision. The server needs either an in-process mod_php handler or a PHP-FPM handoff through mod_proxy and mod_proxy_fcgi. A working DirectoryIndex does not prove that the PHP runtime is reachable, and a working PHP handler does not prove that static paths are correct.

Use a vhost whose root and access scope describe the same public tree

A minimal PHP-FPM-style layout looks like this:

<VirtualHost *:80>
    ServerName example.test
    ServerAlias www.example.test
    DocumentRoot /srv/www/example/public

    <Directory /srv/www/example/public>
        Require all granted
        DirectoryIndex index.php index.html
    </Directory>

    # Use the PHP-FPM socket or TCP endpoint supplied by your system.
    # The filesystem path must be the real path to the PHP files.
    ProxyPassMatch ^/(.*.php(/.*)?)$ fcgi://127.0.0.1:9000/srv/www/example/public/$1
</VirtualHost>

The example is a configuration shape, not a copy-and-paste endpoint for every operating system. Socket names, package names, enabled modules, and PHP versions differ. The important relationships are:

  • DocumentRoot points to the directory containing the entire public site.
  • The <Directory> block names that exact same directory and grants the access the site requires.
  • DirectoryIndex includes the application’s real entry file.
  • The PHP-FPM mapping handles PHP requests while leaving non-PHP files to normal filesystem mapping.
  • The path after the FastCGI host and port is a filesystem path visible to FPM, not a URL alias or a virtual-host name.

Keep the PHP handler in the vhost that owns the site. A rule in another vhost, an unmatched include, or a broad global rule can make troubleshooting misleading because PHP may execute through a configuration different from the one serving static files.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

How Apache resolves the two requests that expose this problem

Requesting /index.php

  1. Apache matches the address, port, and Host header to a vhost.
  2. It joins the URL path to that vhost’s DocumentRoot.
  3. Because the path ends in .php, a PHP handler may intercept it.
  4. With PHP-FPM, Apache passes the script’s real filesystem path to the FPM socket or TCP endpoint.
  5. FPM interprets the script and returns the generated response through Apache.

Requesting /

  1. The same vhost and document-root selection occurs.
  2. The resulting path is a directory, so mod_dir searches the configured DirectoryIndex list.
  3. If index.php is listed and exists, Apache internally resolves the request to that file and invokes the PHP handler.
  4. If no index resource exists and directory listing is enabled, a listing may appear. If listing is disabled, Apache returns a directory-forbidden response instead.

This explains why a direct PHP URL can work while the bare site URL does not: the first request tests PHP mapping, whereas the second also tests directory-index configuration.

A disciplined troubleshooting sequence

  1. Validate syntax before reloading. Run apachectl configtest, or the equivalent configuration-check command for your platform. Resolve every syntax error before attempting a reload.
  2. Dump the active vhosts. Use the platform’s vhost inspection command and confirm the hostname, address, port, enabled configuration file, and default ordering. Do not assume a file on disk is active.
  3. Compare the request hostname with the vhost names. Verify the browser’s hostname, the Host header used by any proxy, ServerName, and every ServerAlias.
  4. Inspect DocumentRoot. Confirm that the directory contains the exact CSS, JavaScript, image, and PHP paths requested by the page.
  5. Compare the <Directory> path. The authorization block should refer to the same public directory, or to a deliberate parent/child rule that you understand. Check that Require all granted or the site’s intended authorization is present.
  6. Check URL casing and generated paths. Use the browser’s network panel or access log to identify the exact failing URL. Correct references such as /css/app.css versus a file stored as CSS/app.css.
  7. Set the directory index. Add DirectoryIndex index.php index.html when the application uses index.php, and ensure that file exists in the directory named by DocumentRoot.
  8. Identify the PHP model. Determine whether the site uses mod_php or PHP-FPM. For FPM, verify that mod_proxy and mod_proxy_fcgi are available and that the configured socket or TCP endpoint is running.
  9. For FPM, verify the script path. Compare the path in ProxyPassMatch with the path visible to the FPM process. Apache aliases and virtual-host namespaces are not understood by FPM; FPM needs the actual filesystem location. In containers, that location must be valid in the FPM container as well as in Apache’s environment.
  10. Reload only after checks pass. Then reproduce one failing request and inspect Apache’s access and error logs together with the PHP-FPM log when FPM is used.

Use the response status to choose the next check

Response or behavior Most useful next checks
404 Not Found for CSS, images, or JavaScript Selected vhost, DocumentRoot, exact file existence, URL casing, and any rewrite or alias that changes the path
403 Forbidden for static files or directories <Directory> authorization, parent-directory traversal permissions, filesystem permissions, and whether the requested path is intentionally blocked
Directory listing at / Whether index.php exists in the root and whether DirectoryIndex includes it; do not treat the listing as evidence that PHP is broken
PHP source downloads or appears as text Whether a PHP handler is configured for this vhost and whether the relevant module or FPM mapping is enabled
502 or 503 on PHP requests FPM process state, socket or port, Apache proxy modules, network reachability, and the FastCGI path
PHP error after a successful handoff The application’s runtime error, its dependencies, environment variables, and the PHP-FPM log; static-file mapping may already be correct

PHP handler choices: mod_php and PHP-FPM

Both models can execute PHP, but they differ operationally. The current PHP manual recommends PHP-FPM with Apache’s mod_proxy_fcgi for modern deployments.

Concern mod_php PHP-FPM through mod_proxy_fcgi
Process model PHP runs inside Apache worker processes. A separate PHP-FPM service interprets scripts; Apache proxies the request.
Apache MPM compatibility Must fit the Apache process model and module combination used by the installation; legacy setups commonly depend on a prefork-style arrangement. Works with Apache’s proxy modules and keeps PHP outside Apache’s worker process model.
Connection setup No FastCGI socket or TCP endpoint is needed. Uses a Unix socket or TCP endpoint, which must be running and reachable.
Restart boundary Changing PHP generally involves the Apache process lifecycle. Apache and FPM can be restarted or tuned as separate services.
Path handling Apache resolves the script directly. The FastCGI mapping must pass FPM the exact real filesystem path; aliases alone are insufficient.
Deployment direction Common in older installations. The recommended approach for modern Apache/PHP deployments.

Do not switch handler models merely because static files fail. A wrong document root or authorization rule affects both models. Change the handler only when PHP itself is not being interpreted, the selected vhost has no handler, or the existing runtime is incompatible with the deployment.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Common configuration mistakes and their precise fixes

The file you edited is not enabled

Apache may be loading a distribution-generated site, a different include, or an earlier vhost. The active vhost dump and configuration check reveal this faster than repeatedly editing the same file. Enable or include the intended configuration using your operating system’s normal site-management process, then validate and reload.

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

PHP and assets are in different trees

A project may contain /var/www/example/index.php while its built assets are under /var/www/example/public. If DocumentRoot points to the first directory, Apache can find the PHP file but not the files beneath the public tree. Point the vhost at the real public directory and place the complete browser-facing tree there.

The <Directory> block does not match the root

Changing DocumentRoot without changing the corresponding authorization block can leave the new directory inaccessible. Keep the two paths aligned and check parent-directory traversal permissions. A syntactically valid configuration can still produce 403 responses.

DirectoryIndex was never changed from its default

Direct navigation to /index.php bypasses the need for directory-index discovery. Add the application’s actual index file to the vhost or an intentionally inherited directory context. Do not “fix” a listing by enabling unrelated rewrites before confirming index resolution.

A PHP-only proxy rule was broadened to catch everything

Sending images and stylesheets through FPM adds needless work and can create incorrect path handling. Keep the pattern limited to PHP requests. Static failures should be corrected in the root, file layout, access rules, URL casing, or aliases.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

The FPM endpoint is alive but receives the wrong path

FPM interprets the PHP file path Apache supplies; it does not know Apache aliases, URL prefixes, or vhost namespaces. A 502 can indicate a dead socket or port, while a PHP “file not found” error can indicate a path mismatch even when FPM is running. Compare the proxy mapping with the path mounted inside the FPM process, especially in containerized deployments.

Verification checklist after the fix

  • The intended hostname selects the intended vhost on the correct port.
  • The active vhost’s DocumentRoot contains the PHP entry point and every public asset requested by the page.
  • The matching <Directory> block authorizes that directory and its required files.
  • DirectoryIndex includes index.php when the application uses it.
  • Static URLs use the exact case and directory names present on disk.
  • PHP is handled by the selected runtime rather than downloaded or returned as source.
  • For FPM, Apache can reach the configured socket or port and the passed filesystem path exists from FPM’s perspective.
  • A clean configuration test succeeds before reload.
  • Access, Apache error, and FPM logs agree with the observed status code for a fresh request.

Once these checks pass, a page that loads PHP, CSS, JavaScript, and images is no longer relying on accidental vhost ordering or a coincidental file location; each request is being resolved by the configuration intended for that site.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.