Skip to main content

.htaccess Analyzer

Available
DevOps

Analyze Apache .htaccess configuration, rewrite rules, redirects, security settings, caching and common configuration issues.

.htaccess Analyzer tool

This analyzer performs static analysis of your .htaccess content. It does not execute the configuration or connect to your server. Analysis runs locally in your browser - your file is not uploaded, stored or sent anywhere.

Click a finding's line number to jump to that line.1 line - Ln 1, Col 1
Ctrl/Cmd + Enter to analyze

No analysis yet

Paste your .htaccess above (or load an example) and click Analyze to see scores, findings, rewrite rule breakdowns and suggested fixes.

About .htaccess analysis

What is a .htaccess file?

A .htaccess file is a per-directory configuration file for the Apache HTTP Server. Placed in a website's folder, it changes how Apache behaves for that folder and everything beneath it, without touching the main server configuration - which is why it is so common on shared hosting and in CMS installs.

It is typically used for URL rewriting and redirects, access control and password protection, custom HTTP headers, browser caching, compression, custom error pages, MIME types and PHP settings.

Two properties explain most .htaccess surprises. Apache reads the file on every request, so a mistake takes effect immediately and a syntax error usually produces a 500 error. And the file only works when the server's AllowOverride setting permits the directives you use and the required modules (mod_rewrite, mod_headers, mod_expires...) are loaded.

What does a .htaccess analyzer check?

This tool is a static analysis workspace: it parses your configuration into directives, sections, rules and conditions and reasons about them without running Apache. It looks at:

  • Syntax: unbalanced sections, bad argument counts, malformed flags, unterminated quotes, inline comments
  • RewriteRule and RewriteCond logic, flags, regular expressions and backreferences
  • Redirect status codes, chains and loops, and HTTP to HTTPS handling
  • www and non-www canonicalization, trailing slashes and extension removal
  • Rule order: unreachable, duplicate and conflicting rules
  • Front controllers and WordPress, Laravel, Symfony, CodeIgniter and Drupal patterns
  • Directory listing, Options, MultiViews and FollowSymLinks
  • Sensitive-file protection for .env, .git, backups, dumps, logs and keys
  • Access control (Require and legacy Order/Allow/Deny), IP ranges and Basic authentication
  • Security headers, CORS and Cache-Control
  • Caching with mod_expires, ETags, compression with mod_deflate and Brotli
  • MIME types, handlers, ErrorDocument and hotlink or user-agent rules
  • Apache 2.2 and 2.4 compatibility, required modules, allowed contexts and AllowOverride classes

It reports what static analysis can establish and says so when something depends on your server. It does not execute the file, connect to your server, or fetch URLs, so it cannot confirm that a module is installed or that a redirect returns a particular status at runtime. Results also come with an optional JSON export that you can pretty-print with the JSON Formatter.

Common .htaccess problems

  • Redirect loops. Opposing rules (www versus non-www, adding versus removing a slash), unconditional catch-all redirects, or an HTTPS redirect that cannot see the original protocol behind a proxy.
  • Incorrect RewriteRule patterns. In .htaccess the pattern is matched against the path without a leading slash, so ^/blog never matches, and the pattern never sees the query string.
  • Missing RewriteEngine On. Without it, mod_rewrite rules are ignored unless a parent configuration enabled it.
  • Conflicting or unreachable rules. An earlier rule with [L] or a catch-all pattern ends processing before later rules run; rules placed after a CMS front controller are skipped for non-file URLs.
  • Directory listing. Options Indexes exposes the file list of directories without an index file.
  • WordPress rewrite issues. Edits inside the generated block get overwritten, a missing /index.php fallback breaks permalinks, and duplicated blocks pile up.
  • Apache version compatibility. Apache 2.4 uses Require; Order/Allow/Deny only works through mod_access_compat. Flags such as [END] need 2.4.
  • Missing modules. Header needs mod_headers, ExpiresActive needs mod_expires, RewriteRule needs mod_rewrite. Without an <IfModule> guard a missing module produces a 500 error; with one the rules are silently skipped.

