Pretty URLs

Pretty URLs Without a Framework

A site of plain PHP scripts can still publish /products/42/blue-mug instead of product.php?id=42: one internal rewrite per URL shape, turning path segments into query parameters.

Readable URLs mapped onto existing scriptsApache config
# /var/www/example/.htaccess: readable URLs for plain PHP scripts
# Needs: mod_rewrite; AllowOverride FileInfo; Options FollowSymLinks
RewriteEngine On
RewriteRule ^products/(\d+)/[a-z0-9-]+$ product.php?id=$1 [L,QSA]
RewriteRule ^tag/(.+)$ tag.php?name=$1 [B,L,QSA]
# /about serves about.php whenever that script exists
RewriteCond %{REQUEST_FILENAME}.php -f
RewriteRule ^([a-z0-9-]+)$ $1.php [L]

Line 4 keeps the ID and ignores the slug, which is for readers. QSA merges the visitor's query string with the new one instead of discarding it. Line 5 captures free text, so B escapes it before it lands in a query string. Lines 7 and 8 drop .php from any script that exists. Each script prints its name and $_GET; the last two lines ran with B removed:

Output of 55
/products/42/blue-mug?ref=x  /product.php {"id":"42","ref":"x"}
/tag/c++                     /tag.php {"name":"c++"}
/tag/rock%20%26%20roll       /tag.php {"name":"rock & roll"}
/about                       /about.php []
/tag/c++                     /tag.php {"name":"c  "}
/tag/rock%20%26%20roll       HTTP 403

Without B, the + signs became spaces, and the space in rock & roll made mod_rewrite refuse the request (AH10411: Rewritten query string contains control characters or spaces), a safeguard added in 2.4.56 with the fix for CVE-2023-25690.