Driver FixRecommendedSound, Wi-Fi or graphics acting up? Check drivers firstFind missing or outdated drivers fast.Check DriversOctober DealsAmazon USOctober deal check: compare before you payAmazon US: current deals, useful picks and tech finds.Check DealsSlow PC?RecommendedPC slow today? Run a repair scan before it gets worseResolve common Windows issues and optimize system performance.Scan Now×
Skip to content
World desk11 min

Apache Virtual Host Only Serving PHP Files? Here’s the Fix

When Apache serves index.php but not static files, troubleshoot the request in layers: virtual-host selection, DocumentRoot mapping, directory access, DirectoryIndex, and PHP handler configuration.
Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

If Apache serves index.php but not CSS, JavaScript, or images, PHP is usually not the root problem. The request is commonly reaching the wrong virtual host, mapping to the wrong DocumentRoot, being denied by a <Directory> rule, or failing directory-index resolution. A PHP-FPM rule can successfully execute PHP while ordinary static-file requests still point at the wrong place.

Fix the layers in order: prove which virtual host handles the request, verify that its DocumentRoot contains the complete public tree, allow that directory, set an appropriate DirectoryIndex, and then validate the PHP handler. The procedure below covers both mod_php and PHP-FPM, including the path mismatch that causes many 502 errors.

As an Amazon Associate I earn from qualifying purchases.

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

What the symptom actually means

Apache processes a request through several independent decisions. The browser sends an address, port, and Host header. Apache selects one virtual host. That host maps the URL beneath its DocumentRoot, applies authorization and filesystem checks, and either serves a static file or hands a PHP script to a handler. A failure at any earlier layer can look like a PHP problem because the application entry point is the file you tested first.

Layer Question to answer Typical evidence when it is wrong
Virtual-host selection Did the request reach the intended ServerName/ServerAlias on the right address and port? The domain shows another site, while a direct PHP URL appears to work under a different host.
Filesystem mapping Does the selected host’s DocumentRoot contain the requested file? PHP exists in one tree, but CSS and images are stored in another; static URLs return 404.
Directory access Does the matching <Directory> block authorize the tree? Assets return 403 even though their paths and names are correct.
Directory index When the URL is a directory, which index resources does mod_dir try? / lists the directory or returns an index error, while /index.php works.
PHP execution Can Apache hand PHP files to mod_php or PHP-FPM? PHP downloads as text, returns 500, or produces 502/503 when FPM is unavailable.

Keep these layers separate while troubleshooting. Do not broaden a PHP proxy rule to “fix” a missing image; static files should normally be served directly by Apache.

1. Prove which virtual host handled the request

Apache chooses a virtual host from the listening address and port, then the request’s Host header. A mismatched name or port can send you to another configuration. When multiple matching hosts exist, the first listed one can become the default for that address and port.

Inspect Apache’s active map

  1. Run the configuration check before reloading: apachectl configtest. On distributions that use a different binary, use that platform’s equivalent.
  2. Dump the active virtual-host map with apachectl -S. Confirm that the expected host appears on the port you are testing and note which entry is marked as the default.
  3. Check the enabled configuration, not only a file in an available-sites directory. A syntactically correct file has no effect until the operating system’s Apache layout enables it.

Check names, ports, and the Host header

The host should contain the exact public name and any required alias:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
<VirtualHost *:80>
    ServerName example.test
    ServerAlias www.example.test
    # ...
</VirtualHost>

Test the same address with an explicit header so DNS and browser caches do not obscure the result:

curl -i -H 'Host: example.test' http://127.0.0.1/

For HTTPS, test the TLS name as well as the header; a request on port 443 is selected independently from port 80. Make sure the virtual host listens on the port you actually use. If apachectl -S shows another host first for that address, correct the names or ordering rather than editing an unrelated file.

2. Make DocumentRoot contain the whole public tree

After selecting a host, Apache maps /assets/site.css to a path beneath that host’s DocumentRoot. It does not search sibling projects, your PHP include path, or a virtual-host name for the file. The root therefore needs the entry script and every browser-facing static asset.

A sound project layout

/srv/www/example/
├── public/
│   ├── index.php
│   ├── assets/
│   │   ├── site.css
│   │   └── app.js
│   └── images/
│       └── logo.svg
└── private/
    └── config.php

Use /srv/www/example/public as the DocumentRoot, not the project parent. Keeping private configuration outside the public directory prevents accidental downloads and makes URL-to-file mapping unambiguous.

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.

Keep DocumentRoot and Directory authorization aligned

<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>
</VirtualHost>

The path in <Directory> must be the real filesystem path and should normally match DocumentRoot. A rule for the project parent does not automatically authorize a differently spelled path, symlink target, or old release directory. On systems using symlinks, verify the resolved target and any restrictive parent-directory permissions.

Verify files and URL casing

Check the exact path from the server, including case:

ls -l /srv/www/example/public/index.php
ls -l /srv/www/example/public/assets/site.css
namei -l /srv/www/example/public/assets/site.css