.htaccess security best practices

  • Disable directory listings with Options -Indexes unless you intentionally publish a file index.
  • Deny web access to sensitive files: .env, version-control folders, backups (.bak .old .orig), database dumps, logs and private keys.
  • Serve the site over HTTPS and redirect plain HTTP with a permanent redirect; confirm the redirect works behind any proxy.
  • Add response headers such as X-Content-Type-Options, Referrer-Policy and a tailored Content-Security-Policy. The HTTP Security Headers Analyzer checks real responses in more depth, and the security headers guide explains each header.
  • Use Require for access control and restrict by IP carefully - the CIDR Calculator helps with ranges such as 192.168.1.0/24.
  • Use Basic authentication only over HTTPS, keep the password file outside the web root, and make sure a Require line actually enforces the login.
  • Avoid blanket CORS wildcards on private responses and never combine a wildcard or reflected origin with credentials.
  • Keep PHP execution out of upload directories and review any auto_prepend_file or unusual AddHandler lines you did not add yourself.

When you share error-log excerpts or access logs while debugging, remove IP addresses, tokens and personal data first - the Log Sanitizer does that locally in your browser.

.htaccess RewriteRule examples

Permanent redirect of one URL

RewriteEngine On
RewriteRule ^old-page$ /new-page [R=301,L]

Redirect a section and keep the rest of the path

RewriteRule ^blog-old/(.*)$ /blog/$1 [R=301,L]

Front controller: send non-file URLs to index.php

RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^ index.php [L]

!-f and !-d mean "not an existing file" and "not an existing directory", so CSS, JavaScript, images and uploads are served directly.

Redirect based on the query string

RewriteCond %{QUERY_STRING} ^id=([0-9]+)$
RewriteRule ^product\.php$ /product/%1? [R=301,L]

The trailing ? drops the old query string; use [QSA] to keep it.

Block hotlinking of images

RewriteCond %{HTTP_REFERER} !^$
RewriteCond %{HTTP_REFERER} !^https?://(www\.)?example\.com [NC]
RewriteRule \.(jpe?g|png|gif|webp)$ - [NC,F,L]

How to troubleshoot .htaccess

  1. Back up the current file, then paste it into the analyzer and fix syntax errors first - they cause 500 responses for the whole directory.
  2. Read the Apache error log (often error.log) for the exact directive and line Apache rejected; look for "Invalid command" (missing module or wrong context) and "not allowed here" (AllowOverride).
  3. Confirm the server uses .htaccess at all: AllowOverride must permit the directive classes, and nginx servers ignore the file.
  4. Check that the required modules are enabled (for example apachectl -M or the hosting panel) and that rewrites have RewriteEngine On and FollowSymLinks.
  5. Bisect: comment out blocks until the problem disappears, then re-enable them one at a time. Use a 302 instead of 301 while testing so browsers do not cache the result.
  6. Review rule order - redirects, HTTPS and canonical-host rules go before front-controller rules - and look at the analyzer's loop, chain and unreachable-rule findings.
  7. Inspect the real response with your browser's network tab or curl -I, test in a private window, and then test on a staging copy before deploying.

Did this save you from debugging another .htaccess mystery?

If these free developer tools save you time, consider buying me a byte. Your support helps keep DevCraftKit free and growing.

Buy me a byte 💾

Resources

View all

Explore more developer tools

Browse the full collection, or see everything in DevOps.

.htaccess FAQ

