Test Your Rules
Load a common setup:
Does the requested file exist on the server? (a browser cannot see your disk, so this decides the -f conditions)
Why the Rule You Wrote Is Not the Rule Apache Runs
Ask a chatbot why your redirect does not fire and you will usually get a confident answer built from general knowledge of Apache. The problem is that the answer depends on three things no language model can look up: the exact text of your file, the request you are making, and the context Apache evaluates the file in. Get the context wrong and the correct-looking pattern still matches nothing.
That context is per-directory. A .htaccess file is not a server config, and Apache does not compare your pattern against the path the visitor typed. It strips the leading slash first, because in a directory context the slash is implied. A request for /old-page arrives at your pattern as old-page, which means ^/old-page can never match anything, ever, on any request. The rule that works is ^old-page. Read the two side by side and they look interchangeable. Run them and only one of them does anything.
The second trap is that a rule with no conditions is not selective, it is just blunt. A bare RewriteRule ^old-(.*)$ /new-$1 [R=301,L] also catches every file that genuinely exists, so working pages get redirected and your images and stylesheets start 404ing. The guard everybody copies and almost nobody understands is two conditions requiring the file to be missing before the rule is allowed to act: RewriteCond %{REQUEST_FILENAME} !-f and RewriteCond %{REQUEST_FILENAME} !-d.
The third is the query string. If your substitution contains its own question mark, the visitor's original query string is discarded unless you add QSA. That is how a link with ?id=42 arrives at its destination with no id, and why the destination quietly stops working. A substitution with no question mark carries the query string across untouched, which is why adding QSA to every rule is harmless and omitting it when you needed it is not.
This tester runs your actual lines against your actual URL and shows the whole evaluation: which rule matched, which condition stopped the ones after it, what the final path and status code are, and whether the path came back around on itself. Then it names the mistakes it found and prints the corrected line.
What You Get
- Per-directory evaluation, like the real thing — leading slash stripped before matching, so a pattern that can never work is reported as never working rather than as a rule you should try harder with.
- A trace, not a verdict — every directive gets a line saying matched, skipped, or blocked, and the blocked ones say which condition blocked them.
- Final URL, status and query string — the address a visitor would actually land on, including whether their parameters survived.
- Condition evaluation — server variables, variable backreferences from the last matching regex, numeric and string comparison operators, negation, and the nocase flag.
- A linter that prints the fix — each finding comes with the corrected directive, not just a description of what is wrong.
- Loop detection — a rule without
[L]that sends the path back to where it started gets caught, with the round it repeated on. - Nine ready-made rule sets — HTTPS enforcement, canonical host, trailing slash, blocking an IP, blocking a bad bot, custom 404, and more, so you can see a working file before you paste your own.
What This Tool Will Not Tell You
A browser cannot see your filesystem, so conditions that test for one, -f for a file, -d for a directory, -s for size, are not evaluated here. They are marked unverifiable, the assumed branch is shown, and you can flip that assumption with one click. That is the honest version: everything else is computed the way Apache computes it, and the one thing that is not gets labelled instead of quietly guessed.
There is no Apache here either. mod_rewrite has options set in your server config that this page has no way to read, including RewriteBase on the server, AllowEncodedSlashes, and any mod_alias rules that may run before your file is read. What you get is the rewrite engine evaluated against the context your file actually runs in, which is the part that accounts for nearly every broken redirect anyone hits.
One more limit worth stating plainly: a browser cannot deploy to your host, and it cannot restart Apache. When a 301 is already cached somewhere, no amount of editing the file will recall it, because the visitors' browsers and the search engines' crawlers both hold their own copy. If you are testing a change that used to be a 301, expect the old behaviour to persist for a while and test with a cache-busting path.
Frequently Asked
Why does my RewriteRule with ^/ at the start match nothing?
Because a .htaccess file runs in per-directory context, and Apache strips the leading slash from the path before it compares your pattern to anything. A request for /old-page comes in as old-page, so the pattern ^/old-page can never match. The pattern that works is ^old-page. This is the single most common reason a redirect rule in a .htaccess file does nothing at all, and it is invisible if you only read the rule rather than watch it run. The same rule written ^/old-page in a server config or a virtual host block is correct, because server config runs in server context where the slash is present. Moving a rule between the two places without changing the pattern is what breaks it.
What is the difference between 301 and 302 in an htaccess redirect?
301 is permanent. It tells the browser and every search engine that this is the address from now on, and they update their records accordingly. 302 is temporary, and nothing is transferred: the destination keeps its own ranking and the original address stays in the index. A bare [R] flag with no number is 302, so a rule written with just [R] looks like a redirect and behaves like a temporary one. The practical trap is the reverse: a 301 on a rule you were only testing gets cached hard by browsers, so when you remove it the old address can stay stuck in visitors' caches for a long time. Use 301 when the move is final and 302 while you are still deciding.
Why does my redirect rule work on some pages and not others?
Usually a missing RewriteCond. A bare RewriteRule that redirects also catches the files that actually exist, so your rule sends working pages to a destination and images, CSS and JavaScript 404. The standard guard is two conditions that require the file to be absent before the rule is allowed to fire: %{REQUEST_FILENAME} !-f and %{REQUEST_FILENAME} !-d. Without them the rule is not selective, it is just aggressive. This tester flags the omission by name rather than leaving you to work out why one path behaves differently from another.
What does the QSA flag do and when do I need it?
QSA means append the query string. By default a RewriteRule substitution that contains its own question mark throws the visitor's original query string away. A visitor on /product?id=42 hits a rule that sends them to /new-product and arrives with no id, which breaks anything on the destination that reads parameters. Adding [QSA, L] keeps the id. If your substitution has no question mark at all, the original query string is carried across untouched and QSA changes nothing, which is why adding it is harmless and why omitting it when you needed it is not.
What does the L flag do and what happens if I leave it off?
L means last: stop evaluating further rules in this pass. Without it Apache applies the substitution, moves on to the next rule using the new path, and keeps going. That is sometimes what you want, in a chain of small substitutions. More often it produces a redirect loop, because a later rule sends the path back to where it started. If a redirect is not working, a missing [L] is one of the three things to check, alongside the leading slash and the conditions. This tester shows the whole pass, so you can see which rule fired last and whether the path came back around.
Does this tool check whether a file actually exists on my server?
No, and it says so rather than guessing. A browser cannot see your filesystem, so the -f, -d and -s conditions cannot be evaluated here. This tester marks any condition that depends on the filesystem as unverifiable and shows which branch it assumed, so you always know which verdicts are real and which depend on that one assumption. Everything else, the pattern matching, the variables, the query string handling and the status codes, is computed the same way Apache computes it.
Is my .htaccess file uploaded or stored anywhere?
Nothing leaves your browser. The rules are parsed and simulated by JavaScript running on your own machine, there is no request to a server, no account, and nothing written to disk or to any database. Reload the page and it is all gone. That is also why the tool works with a .htaccess containing internal hostnames or credentials-protected paths, which you would not want to paste into a site that logs what people type.
Comments & Ratings