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.
Need a server to try this on? Antihost offers Linux VPS plans with NVMe storage, full root access and a 14-day free trial.
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.
Crashes, No Sound, or Screen Glitches?
Random freezes, missing sound and display glitches usually trace back to one bad driver. Find and replace yours safely.Free scan · under a minuteWindows Errors? Fix Them Before They Spread
Repair common Windows errors and clear accumulated junk for a smoother, more stable PC - no reinstall needed.Free scan · no reinstallWhat 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 Best Overall
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
- Run the configuration check before reloading:
apachectl configtest. On distributions that use a different binary, use that platform’s equivalent. - 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. - 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:
The Tool Desk
Outbyte PC Repair FREEClear out junk files and repair common Windows errorsFree Scan →Outbyte Driver Updater FREEFix the driver behind crashes, sound loss and screen glitchesFind Drivers →<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.
Rank #2
- Used Book in Good Condition
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.
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.
Recommended Free Tools
<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.
PHP-FPM configuration pattern
A conceptual FPM virtual host keeps the PHP rule inside the same host as the document root:
Rank #4
<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.
Do these 3 things before closing this tab:
1Repair Windows errors before they cause bigger problems2Fix the driver behind crashes, sound loss and screen glitches3Clear out junk files and repair common Windows errors5. Run this troubleshooting sequence
- Validate syntax. Run
apachectl configtest. Fix every error before a reload; otherwise Apache may continue serving the previous configuration. - Inspect active hosts. Run
apachectl -S. Match the request’s scheme, address, and port to the intendedServerNameorServerAlias. - 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. - Confirm the root and access rule. Compare
DocumentRootwith the matching<Directory>path. Ensure the block hasRequire all grantedwhere public access is intended and that parent directories allow traversal. - Check physical files. Use
lsorstaton the exact paths. Match every URL character, including capitalization, hyphens, and extensions. - Set directory indexes. Add
DirectoryIndex index.php index.htmlin the appropriate scope when the application’s entry point is PHP. Confirm that no deeper directory rule overrides it. - 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. - 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. |
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.
Best Value
- Used Book in Good Condition
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.
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:
- Run
apachectl configtestand require a successful result. - Reload Apache using the service manager for your operating system.
- Request the root, the PHP entry point, one CSS file, one JavaScript file, and one image with
curl -i. - Confirm that the response status,
Content-Type, and body are appropriate for each resource. - 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.
Quick wins for a faster PC:
Repair Windows errors before they cause bigger problemsFix Now →Scan for outdated or missing drivers - takes under a minuteDriver Scan →Quick Recap
Final configuration checklist
- The request’s address, port, and
Hostheader select the intended virtual host. ServerNameand requiredServerAliasvalues match the hostname users enter.DocumentRootpoints 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.
DirectoryIndexincludes 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.




