How to debug a mod_rewrite rule in PhpStorm or IntelliJ IDEA

Apache’s own rewrite log is the correct answer, and on a lot of hosting you cannot switch it on. Here is what it does, why it is often unavailable, and what to do instead when the file you are debugging is already open in your editor.

The loop you are trying to get out of

You change a RewriteRule, push it, load the URL, and get a 404. Or two redirects. Or a 500 that says nothing useful. So you change it again and push again. Each turn of that loop costs a deploy and tells you one bit: it still doesn’t work.

What you actually want to know is narrower: which rule matched, which RewriteCond failed and what value it was tested against, and what the path had become by the time Apache gave up. There are three places to get that.

1. Apache’s rewrite log — accurate, and often out of reach

If you have server configuration access, this is the ground truth and nothing here replaces it. In Apache 2.4 the old directives are gone:

“Those familiar with earlier versions of mod_rewrite will no doubt be looking for the RewriteLog and RewriteLogLevel directives. Since the release of httpd 2.4, these directives have been replaced with the per-module logging configuration provided by LogLevel.” — Apache HTTP Server documentation, mod_rewrite

The replacement is one directive:

LogLevel alert rewrite:trace3

Rewrite tracing then appears in the error log. mod_rewrite logs at the trace1 to trace8 levels; up to debug no actions are logged at all, and at trace8, in Apache’s words, “practically all actions are logged”. To read only the rewrite lines, the documentation suggests tail -f error_log|fgrep '[rewrite:'.

Two things stop people using it, and both are in Apache’s own documentation rather than folklore.

And even when you have it, the log is downstream of a deploy. It tells you what happened after you shipped the guess.

2. curl, before you open a browser

Browsers cache redirects aggressively — a 301 you have since fixed will keep firing from cache and convince you your new rule is broken. Ask the server directly instead:

curl -sSIL -o /dev/null -w '%{http_code} %{url_effective}\n' https://example.com/blog/hello-world

-L follows the redirect chain and -w prints where it ended up, so a redirect loop shows as a chain rather than as a blank page. This is worth doing before anything else, because it distinguishes “my rule is wrong” from “my browser remembers the old one”. It still needs the rules deployed somewhere.

3. Simulate the file you are editing, before it goes anywhere

The third option is to run the ruleset against a URL locally and read the trace. Nothing is deployed, so the loop is as fast as typing.

Two ways to do that here, both free:

In the browser, with nothing installed: the .htaccess tester. Paste a file and a URL, get every RewriteCond with the value it was actually tested against and every RewriteRule that matched or didn’t. It runs entirely in your browser — your .htaccess is not uploaded, not logged and not sent anywhere, which matters because production rewrite files tend to carry internal paths, admin URLs, staging hostnames and IP allowlists.

From a terminal: the same engine, MIT-licensed, as npx htaccess-trace .htaccess /blog/hello-worldgithub.com/theseusbuilds/htaccess-trace.

In the IDE: .htaccess Pro runs that engine against the file open in your editor, and double-clicking a line of the trace jumps to that line of the file. It is free while in early access.

What the trace tells you that a 404 does not

Most rules that “do nothing” fail for one of a small number of reasons, and the useful part of a trace is that it names which one you have rather than leaving you to guess:

There is a worked example of each on the tester page — including the loop, which is the one that is hardest to see by reading.

What a simulator cannot tell you

Stated here rather than discovered later. A simulation is a model of Apache, not Apache.

So the honest ordering is: simulate to find the rule that is wrong, deploy, then confirm with curl — and if the answers disagree, believe the server and send me the file, because a file this gets wrong is the most useful thing anyone sends me.

Installing the plugin

Any JetBrains IDE on build 242 or later — IntelliJ IDEA (including Community), PhpStorm, WebStorm, PyCharm, RubyMine, GoLand, Rider, CLion.

In your IDE: Settings → Plugins → ⚙ → Manage Plugin Repositories… → + and add

https://theseusbuilds.dev/htaccess-pro/updatePlugins.xml

Then find .htaccess Pro under the Marketplace tab and install it. No account, no email address, no card. Full details on the .htaccess Pro page.