Clean URLs with Apache mod_rewrite: A Practical Guide
Learn how to use Apache mod_rewrite to turn unfriendly query‑string URLs into clean, SEO‑friendly paths without changing your application code.
21 Sept 2025, 19:35 UTC

Problem: Unfriendly URLs hurt SEO and usability
When a web application exposes URLs like /product.php?id=123 or /blog.php?year=2026&month=10&day=11&title=apache-modrewrite, users see raw query strings that are hard to read, share, and index.
Thesis: Use Apache mod_rewrite to internally map clean paths to existing scripts
By adding a few directives you can keep the application unchanged while presenting /product/123 or /blog/2026/10/11/apache-modrewrite to visitors.
Enabling the module
On Debian‑based systems run:
sudo a2enmod rewrite sudo systemctl restart apache2You need root or sudo privileges. After restart, verify the module is loaded:
apachectl -t -D DUMP_MODULES | grep rewriteIf the command returns a line containing
rewrite_module, the module is active.Writing a rewrite rule in .htaccess
Place the following block in the document root’s
.htaccessfile (ensureAllowOverride Allis set for the directory):# Turn on the rewriting engine RewriteEngine On # Example: /blog/2026/10/11/apache-modrewrite → blog.php?year=2026&month=10&day=11&title=apache-modrewrite RewriteRule ^blog/([0-9]{4})/([0-9]{2})/([0-9]{2})/([a-z0-9\-]+)$ blog.php?year=$1&month=$2&day=$3&title=$4 [L] # Example: /product/123 → product.php?id=123 RewriteRule ^product/([0-9]+)$ product.php?id=$1 [L]Explanation:
RewriteEngine Onactivates the rewriting engine.- Each
RewriteRuleconsists of a pattern, a substitution, and optional flags. - The pattern uses regular‑expression capture groups (
()) to extract parts of the clean URL. - The substitution references those groups with
$1,$2, etc., building the query string that the original script expects. - The
[L]flag tells Apache to stop processing further rules if this one matches.
Performance considerations and verification
While .htaccess is convenient, it triggers a filesystem check for every request, which can add latency on high‑traffic sites. For better performance place the same rules in the virtual host or server configuration and set AllowOverride None.
To verify that the rewrite works internally (the browser URL stays unchanged):
- Request a clean URL with curl, asking for headers only:
curl -I http://localhost/blog/2026/10/11/apache-modrewriteLook for a
200 OKresponse and ensure there is noLocation:header (which would indicate an external redirect).
- Check the application logs or add a temporary debug line in
blog.phpto print$_GETvalues; they should contain the expected parameters.If you see a 404 or the script receives empty parameters, double‑check the regular expression and the
[L]flag placement.Trade‑off: simplicity vs. performance
The .htaccess approach lets you change URLs without restarting Apache, but each request incurs an extra stat() call to see if a .htaccess file exists in any parent directory. On a busy server this can become measurable. Moving the rules to
apache2.confor a virtual host eliminates the per‑request check and is the recommended practice for production workloads.Actionable closing
Enable mod_rewrite, test a simple rule in .htaccess, confirm the internal mapping with curl and application logs, then consider migrating the rules to the server config for better performance. If you need to revert the module change, run:
sudo a2dismod rewrite sudo systemctl restart apache2Remember that removing the module will break any existing rewrite rules, so keep a backup of your configuration before disabling.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.