Linux filesystems are case-sensitive. A browser request for /assets/Site.css does not match site.css. Inspect the browser’s Network panel rather than assuming the HTML contains the path you intended. A relative URL can also resolve under an unexpected directory when the page itself is nested.

3. Resolve the root URL with DirectoryIndex

A request for / is a directory request, not automatically a request for index.php. Apache’s mod_dir checks the resources named by DirectoryIndex; its documented default is index.html. PHP applications commonly need to add index.php.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
<Directory /srv/www/example/public>
    Require all granted
    DirectoryIndex index.php index.html
</Directory>

The order matters. Apache tries index.php first, then index.html. If neither exists and directory listing is enabled, the browser may show a file listing. A listing proves that Apache reached the directory; it does not prove that PHP execution is configured. If listings are not intended, keep directory indexing disabled and fix the missing index instead of treating the listing as a PHP failure.

After changing this directive, reload Apache and request both / and /index.php. If the explicit PHP URL works but the root still lists files, the issue is index resolution or an overriding directory configuration, not the PHP script itself.

4. Confirm how PHP is executed

Apache can execute PHP through an in-process module (mod_php) or pass scripts to a separate PHP-FPM service through mod_proxy_fcgi. The static-file path is independent of either model.

Aspect mod_php PHP-FPM with mod_proxy_fcgi
Process model PHP runs inside Apache worker processes. Apache forwards matching scripts to a separate FPM pool.
Apache MPM considerations Must be compatible with the PHP module and the distribution’s chosen MPM; changing MPM can require changing PHP integration. Works with Apache’s event or worker MPM because PHP is outside the web-server process.
Connection setup No FastCGI socket or port is needed. Uses a Unix socket or TCP endpoint that must be running and reachable.
Restart boundary Reloading or restarting Apache also reloads the embedded PHP module. Apache and FPM can be restarted and scaled separately.
Current deployment guidance Still present in some packages and legacy installations. PHP’s official manual recommends PHP-FPM with Apache mod_proxy_fcgi for modern deployments.

Choose the model your operating system package supports; do not configure both handlers for the same files without understanding which one wins.

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

PHP-FPM configuration pattern

A conceptual FPM virtual host keeps the PHP rule inside the same host as the document root:

<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>

    ProxyPassMatch ^/(.*.php(/.*)?)$ fcgi://127.0.0.1:9000/srv/www/example/public/$1
</VirtualHost>

The exact socket, TCP port, package names, and module-enabling commands vary by operating system and PHP version. Apache needs the proxy modules appropriate to FastCGI, and the FPM service must be listening at the configured endpoint.

The FPM path must be real

PHP-FPM interprets the filesystem path Apache passes to it; it does not understand Apache aliases, virtual-host names, or URL namespaces. In the example, a request for /index.php is passed as /srv/www/example/public/index.php. If the script is actually mounted at /var/www/html/index.php, FPM cannot find it even if the URL looks correct. Containerized deployments are especially prone to this error: the path visible inside the Apache container must resolve to the same script path visible inside the FPM container, or the proxy configuration must account for the separate mount.

Why a PHP-only ProxyPassMatch is normal

A pattern such as ^/(.*.php)$ intentionally matches PHP requests only. CSS, JavaScript, fonts, and images should fall through to Apache’s normal filesystem handler. If those files fail, inspect DocumentRoot, authorization, permissions, URL casing, and the requested path. Expanding the pattern to proxy every extension can hide the real mapping error and needlessly send static content through FPM.

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

5. Run this troubleshooting sequence

  1. Validate syntax. Run apachectl configtest. Fix every error before a reload; otherwise Apache may continue serving the previous configuration.
  2. Inspect active hosts. Run apachectl -S. Match the request’s scheme, address, and port to the intended ServerName or ServerAlias.
  3. Test with an explicit Host header. Use curl -i -H 'Host: example.test' http://127.0.0.1/ and repeat for a known asset, such as /assets/site.css. This isolates vhost selection from DNS.
  4. Confirm the root and access rule. Compare DocumentRoot with the matching <Directory> path. Ensure the block has Require all granted where public access is intended and that parent directories allow traversal.
  5. Check physical files. Use ls or stat on the exact paths. Match every URL character, including capitalization, hyphens, and extensions.
  6. Set directory indexes. Add DirectoryIndex index.php index.html in the appropriate scope when the application’s entry point is PHP. Confirm that no deeper directory rule overrides it.
  7. Identify the handler. For mod_php, verify that the PHP module and file handler are loaded. For FPM, verify mod_proxy/mod_proxy_fcgi, the socket or TCP listener, the pool configuration, and the script path passed by the proxy rule.
  8. Reload and read logs. Reload only after the configuration test succeeds. Correlate the request in Apache access and error logs and in the PHP-FPM log. A 404 generally indicates mapping or file existence; 403 indicates authorization or permissions; 502/503 indicates an FPM connection problem; a PHP error after a successful handoff points into the application or runtime.

Use status codes as branches

