.htaccess Generator for WordPress

.htaccess Directives & Flags Guide

JA

How the HTTPS Redirect Rule Works

RewriteCond %{HTTPS} !=on [NC]
RewriteCond %{HTTP:X-Forwarded-Proto} !https [NC]
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]

Role of Each Line

Line 1: RewriteCond %{HTTPS} !=on [NC]

Checks that the current request is not an HTTPS connection. If the client is already connected directly via HTTPS, this condition does not match and no redirect occurs.

Line 2: RewriteCond %{HTTP:X-Forwarded-Proto} !https [NC]

Handles cases where the request passes through a reverse proxy or CDN (Cloudflare, AWS ALB, etc.).

In the following topology:

User →(HTTPS)→ CDN/Proxy →(HTTP)→ Server

The server sees an HTTP request even though the user is accessing over HTTPS. The proxy sets the X-Forwarded-Proto header, which allows the original protocol to be determined.

Without this line, proxy-forwarded HTTPS requests will trigger an infinite redirect loop .

Line 3: RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]

Only when both conditions above are true (i.e., it is a genuine HTTP request) does a 301 redirect to the HTTPS URL take place.


RewriteCond (Condition Directive)

A conditional statement that determines "whether to execute the next RewriteRule ". Equivalent to an if statement in programming.

Syntax

RewriteCond %{TestString} Pattern [Flags]

Common Test Strings (Server Variables)

Variable Description
%{HTTPS} on when connection is HTTPS
%{HTTP_USER_AGENT} User-Agent string of the browser or bot
%{REQUEST_URI} Requested path (e.g. /wp-admin/ )
%{REQUEST_FILENAME} Actual file path on the server
%{QUERY_STRING} Query string after ?
%{THE_REQUEST} Raw HTTP request line (e.g. GET /path HTTP/1.1 )
%{HTTP:HeaderName} Value of any HTTP request header

Why Use %{THE_REQUEST} and When

%{THE_REQUEST} and %{REQUEST_URI} look similar, but there is a critical difference: before vs. after Apache's URL normalization .

Variable Example value Characteristic
%{REQUEST_URI} /path/to/page Path after Apache normalization — // is collapsed to /
%{THE_REQUEST} GET /path//to/page HTTP/1.1 Raw request line sent by the client; retains pre-normalization information

Detecting double slashes requires %{THE_REQUEST} . By the time %{REQUEST_URI} is available, // has already been collapsed to / and the condition will not match.

# Normalize URLs containing double slashes with a 301 redirect
# %{REQUEST_URI} is already normalized, so use %{THE_REQUEST} for detection
RewriteCond %{THE_REQUEST} \s[^\s?]*//
RewriteRule ^ %{REQUEST_URI} [R=301,L,NE]

Breakdown of the \s[^\s?]*// pattern:

  • \s — Whitespace between the method and the URL (immediately after GET  )
  • [^\s?]* — Any characters except whitespace and ? (targets the path portion only)
  • // — The double slash being detected

The RewriteRule pattern is applied against the normalized REQUEST_URI . Use %{THE_REQUEST} to detect double slashes in the original request line, then specify the already-normalized %{REQUEST_URI} as the redirect target. The NE flag (noescape) prevents special characters in the redirect target from being escaped. This is the same pattern used in the generator output.

Pattern Syntax

# Regex match
RewriteCond %{HTTP_USER_AGENT} (wget|curl|nikto) [NC]

# Negation match (prefix with !)
RewriteCond %{HTTP:X-Forwarded-Proto} !https [NC]

# Check if a file exists
RewriteCond %{REQUEST_FILENAME} -f

# Check if a directory exists
RewriteCond %{REQUEST_FILENAME} -d

Test Operators (-f, -d, etc.)

In addition to regular expressions, the pattern portion accepts special test operators that check file system state.

Operator Meaning What it checks
-f is F ile A regular file exists
-d is D irectory A directory exists
-s is file with S ize File exists and has a non-zero size
-l is symbolic L ink A symbolic link exists
-F is existing F ile (via subrequest) Existence confirmed via an Apache subrequest (expensive)
-U is existing U RL (via subrequest) URL is accessible (expensive)

In practice, -f and -d are by far the most common. Prefix with ! to negate (e.g. !-f = file does not exist, !-d = directory does not exist). WordPress .htaccess files frequently combine !-f and !-d .

WordPress Usage Example

RewriteCond %{REQUEST_FILENAME} -f [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^ - [L]

This means "if the requested path corresponds to an actual file or directory, return it as-is without rewriting the URL."

This is necessary to serve static files (images, CSS, JS) directly without routing them through WordPress's rewrite engine ( index.php ). Without it, every request to a real file such as /wp-content/uploads/photo.jpg would be forwarded to index.php , breaking images and stylesheets.

Combining Multiple Conditions

Multiple RewriteCond lines default to an AND relationship.

# Condition A AND Condition B → RewriteRule fires only when both are satisfied
RewriteCond %{HTTPS} !=on [NC]
RewriteCond %{HTTP:X-Forwarded-Proto} !https [NC]
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]

Use the [OR] flag to create an OR relationship.

