Turning URLs like ?d=example.com&page=wsget into clean paths such as /tool/whois/info/example.com/197 is not only a visual improvement. In real operations, this affects SEO authority, request traceability, route predictability, and long-term maintainability.
This article documents the exact production procedure I used to migrate a WHOIS tool from query-string routing to friendly URLs while preserving backend PHP parameters and legacy compatibility.
1) Initial problem
Legacy URL:
https://example.com/tool/whois/?d=domain1.com
Target route model:
- base domain route:
/tool/whois/domain1.com
- info route:
/tool/whois/info/domain2.com
- info route with record id:
/tool/whois/info/domain3.com/197
Hard requirements:
- keep backend compatibility with
$_GET - avoid redirect loops
- preserve indexed authority from old URLs
- support both with and without trailing slash
2) Technical diagnosis
Before writing rules, I validated typical rewrite failure points:
- generic rule matching before specific rule
- conflict between external redirect and internal rewrite
- old query string leaking into canonical URL
- permissive regex causing wrong captures
Validation command:
apachectl -t
3) Rule design principle
Stable model used in production:
- external 301 redirects first (legacy -> new)
- internal rewrites second (new route ->
index.php?page=...)
This prevents canonical drift and accidental early captures.
4) Internal rewrite rules
To capture domain safely, I used ([^/]+).
RewriteRule ^tool/whois/([^/]+)/?$ index.php?page=whois&d=$1 [L,QSA]
Full internal layer:
RewriteRule ^tool/whois/info/([^/]+)/([^/]+)/?$ index.php?page=wsget&d=$1&id=$2 [L,QSA]
RewriteRule ^tool/whois/info/([^/]+)/?$ index.php?page=wsinf&d=$1 [L,QSA]
RewriteRule ^tool/whois/?$ index.php?page=whois [L,QSA]
5) Legacy 301 redirects
For old links containing page=wsinf, I consolidated to canonical friendly URLs with explicit query-string cleanup.
RewriteCond %{QUERY_STRING} ^([^&]+)&page=wsinf$ [NC]
RewriteRule ^tool/whois/?$ tool/whois/info/%1? [R=301,L]
The trailing ? in destination is critical to drop old query parameters.
6) Final .htaccess used
RewriteEngine On
RewriteBase /
# 1) Legacy to canonical redirects
RewriteCond %{QUERY_STRING} ^page=wsget&id=([^&]+)$ [NC]
RewriteRule ^tool/whois/info/([^/]+)/?$ tool/whois/info/$1/%1? [R=301,L]
RewriteCond %{QUERY_STRING} ^([^&]+)&page=wsinf$ [NC]
RewriteRule ^tool/whois/?$ tool/whois/info/%1? [R=301,L]
# 2) Internal routing
RewriteRule ^tool/whois/info/([^/]+)/([^/]+)/?$ index.php?page=wsget&d=$1&id=$2 [L,QSA]
RewriteRule ^tool/whois/info/([^/]+)/?$ index.php?page=wsinf&d=$1 [L,QSA]
RewriteRule ^tool/whois/([^/]+)/?$ index.php?page=whois&d=$1 [L,QSA]
RewriteRule ^tool/whois/?$ index.php?page=whois [L,QSA]
7) Production validation checklist
- Apache syntax:
apachectl -t
- Redirect/canonical headers:
curl -I "https://example.com/tool/whois/?domain1.com&page=wsinf"
curl -I "https://example.com/tool/whois/info/domain1.com"
- App-level parameter validation:
$_GET['page']$_GET['d']$_GET['id']
- Loop detection:
curl -IL "https://example.com/tool/whois/?domain1.com&page=wsinf"
- Trailing slash behavior:
/tool/whois/info/domain.com/tool/whois/info/domain.com/
8) Troubleshooting notes from field execution
8.1 Generic rule catching specific route
Fix: sort rules from most specific to most generic.
8.2 Browser cache hiding changes
Fix: use private session and curl for deterministic checks.
8.3 Legacy query string surviving redirect
Fix: append ? in redirect destination to clear old query params.
9) Operational best practices
- use
[NC]for case-insensitive query handling - use
/?$when both slash variants must be accepted - separate external redirect and internal rewrite concerns
- validate with
curl+ access logs, not browser only - keep a route matrix document to prevent future regressions
Technical conclusion
Friendly URL migration with Apache .htaccess is a routing refactor, not a cosmetic change. With a two-stage model (301 canonicalization + internal rewrite), you can preserve legacy compatibility, improve SEO, and keep backend business parameters stable under production load.
This post is licensed under CC BY-NC.
Comments
Join the discussion below.
Comments are not configured yet. Add Cusdis settings in /assets/json/config/blog-comments-config.json.