Observed result Most useful next check
404 for CSS or images Selected vhost, DocumentRoot, exact file path, URL casing, and rewrite/alias rules.
403 for existing assets Matching <Directory> authorization, parent-directory execute permissions, ownership, and security-policy denials.
Directory listing at / DirectoryIndex, presence of index.php, and any overriding directory configuration.
502 or 503 for PHP FPM process state, socket/port reachability, proxy modules, and the real filesystem path sent to FPM.
PHP source downloads No PHP handler is claiming the file, or a conflicting handler is configured. Stop serving the site publicly until PHP handling is corrected.
500 after PHP starts Read the application and PHP runtime logs; the Apache-to-PHP handoff likely succeeded.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

6. Fix the common symptom patterns

/index.php works, but assets return 404

First request the asset with the same host header used for the PHP test. If the response identifies a different site or document root, correct ServerName, ServerAlias, or port selection. If the host is correct, map the URL manually: /assets/site.css must be present at DocumentRoot/assets/site.css. Move or deploy the complete public tree together; do not copy only the PHP entry script.

/ shows a directory listing, but /index.php works

Add DirectoryIndex index.php in the selected host’s directory scope and reload. Check for a typo in the filename and for a higher- or lower-level configuration that overrides the directive. A listing is also a reason to review whether directory indexing should be disabled for that site.

Assets return 403

Verify the path’s <Directory> block and each parent directory’s traversal permission. The file must be readable by the account Apache uses, but granting broad write permissions is not a safe fix. Correct ownership and mode according to the operating system’s web-server service account, then inspect the error log for the exact denied path.

PHP returns 502 or 503

Check that the FPM service is running and listening on the endpoint in ProxyPassMatch. A Unix socket must exist and be accessible to Apache; a TCP endpoint must be reachable from the Apache process. Then compare the proxy’s filesystem prefix with the path inside the FPM environment. A valid URL and a running FPM service still fail when that path does not exist from FPM’s point of view.

Free tools Windows power users keep installed

One-click scans. No signup required.

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

The domain shows another project

Use apachectl -S and an explicit Host header to identify the selected host. Correct the DNS target, the listen port, or the ServerName/ServerAlias values. Editing the intended project’s file cannot change traffic that is being handled by a different virtual host.

It works on a laptop but not in containers

Compare mounts and paths in both containers. Apache resolves the URL against its own filesystem, while FPM resolves the script path in its own filesystem. Mount the public tree at a consistent path or configure the FastCGI mapping so the path passed by Apache is valid inside the FPM container. Check logs from both services for the same request identifier or timestamp.

7. Reload safely and verify every resource type

Use a repeatable validation pass after each change:

  1. Run apachectl configtest and require a successful result.
  2. Reload Apache using the service manager for your operating system.
  3. Request the root, the PHP entry point, one CSS file, one JavaScript file, and one image with curl -i.
  4. Confirm that the response status, Content-Type, and body are appropriate for each resource.
  5. Watch Apache’s access/error logs while reproducing the browser request, then check PHP-FPM logs only for requests that reach the handler.

A minimal smoke-test set might look like this:

curl -i http://example.test/
curl -i http://example.test/index.php
curl -i http://example.test/assets/site.css
curl -i http://example.test/assets/app.js
curl -I http://example.test/images/logo.svg

Do not consider the fix complete until both dynamic and static requests succeed under the real hostname and protocol. Testing only /index.php can leave a broken document root undiscovered.

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

Final configuration checklist

  • The request’s address, port, and Host header select the intended virtual host.
  • ServerName and required ServerAlias values match the hostname users enter.
  • DocumentRoot points to the directory containing PHP, CSS, JavaScript, images, and other public files.
  • The matching <Directory> block names that same real path and authorizes intended reads.
  • Every requested filename exists with the exact URL casing.
  • DirectoryIndex includes the application’s actual entry file.
  • PHP uses one deliberate handler model: mod_php or PHP-FPM.
  • For FPM, the proxy endpoint is running and the filesystem path passed to FPM exists in FPM’s environment.
  • Configuration tests pass before reload, and logs show no authorization, mapping, or handler errors for the verified requests.

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.

Leave a Reply

Your email address will not be published. Required fields are marked *

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

More from the Wire

  1. World desk4 min
    How to Spot an AI Voice Scam Before Sending MoneyDon’t rely on how a caller sounds. Pause, call back through a known number, and verify the emergency with another trusted person before sending money.
  2. Mountain View desk4 min
    Google’s SynthID Detector: How to Check AI-Generated Images, Video and AudioGoogle’s SynthID Detector looks for an embedded watermark in supported images, video and audio. Here is what its results do—and do not—show.
  3. Redmond desk20 min
    How to create a link to File or Folder in Windows 11Windows 11 gives you several ways to point to a file or folder without moving or duplicating it. You can create a desktop shortcut,…
Recommended PC Tool
Recommended PC Tool
Windows Errors? Fix Them Before They SpreadFree repair scan
Crashes, No Sound, or Screen Glitches?Free driver scan

Two free Windows tools

One Free Minute Could Fix That PC

Before you go - each of these free tools takes about a minute and tackles what quietly slows a Windows PC down.

Special offer. View Outbyte info, uninstall instructions, EULA, and Privacy Policy.