# File exists OR directory exists
RewriteCond %{REQUEST_FILENAME} -f [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^ - [L]

Important: A RewriteCond applies only to the RewriteRule immediately following it.


RewriteRule (Rewrite Rule)

The core directive that actually rewrites URLs or issues redirects.

Syntax

RewriteRule Pattern Substitution [Flags]

Pattern and Substitution

Element Description
^(.*)$ Matches any URL (captured with () )
$1 References the captured content in the substitution
%{HTTP_HOST} Hostname (e.g. example.com )
%{REQUEST_URI} Request path
- No URL rewrite (used for blocking)

Examples

# HTTPS redirect
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]

# Return 403 Forbidden without rewriting the URL (bot blocking)
RewriteRule .* - [F,L]

# Return 410 Gone (against malicious query strings)
RewriteRule ^ - [R=410,L]

IfModule (Module Existence Check)

Executes the enclosed directives only when the specified Apache module is enabled.

Syntax

<IfModule ModuleName>
    # Executed only when the module is available
</IfModule>

Why It Is Needed

Writing directives for a module that does not exist causes an Apache startup error (500) . Wrapping them in IfModule allows the block to be skipped safely in environments where the module is absent.

Common Modules

Module Role
mod_rewrite.c URL rewriting and redirects
mod_deflate.c Gzip compression
mod_expires.c Browser cache expiration headers
mod_headers.c Adding/modifying HTTP response headers
mod_authz_core.c Access control (Apache 2.4+)
mime_module Adding MIME types

Negation (Apache 2.2 / 2.4 Compatible Pattern)

<IfModule mod_authz_core.c>
    # Apache 2.4+ syntax
    Require all denied
</IfModule>
<IfModule !mod_authz_core.c>
    # Apache 2.2 and earlier (fallback)
    Order deny,allow
    Deny from all
</IfModule>

Note: XServer runs Apache 2.4, so in practice only the mod_authz_core.c block is used.


Module Enable Switches

Some modules require not just an IfModule wrapper but also an explicit enable switch (directive) to activate the feature. Without the switch, all subsequent settings in the block are silently ignored.

Modules That Require a Switch

Module Switch Role
mod_rewrite.c RewriteEngine On Enables the URL rewriting engine
mod_expires.c ExpiresActive On Enables the cache expiration feature
# For mod_rewrite
<IfModule mod_rewrite.c>
    RewriteEngine On        ← Switch ON
    RewriteCond %{HTTPS} !=on [NC]
    RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
</IfModule>

# For mod_expires
<IfModule mod_expires.c>
    ExpiresActive On        ← Switch ON
    ExpiresByType text/css "access plus 1 year"
    ExpiresByType image/jpeg "access plus 1 month"
</IfModule>

Modules That Do Not Need a Switch

Modules such as mod_deflate and mod_headers do not require a switch — directives can be written directly inside the IfModule block. Not every module has an enable switch; it depends on the module's specification.

Basic Pattern

IfModule checks module exists → Switch ON → Write directives

These three steps together are the standard .htaccess boilerplate.

Note: You can also explicitly disable with RewriteEngine Off , which is useful for temporarily stopping all rewrite rules during debugging.

Note: When multiple <IfModule mod_rewrite.c> blocks exist, each block needs its own RewriteEngine On . Each block is independent, so the switch must be repeated.


RewriteCond Flags

Flag Name Description
[NC] No Case Case-insensitive matching
[OR] OR condition Changes the default AND relationship to OR

RewriteRule Flags

Frequently Used Flags

Flag Name Description
[L] Last Stops the current rewrite pass; no further rules are evaluated. However, if the URI was rewritten, Apache internally re-evaluates .htaccess . When combined with [R] (external redirect), a response is sent to the client and processing is complete, so no re-evaluation occurs.
[R=code] Redirect Issues a redirect with the given HTTP status code (e.g. R=301 , R=410 ). Using [R] alone defaults to 302.
[F] Forbidden Returns 403 Forbidden
[NE] No Escape Does not encode the substitution URL (prevents double-encoding)
[T=type] Type Forces a MIME type (e.g. T=image/webp )

Other Flags

Flag Name Description
[QSA] Query String Append Appends the original query string to the redirect destination
[QSD] Query String Discard Discards the query string
[P] Proxy Forwards the request as a proxy instead of redirecting
[G] Gone Returns 410 Gone (shorthand for [R=410] )
[CO] Cookie Sets a cookie
[E] Environment Sets an environment variable

Combining Flags

Multiple flags can be combined with comma separation.

# 301 redirect + stop processing + no encoding
RewriteRule ^ %{REQUEST_URI} [R=301,L,NE]

Always include [L] . Omitting it can cause subsequent rules to be applied unintentionally.


Relationship Between the Three Directives

IfModule      → "Is this module available?"
 └ RewriteCond  → "Does this condition match?"
   └ RewriteRule  → "Condition met — process it like this"

Reference: HTTP Status Codes Used in .htaccess

Code Meaning Use case
301 Moved Permanently Permanent redirect (HTTPS enforcement, URL normalization)
302 Found Temporary redirect
403 Forbidden Access denial (bot blocking, file protection)
404 Not Found Resource does not exist (use ErrorDocument 404 for a custom error page)
410 Gone Resource permanently removed (used against malicious requests)
← Back to Generator