When building a custom PHP MVC application without a framework like Laravel or Symfony, one of the most essential features you need is clean, user-friendly URLs.
For example, instead of serving a page via [example.com/index.php?page=details&domain=example.com](https://example.com/index.php?page=details&domain=example.com),
you want users and search engines to see [example.com/details/example.com](https://example.com/details/example.com).
In this guide, you will learn how to implement a production-ready Front Controller Pattern using Apache .htaccess and PHP to route dynamic requests seamlessly.
Key Requirements for Clean URL Routing
To make custom URL routing work seamlessly in PHP, your application must fulfill these criteria:
- Clean Browser URLs: Internal rewrites should handle requests without showing index.php in the browser address bar.
- Bypass Static Files: Requests for actual files (like robots.txt, sitemap.xml, CSS, JS, and images) must be served directly.
- Preserve Query Strings: Query parameters like ?ref=123 or ?sort=asc must automatically pass through to PHP without extra flags.
- Normalize Trailing Slashes: Prevent duplicate content issues by automatically redirecting URLs ending with a /.
Step 1: The .htaccess Configuration
Create or update your .htaccess file in your project’s root directory with the following code:
apache
RewriteEngine On
# 1. Force removal of trailing slashes (preserves actual directories)
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_URI} ^(.+)/$
RewriteRule ^ %1 [R=301,L]
# 2. Redirect explicit direct browser requests for index.php to the clean URL
RewriteCond %{THE_REQUEST} \s/index\.php[?\s] [NC]
RewriteRule ^index\.php$ / [R=301,L]
# 3. Serve existing files and directories directly
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
# 4. Route all dynamic requests to index.php internally
RewriteRule ^(.*)$ index.php [L]Technical Breakdown:
THE_REQUEST Rule: Using THE_REQUEST is crucial because it inspects the raw HTTP request line sent by the browser. It detects explicit browser requests for /index.php while ignoring Apache’s internal rewrite to index.php, allowing the application to redirect only direct requests to the clean URL.
Step 2: Parsing Clean URLs and Dynamic Parameters in PHP
Now that Apache routes all traffic through index.php, you can inspect $_SERVER[‘REQUEST_URI’] to build a dynamic router capable of handling multiple route parameters:
<?php
// 1. Get the requested path without query strings
$requestUri = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
// 2. Trim slashes, filter empty segments, and re-index array keys using array_values()
$rawSegments = explode('/', trim($requestUri, '/'));
$pathSegments = array_values(array_filter($rawSegments));
// 3. Extract Controller and Parameters dynamically
$controllerName = !empty($pathSegments[0]) ? $pathSegments[0] : 'home';
// Slice the array to safely extract all remaining sub-paths/parameters
$params = array_slice($pathSegments, 1);
// 4. Router Dispatcher
switch ($controllerName) {
case 'home':
echo "Welcome to the Homepage!";
break;
case 'details':
$domain = $params[0] ?? null;
$subAction = $params[1] ?? null;
if ($domain) {
echo "Loading details for: " . htmlspecialchars($domain);
if ($subAction) {
echo " | Sub-action: " . htmlspecialchars($subAction);
}
} else {
http_response_code(400);
echo "400 Bad Request: Missing required parameters.";
}
break;
default:
http_response_code(404);
echo "404 Page Not Found";
break;
}
Common Pitfalls to Avoid
- Missing Mod_Rewrite: Ensure Apache’s mod_rewrite module is enabled on your server (a2enmod rewrite on Linux).
- AllowOverride None: If your .htaccess rules are ignored, verify that your Apache VirtualHost configuration sets AllowOverride All.
- Hardcoded Links: Update your HTML navigation links to relative clean paths (e.g., <a href=”/details/example.com”>) instead of pointing directly to index.php.
Also read : How to Embed Interactive Plotly Graphs in Python HTML Emails (Fixed)
1 thought on “How to Route Clean PHP URLs to a Controller Without Exposing index.php”