What is an .htaccess file?
An .htaccess file is a per-directory configuration file read by the Apache HTTP Server. It lets you change server behavior for one directory and everything below it - URL rewriting and redirects, access control, authentication, custom headers, caching, compression and error pages - without editing the main server configuration. Apache re-reads it on every request, and it only works when the server's AllowOverride setting permits the directives used.
How do I check an .htaccess file?
Paste it into the analyzer above and click Analyze. You get syntax checks, a breakdown of every rewrite rule and redirect, security, caching and compatibility findings, and suggested fixes. Because this is static analysis, finish by testing the real file on a staging copy of your site and watching Apache's error log, which is the only way to confirm runtime behavior.
How do I validate .htaccess syntax?
The analyzer checks structure that can be verified from the text alone: unbalanced section tags, wrong argument counts, malformed RewriteRule flags, unterminated quotes, invalid Options keywords and similar mistakes. Apache itself only parses .htaccess when a request arrives, so apachectl configtest does not validate it; the definitive check is a request against a staging site, with a syntax error showing up as a 500 response and a line in the error log.
Why is my .htaccess redirect not working?
Common causes are: the server ignores .htaccess (AllowOverride None); mod_rewrite is not enabled; RewriteEngine On is missing; the rule sits after a front-controller or CMS block that already handled the request; a RewriteRule pattern starts with ^/ (in .htaccess the path has no leading slash); an earlier rule ends processing first; or the browser is serving a cached 301. The analyzer flags the first four of these and rule-order problems directly from your file.
Why does my .htaccess cause a redirect loop?
A loop means two or more rules keep sending the request back to a URL the first rule matches again. Typical culprits: an HTTPS redirect that checks %{HTTPS} behind a proxy that terminates TLS, www and non-www rules pointing at each other, an unconditional catch-all redirect, rules that add and remove a trailing slash, and an internal rewrite combined with an external redirect for the same URL. The analyzer reports static loop candidates and explains where the runtime environment could change the result.
How do I force HTTPS with .htaccess?
Enable the rewrite engine, test whether the request is not already HTTPS, and redirect to the same host and URI over https with a permanent redirect: RewriteEngine On, RewriteCond %{HTTPS} !=on, RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]. Put it above other rewrite rules. Behind a reverse proxy or load balancer, also test %{HTTP:X-Forwarded-Proto} !https so the rule does not loop, and skip the redirect on local development hosts.
RewriteEngine On
RewriteCond %{HTTPS} !=on
RewriteCond %{HTTP:X-Forwarded-Proto} !https
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
How do I redirect HTTP to HTTPS?
Use the same pattern: a RewriteCond that detects plain HTTP followed by a RewriteRule with a 301 (or 308) redirect to the https:// URL. If you also canonicalize the host, combine both into one redirect so visitors reach the final URL in a single hop instead of two.
How do I redirect www to non-www?
Match a host that starts with www. in a RewriteCond, capture the rest of the name with a group, and redirect to that captured host: RewriteCond %{HTTP_HOST} ^www\.(.+)$ [NC] followed by RewriteRule ^ https://%1%{REQUEST_URI} [L,R=301]. Use only one direction per site; having both www and non-www rules is a classic redirect loop.
RewriteEngine On
RewriteCond %{HTTP_HOST} ^www\.(.+)$ [NC]
RewriteRule ^ https://%1%{REQUEST_URI} [L,R=301]
How do I remove .php from URLs?
Serve /about from about.php with an internal rewrite guarded by a file-exists check, and optionally redirect direct /about.php requests to /about using %{THE_REQUEST} so the two rules do not loop. Add Options -MultiViews so content negotiation does not compete with the rules. The analyzer checks for missing existence checks, loops between the redirect and the internal rewrite, and MultiViews conflicts.
Options -MultiViews
RewriteEngine On
RewriteCond %{THE_REQUEST} \s/+(.+?)\.php[\s?] [NC]
RewriteRule ^ /%1 [R=301,L]
RewriteCond %{REQUEST_FILENAME}.php -f
RewriteRule ^(.+?)/?$ $1.php [L]
How do I disable directory listing?
Add Options -Indexes to the .htaccess file in the directory (it applies to subdirectories too). Without an index file, Apache then answers with 403 Forbidden instead of listing the files. Setting Options Indexes or Options All turns listings on, which can reveal backups, logs and unexpected files.
Options -Indexes
How do I protect .env using .htaccess?
Deny web access to environment files with a FilesMatch block: Require all denied for names that start with .env. Also keep secrets outside the document root where you can; a deny rule is a safety net, not a substitute. .env.example normally holds no real secrets and does not need to be blocked. Files and FilesMatch match only the last path segment, so protecting a .git directory needs a rewrite rule instead.
<FilesMatch "^\.env(\..+)?$">
    Require all denied
</FilesMatch>
Does .htaccess work on every Apache server?
No. It needs Apache with AllowOverride set to allow the directives you use, and many directives also need a module such as mod_rewrite, mod_headers or mod_expires. Nginx ignores .htaccess entirely, LiteSpeed and OpenLiteSpeed read much of it, and some hosts restrict specific directives. Apache 2.2 and 2.4 also differ - for example Order/Allow/Deny versus Require.
Can this tool execute my .htaccess file?
No. The analyzer performs static analysis in your browser and does not execute or upload your configuration.

Found a bug or an incorrect result in .htaccess Analyzer?

Report it, or suggest an improvement. Messages go straight to the developer.

Send feedback