Dynamic Routing with Apache

When a web request hits the Linux kernel, it is handed off directly to Apache. Suppose a user requests a modern framework URL like /user/add.

Because Apache allows configurations to be updated dynamically at the folder level, it must immediately walk your directory tree. First, it asks the kernel to check for a .htaccess file inside your root directory. Next, it attempts to look for the /user directory on your drive. Because that physical folder does not exist, a total of two filesystem checks happen upfront.

Only after the kernel confirms that the path is missing does Apache execute its fallback instructions, internally rewriting the URL to index.php/user/add before finally handing the process over to PHP.

Static File Access: The Hidden Cache Miss Crisis

The true performance crisis occurs when a user requests a deeply nested static asset, such as: /frontend/public/assets/lib/js/modal.js

Because Apache demands real-time configuration updates, it cannot blindly stream this file. It must painstakingly search for a .htaccess file inside every single parent folder leading down to that script, resulting in 7 separate folder layer lookups.

To handle these frequent lookups, the Linux kernel relies on an in-memory dictionary index called the dcache (Directory Entry Cache) and tracks file modifications using RAM-based inodes. Because any file modification (like editing a .htaccess file via FTP or Git) must pass through the kernel first, the kernel instantly updates the file's modification timestamp (mtime) in RAM without needing to poll the storage disk.

However, the way the kernel manages this RAM dictionary introduces a stark contrast between a Cache Hit and a Cache Miss:

The Best Case: A Cache Hit

When a path is accessed, the kernel stores its tracking status directly inside the dcache RAM. If a .htaccess file does not exist, the kernel creates a special card called a Negative Dentry in memory, explicitly marking it as missing. As long as that card remains in memory, the kernel instantly reverts a "Not Found" response to Apache entirely out of RAM. The physical hard drive stays completely asleep.

The Nightmare Scenario: A Cache Miss

The dcache dictionary operates on a strict Least Recently Used (LRU) cache eviction algorithm. Under heavy traffic or low system memory, the kernel continuously deletes old index cards to free up RAM.

When a cache eviction occurs in a deeply nested subdirectory structure, it triggers a catastrophic performance drop. When Apache asks for a .htaccess file in an evicted subdirectory (like /frontend/public/assets/), the kernel hits a Cache Miss. Because the index card is gone, the kernel cannot guess if a file is there. It cannot assume the file is missing because a developer might have uploaded a new .htaccess rule a millisecond ago.

The kernel has no choice: it must bypass RAM, drop down to the hardware layer, and execute a slow, physical disk I/O read operation to scan the drive blocks just to find out if an imaginary file was created.

If you have 6 layers of subdirectories with zero overrides turned off, a single LRU cache clearing forces the kernel to execute 6 independent physical disk seeks just to verify that .htaccess files do not exist, turning a simple asset request into a volatile, time-consuming performance bottleneck.

How we trickle down that I/O cost

While you are bound to Apache's core engine, you can drastically reduce this subdirectory scanning overhead using targeted server configurations. If you have root access to your VPS, you can bypass local directory scanning entirely by implementing one of the following two master configuration styles:

Approach 1: Targeted Directory Blocking

This configuration isolates and disables .htaccess scanning inside a specific folder designated for static assets:

Apache
<VirtualHost *:80>
    ServerName site-one.com
    DocumentRoot /var/www/html/site1
    
    # 1. Main Application Folder: Enable .htaccess for your framework routing
    <Directory "/var/www/html/site1">
        AllowOverride All
    </Directory>
    
    # 2. Public Static Assets Folder: Completely kill .htaccess scanning here
    <Directory "/var/www/html/site1/backend/public">
        AllowOverride None
    </Directory>
</VirtualHost>

Approach 2: Global Sub-Folder Locking (The Optimal Fix)

This approach uses regular expressions (DirectoryMatch) to allow a .htaccess file only at the absolute root of your project, completely blocking sub-folder scanning everywhere else:

Apache
<VirtualHost *:80>
    ServerName site-one.com
    DocumentRoot /var/www/html/site1
    
    # 1. ROOT ONLY: Enable .htaccess EXACTLY at the root domain folder
    <Directory "/var/www/html/site1">
        AllowOverride All
    </Directory>
    
    # 2. ALL SUB-DIRECTORIES: Match any folder inside site1 and kill overrides
    # The Regex "^/var/www/html/site1/.+" means "any sub-path beneath site1"
    <DirectoryMatch "^/var/www/html/site1/.+">
        AllowOverride None
    </DirectoryMatch>
</VirtualHost>

The Verdict

In the first code sample, the server explicitly blocks .htaccess disk checks inside the site1/backend/public path, keeping adjustments localized.

The second scenario is the ultimate optimization play: it strictly confines .htaccess execution to the root directory. For every deep subdirectory or static asset path, Apache reads the instruction AllowOverride None instantly out of its pre-compiled master RAM config.

By eliminating the cascading folder-by-folder crawl, you drop the sub-directory cache-miss I/O cost down to exactly zero, saving a massive amount of CPU and memory resources. Best of all, because this is an administrative web server optimization, you can safely deploy these settings right alongside modern PHP frameworks like Laravel and CodeIgniter without altering a single line of your application code.


The True Resource Cost of Apache’s Synchronous Handoff

The Handoff and the Standby State

When Apache forwards a dynamic web request to PHP, the traffic flows through the pipeline to the PHP-FPM Master Process, which instantly flags an available PHP-FPM worker process to execute the code. However, the internal mechanics of PHP are not our main focus here. Our primary concern is what happens to the Apache worker process during this execution window.

The moment Apache passes the request over the network socket to PHP, that specific Apache worker process remains completely alive in a stagnant, standby mode. It does not shut down, and it is entirely incapable of serving any other incoming visitors. It is held completely hostage by the operating system, forced to wait doing absolutely nothing until PHP completely finishes rendering the page and returns the Response.

The Compounding Memory Disaster at Scale

Consider the structural consequences of this blocking layout under heavy application traffic. Suppose your server is hit with a rapid flood of thousands of parallel requests, arriving just microseconds apart from each other.

Because Apache’s non-blocking scheduler capabilities are non-existent, it can hand off requests to PHP-FPM workers with blazing speed. However, because each handoff forces that specific Apache worker into an uninterruptible sleep state, none of those workers are released back into the server pool. They simply accumulate.

Even if you optimize your Apache environment to ensure that each baseline worker process consumes a tiny, just 3 to 5 MB of memory

*1,000 Concurrent Workers 5 MB Per Worker = 5,000 MB (5 GB of Physical RAM**