.htaccess RewriteRule Generator
Generate RewriteRule (mod_rewrite) rules for .htaccess. Includes [L,R=301,QSA] flags and regex capture groups.
mod_rewrite in .htaccess: internal rewrites, flags, RewriteCond, and the patterns every developer should memorize
Apache's mod_rewrite is a small regex-based URL processor that runs on every request. Unlike a plain redirect, a rewrite is internal: the URL changes inside the server, but the browser keeps seeing the original address β pretty URLs, hidden file extensions, SPA fallbacks, and reverse-proxy hops are all rewrites under the hood. Externally visible 301/302 hops are rewrites with the [R] flag added on top.
This generator emits a single RewriteRule for the pattern/substitution pair you typed; the reference below covers the full syntax, every flag worth knowing, the RewriteCond variables you will use most often, the common patterns (WordPress, SPA fallback, maintenance mode, pretty URLs), and the performance trade-offs of running rules from .htaccess.
Syntax: RewriteEngine, RewriteCond, RewriteRule
RewriteEngine On
RewriteBase /
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php?route=$1 [QSA,L]
RewriteCond lines stack like an AND chain and apply only to the next RewriteRule. The !-f / !-d tests ("not a file", "not a directory") are the classic guard so static files and real directories bypass the front controller.
Flags cheat sheet
[L]β last; stop processing further rules in this pass.[R=301]β emit an external 301 redirect (any 3xx code accepted).[QSA]β query string append; merge the original query with the new one.[NC]β no case; case-insensitive pattern match.[NE]β no escape; do not URL-encode special characters in the substitution.[F]β forbidden; return 403 without writing the new URL.[G]β gone; return 410 (use to retire URLs permanently).[P]β proxy; pass the request through to a backend (requiresmod_proxy).[E=var:val]β set an environment variable visible to PHP and the access log.
Common patterns
# Pretty URLs: /product/42 -> product.php?id=42
RewriteRule ^product/(\d+)$ product.php?id=$1 [L]
# Hide .php extension
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^([^.]+)$ $1.php [L]
# Single-page app fallback (Vue, React, Angular)
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.html [L]
# Maintenance mode (allow your own IP)
RewriteCond %{REMOTE_ADDR} !^198\.51\.100\.42$
RewriteCond %{REQUEST_URI} !^/maintenance\.html$
RewriteRule (.*) /maintenance.html [R=503,L]
# Subdomain to subdirectory: blog.example.com -> example.com/blog
RewriteCond %{HTTP_HOST} ^blog\.example\.com$ [NC]
RewriteRule (.*) /blog/$1 [L]
RewriteCond variables and operators
Useful variables: %{REQUEST_URI}, %{REQUEST_FILENAME}, %{HTTP_HOST}, %{HTTPS}, %{HTTP_USER_AGENT}, %{QUERY_STRING}, %{TIME_HOUR}, %{REMOTE_ADDR}. Test operators: ! (negate), = (string equality), </> (lexicographic), -d (is directory), -f (is file), -l (is symlink), -s (size > 0). Combine flags after the pattern with [NC,OR] when you need a logical OR between two conditions.
Performance and debugging
Apache reads every .htaccess from the URL's folder up to the document root on each request, so a deep tree with overrides adds latency. When you control the virtual host config, move the rules into <Directory> with AllowOverride None for a measurable speedup. For debugging, raise the log temporarily with LogLevel rewrite:trace3 β every matched rule, captured group, and substitution is printed to the access log. Tools such as htaccess.io and htaccess-checker.com let you dry-run rules in a browser.
FAQ
Rewrite or redirect β which one do I want? Use a rewrite when the URL should change only inside the server (pretty URLs, SPA routing). Use a redirect ([R=301]) when the browser address bar must change too (canonical host, HTTPS, retired URL).
Are .htaccess files slow? Yes, slightly β they are re-read on every request. The fix is to move the rules into the virtual host config and turn AllowOverride off. The behaviour stays identical, only the per-request stat() calls go away.
How do I debug a rule that does not match? Temporarily set LogLevel alert rewrite:trace3 in your virtual host. The access log will print the full rewrite trace: which rules ran, what each capture group held, and the final URL. Remember to lower the level afterwards β trace3 is verbose.
Why is the regex order important? Rules are processed top-to-bottom, and [L] only stops the current pass β internal rewrites can re-enter the pipeline. Place the most specific rules first and the catch-all (e.g. SPA fallback) last so the first match is the right one.
Related Tools
Handwriting Generator
Convert typed text into an image with handwriting appearance. Useful for adding a personal touch to digital work.
Resume Generator
Fill a simple printable A4 CV from a form with personal data, education and experience.
Favicon Generator
Generate a favicon from text/emoji in all common sizes (16, 32, 48, 64, 192, 512). PNG download.