471 lines
14 KiB
Markdown
471 lines
14 KiB
Markdown
|
|
## Apache mod_rewrite Compatibility Module for Nginx
|
||
|
|
|
||
|
|
This module provides Apache's `mod_rewrite` compatibility for Nginx, enabling `.htaccess` file support and standard rewrite rules.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
# Configuration Directives
|
||
|
|
|
||
|
|
The following directives are available in this module:
|
||
|
|
|
||
|
|
### 1. RewriteEngine
|
||
|
|
|
||
|
|
**Purpose:** Enable or disable the rewrite engine.
|
||
|
|
|
||
|
|
| Context | Available Levels |
|
||
|
|
|----------------|---------------------------------------------|
|
||
|
|
| `main`, `server`, `location` | ✓ All three levels supported |
|
||
|
|
|
||
|
|
**Syntax:**
|
||
|
|
```nginx
|
||
|
|
RewriteEngine on|off
|
||
|
|
```
|
||
|
|
|
||
|
|
**Possible Values:**
|
||
|
|
- `on` - Enable rewrite engine
|
||
|
|
- `off` - Disable rewrite engine (default)
|
||
|
|
|
||
|
|
**Example in nginx.conf:**
|
||
|
|
```nginx
|
||
|
|
http {
|
||
|
|
server {
|
||
|
|
RewriteEngine on;
|
||
|
|
|
||
|
|
location /blog/ {
|
||
|
|
# Rules apply here
|
||
|
|
}
|
||
|
|
|
||
|
|
location /static/ {
|
||
|
|
RewriteEngine off; # Disable for this location
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### 2. RewriteRule
|
||
|
|
|
||
|
|
**Purpose:** Define a rewrite rule that matches a URL pattern and substitutes it with a new value.
|
||
|
|
|
||
|
|
| Context | Available Levels |
|
||
|
|
|----------------|---------------------------------------------|
|
||
|
|
| `server`, `location` | ✓ Server and Location level |
|
||
|
|
| `.htaccess` | ✓ Supported in .htaccess files |
|
||
|
|
|
||
|
|
**Syntax:**
|
||
|
|
```nginx
|
||
|
|
RewriteRule pattern substitution [flags]
|
||
|
|
```
|
||
|
|
|
||
|
|
**Parameters:**
|
||
|
|
|
||
|
|
- **pattern** - Regular expression to match the URL (supports backreferences $1, $2, etc.)
|
||
|
|
- Pattern can be prefixed with `!` for negation
|
||
|
|
- `-` as substitution means "no substitution" (only match for conditions)
|
||
|
|
|
||
|
|
- **substitution** - Target URL or file path to rewrite to
|
||
|
|
- Can be a relative path (`/new/path`) or absolute redirect (`http://example.com/new`)
|
||
|
|
|
||
|
|
**Flags:** `[flag1,flag2,...]` (optional):
|
||
|
|
|
||
|
|
| Flag | Abbreviation | Description |
|
||
|
|
|------|--------------|-------------|
|
||
|
|
| `B` | - | Escape backreferences in output (prevent URL encoding issues) |
|
||
|
|
| `C` | Chain | Chain this rule with the next one (requires previous match) |
|
||
|
|
| `D` | DPI | Discard path info after substitution |
|
||
|
|
| `E` | Env=var:val | Set an environment variable for later use (FastCGI integration) |
|
||
|
|
| `F` | Forbidden | Return HTTP 403 Forbidden status |
|
||
|
|
| `G` | Gone | Return HTTP 410 Gone status |
|
||
|
|
| `L` | Last | Stop processing rules after this one |
|
||
|
|
| `N` | Next=limit | Loop (retry) up to N times for infinite rewriting prevention |
|
||
|
|
| `P` | Proxy | Proxy the request internally (Phase 2) |
|
||
|
|
| `Q` | QSA | Append original query string to substitution URL |
|
||
|
|
| `Q` | QSD | Discard original query string |
|
||
|
|
| `Q` | QSL | Set "query string last" marker |
|
||
|
|
| `R` | Redirect=code| Perform external redirect with status code (301, 302, 307) or: Permanent, Temp, SeeOther |
|
||
|
|
| `S` | Skip=N | Skip N subsequent rules after this one |
|
||
|
|
| `T` | Type=mime | Force specific MIME type for the response |
|
||
|
|
| `NC` | - | Case-insensitive matching |
|
||
|
|
| `NOESCAPE` | - | Do not escape special characters in output |
|
||
|
|
| `END`| - | Stop all rewriting and pass to content phase |
|
||
|
|
|
||
|
|
**Example:**
|
||
|
|
```nginx
|
||
|
|
# Redirect /old-path/ to /new-path/ (permanent redirect)
|
||
|
|
RewriteRule ^old-path/(.*)$ /new-path/$1 [R=301,L]
|
||
|
|
|
||
|
|
# Condition-based rewrite (chain)
|
||
|
|
RewriteCond %REQUEST_URI !^/admin/
|
||
|
|
RewriteRule ^admin/(.*)$ /login.php?user=$1 [NC,E,END]
|
||
|
|
|
||
|
|
# In .htaccess:
|
||
|
|
RewriteEngine on
|
||
|
|
RewriteCond %{HTTP_HOST} ^www\.example\.com$ [OR]
|
||
|
|
RewriteCond %{HTTPS} off
|
||
|
|
RewriteRule ^(.*)$ https://example.com/$1 [R=301,L]
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### 3. RewriteCond
|
||
|
|
|
||
|
|
**Purpose:** Define conditions that must be met before a `RewriteRule` is applied. Multiple conditions are ANDed together; OR flag can change this behavior.
|
||
|
|
|
||
|
|
| Context | Available Levels |
|
||
|
|
|----------------|---------------------------------------------|
|
||
|
|
| `server`, `location` | ✓ Server and Location level |
|
||
|
|
| `.htaccess` | ✓ Supported in .htaccess files |
|
||
|
|
|
||
|
|
**Syntax:**
|
||
|
|
```nginx
|
||
|
|
RewriteCond input_string pattern [flags]
|
||
|
|
```
|
||
|
|
|
||
|
|
**Parameters:**
|
||
|
|
|
||
|
|
- **input_string** - Variable or string to test (e.g., `%REQUEST_URI`, `%HTTP_HOST`, `%REQUEST_FILENAME`)
|
||
|
|
- Common variables: `REQUEST_URI`, `HTTP_HOST`, `REQUEST_METHOD`, `REQUEST_FILENAME`
|
||
|
|
|
||
|
|
- **pattern** - Test pattern for the condition:
|
||
|
|
- **File tests:** `-f` (file exists), `-d` (directory exists), `-x` (executable), `-s` (non-zero size)
|
||
|
|
- **Link tests:** `-h` or `-l` (hard/soft link), `-L` (link target exists)
|
||
|
|
- **Integer comparisons:** `-lt`, `-le`, `-eq`, `-gt`, `-ge`, `-ne`
|
||
|
|
- **String comparisons:** `=`, `>`, `<`, `>=`, `<=`
|
||
|
|
- **Regular expressions:** Any regex pattern
|
||
|
|
|
||
|
|
- **flags** (optional):
|
||
|
|
- `NC` - Case-insensitive matching
|
||
|
|
- `OR` / `ornext` - OR logic between conditions (instead of AND)
|
||
|
|
|
||
|
|
**Examples:**
|
||
|
|
```nginx
|
||
|
|
# Condition: file exists
|
||
|
|
RewriteCond %REQUEST_FILENAME -f
|
||
|
|
|
||
|
|
# Condition: not a directory
|
||
|
|
RewriteCond %REQUEST_FILENAME !-d
|
||
|
|
|
||
|
|
# Condition: string comparison
|
||
|
|
RewriteCond %{HTTP_HOST} ^www\.example\.com$ [NC]
|
||
|
|
|
||
|
|
# Integer comparison (file size > 1024 bytes)
|
||
|
|
RewriteCond %REQUEST_FILENAME -s
|
||
|
|
RewriteCond %{FILESIZE} -gt 1024
|
||
|
|
|
||
|
|
# Multiple conditions with OR logic
|
||
|
|
RewriteCond %{HTTP_HOST} ^www\.example\.com$ [OR]
|
||
|
|
RewriteCond %{HTTPS} off
|
||
|
|
|
||
|
|
# Regular expression pattern
|
||
|
|
RewriteCond %{REQUEST_URI} ^/old/(.*)$
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### 4. RewriteBase
|
||
|
|
|
||
|
|
**Purpose:** Set the base URL path for relative substitutions in rewrite rules.
|
||
|
|
|
||
|
|
| Context | Available Levels |
|
||
|
|
|----------------|---------------------------------------------|
|
||
|
|
| `location` | ✓ Location level only |
|
||
|
|
| `.htaccess` | ✓ Supported in .htaccess files |
|
||
|
|
|
||
|
|
**Syntax:**
|
||
|
|
```nginx
|
||
|
|
RewriteBase /path/
|
||
|
|
```
|
||
|
|
|
||
|
|
**Parameters:**
|
||
|
|
- Must start with `/` and end with `/` (trailing slash required)
|
||
|
|
|
||
|
|
**Example:**
|
||
|
|
```nginx
|
||
|
|
# Inside a specific location
|
||
|
|
location /blog/ {
|
||
|
|
RewriteBase /blog/
|
||
|
|
|
||
|
|
# Relative substitution resolves to: /blog/news.html
|
||
|
|
RewriteRule ^news\.html$ news.php [L]
|
||
|
|
}
|
||
|
|
|
||
|
|
# In .htaccess:
|
||
|
|
RewriteEngine on
|
||
|
|
RewriteBase /subdir/
|
||
|
|
RewriteRule ^index\.html$ home.php [L]
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### 5. RewriteOptions
|
||
|
|
|
||
|
|
**Purpose:** Configure rewrite behavior options that affect rule inheritance and processing order.
|
||
|
|
|
||
|
|
| Context | Available Levels |
|
||
|
|
|----------------|---------------------------------------------|
|
||
|
|
| `main`, `server`, `location` | ✓ All three levels supported |
|
||
|
|
|
||
|
|
**Syntax:**
|
||
|
|
```nginx
|
||
|
|
RewriteOptions option1 [option2 ...]
|
||
|
|
```
|
||
|
|
|
||
|
|
**Possible Options:**
|
||
|
|
|
||
|
|
| Option | Description |
|
||
|
|
|-----------------|-------------|
|
||
|
|
| `Inherit` | Inherit rules from parent locations/servers (default behavior) |
|
||
|
|
| `InheritBefore` | Process parent rules before child rules |
|
||
|
|
|
||
|
|
**Example:**
|
||
|
|
```nginx
|
||
|
|
http {
|
||
|
|
server {
|
||
|
|
RewriteOptions Inherit
|
||
|
|
|
||
|
|
location /parent/ {
|
||
|
|
RewriteRule ^parent/(.*)$ /new/$1 [L]
|
||
|
|
}
|
||
|
|
|
||
|
|
location /child/ {
|
||
|
|
# Inherits rules from parent by default with 'Inherit' option
|
||
|
|
RewriteBase /child/
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### 6. RewriteMap
|
||
|
|
|
||
|
|
**Purpose:** Define name-value mappings for lookup tables in rewrite expressions.
|
||
|
|
|
||
|
|
| Context | Available Levels |
|
||
|
|
|----------------|---------------------------------------------|
|
||
|
|
| `server` | ✓ Server level only |
|
||
|
|
| `.htaccess` | ✗ Not supported in .htaccess |
|
||
|
|
|
||
|
|
**Syntax:**
|
||
|
|
```nginx
|
||
|
|
RewriteMap name type:source
|
||
|
|
```
|
||
|
|
|
||
|
|
**Parameters:**
|
||
|
|
|
||
|
|
- **name** - Map identifier (used as variable like `%MAPNAME:value`)
|
||
|
|
- **type** - Type of map:
|
||
|
|
- `int` - Internal function (supported: `tolower`, `toupper`, `escape`, `unescape`)
|
||
|
|
- `txt` - Text file lookup (not supported yet)
|
||
|
|
- `rnd` - Random lookup (not supported yet)
|
||
|
|
- `prg` - Program lookup (not supported yet)
|
||
|
|
|
||
|
|
**Example:**
|
||
|
|
```nginx
|
||
|
|
server {
|
||
|
|
# Map to lowercase version of hostname
|
||
|
|
RewriteMap lc int:tolower
|
||
|
|
|
||
|
|
server_name example.com;
|
||
|
|
|
||
|
|
location / {
|
||
|
|
# Use mapped value in rule
|
||
|
|
RewriteRule ^lc/([^/]+)$ /redirect/$1 [L]
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
# Usage in rules:
|
||
|
|
RewriteRule ^(.*)$ /%lc:$1 [L]
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### 7. HtaccessEnable
|
||
|
|
|
||
|
|
**Purpose:** Enable or disable `.htaccess` file parsing for the server/location.
|
||
|
|
|
||
|
|
| Context | Available Levels |
|
||
|
|
|----------------|---------------------------------------------|
|
||
|
|
| `main`, `server` | ✓ Main and Server level only |
|
||
|
|
| `.htaccess` | ✗ Not supported in .htaccess |
|
||
|
|
|
||
|
|
**Syntax:**
|
||
|
|
```nginx
|
||
|
|
HtaccessEnable on|off
|
||
|
|
```
|
||
|
|
|
||
|
|
**Parameters:**
|
||
|
|
- `on` - Enable `.htaccess` parsing
|
||
|
|
- `off` - Disable `.htaccess` parsing (default)
|
||
|
|
|
||
|
|
**Example:**
|
||
|
|
```nginx
|
||
|
|
http {
|
||
|
|
server {
|
||
|
|
HtaccessEnable on
|
||
|
|
|
||
|
|
# .htaccess files will be searched upward from the request path
|
||
|
|
location /subdir/ {
|
||
|
|
root /var/www/html;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
# In nginx.conf, you can also enable at http level:
|
||
|
|
http {
|
||
|
|
HtaccessEnable on;
|
||
|
|
|
||
|
|
server {
|
||
|
|
# Inherits setting from http level
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### 8. HtaccessName
|
||
|
|
|
||
|
|
**Purpose:** Specify an alternative filename for `.htaccess` files.
|
||
|
|
|
||
|
|
| Context | Available Levels |
|
||
|
|
|----------------|---------------------------------------------|
|
||
|
|
| `main`, `server` | ✓ Main and Server level only |
|
||
|
|
| `.htaccess` | ✗ Not supported in .htaccess |
|
||
|
|
|
||
|
|
**Syntax:**
|
||
|
|
```nginx
|
||
|
|
HtaccessName filename
|
||
|
|
```
|
||
|
|
|
||
|
|
**Parameters:**
|
||
|
|
- Any string (default is `.htaccess`)
|
||
|
|
|
||
|
|
**Example:**
|
||
|
|
```nginx
|
||
|
|
server {
|
||
|
|
HtaccessName .webconfig
|
||
|
|
|
||
|
|
# Will look for .webconfig files instead of .htaccess
|
||
|
|
location / {
|
||
|
|
root /var/www/html;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
# In .htaccess:
|
||
|
|
HtaccessEnable on
|
||
|
|
HtaccessName .custom_htac
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### 9. RewriteFallBack
|
||
|
|
|
||
|
|
**Purpose:** Specify an alternative fallback path when the rewritten file doesn't exist (instead of default `/index.php`). This directive can only be used in `.htaccess` files and is read by `ngx_htaccess_parse_file_from_ha()`. The fallback path is cached per request and retrieved in `ngx_http_apache_rewrite_url_register_hook_with_fallback()`.
|
||
|
|
|
||
|
|
| Context | Available Levels |
|
||
|
|
|----------------|---------------------------------------------|
|
||
|
|
| `.htaccess` | ✓ Supported only in .htaccess files |
|
||
|
|
|
||
|
|
**Syntax:**
|
||
|
|
```apache
|
||
|
|
RewriteFallBack /path/to/fallback.php
|
||
|
|
```
|
||
|
|
|
||
|
|
**Parameters:**
|
||
|
|
- Path must start with `/` (required)
|
||
|
|
- Default fallback is `/index.php` if not specified
|
||
|
|
|
||
|
|
**Example in .htaccess:**
|
||
|
|
```apache
|
||
|
|
# Use custom fallback when file not found
|
||
|
|
RewriteFallBack /custom/app.php
|
||
|
|
|
||
|
|
# Fallback will redirect to /custom/app.php?query_string instead of /index.php
|
||
|
|
RewriteEngine on
|
||
|
|
RewriteCond %{REQUEST_FILENAME} !-f
|
||
|
|
RewriteRule ^(.*)$ index.php [QSA,L]
|
||
|
|
```
|
||
|
|
|
||
|
|
**Example with query string:**
|
||
|
|
```apache
|
||
|
|
RewriteFallBack /handler.php?lang=ru
|
||
|
|
|
||
|
|
# When file doesn't exist, request is redirected to the fallback path
|
||
|
|
# preserving the original query string if present
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Configuration Levels Summary
|
||
|
|
|
||
|
|
| Directive | http/main | server | location | .htaccess |
|
||
|
|
|-----------------|-----------|--------|----------|-----------|
|
||
|
|
| RewriteEngine | ✓ | ✓ | ✓ | ✓ |
|
||
|
|
| RewriteRule | - | ✓ | ✓ | ✓ |
|
||
|
|
| RewriteCond | - | ✓ | ✓ | ✓ |
|
||
|
|
| RewriteBase | - | - | ✓ | ✓ |
|
||
|
|
| RewriteOptions | ✓ | ✓ | ✓ | - |
|
||
|
|
| RewriteMap | - | ✓ | - | - |
|
||
|
|
| HtaccessEnable | ✓ | ✓ | - | - |
|
||
|
|
| HtaccessName | ✓ | ✓ | - | - |
|
||
|
|
| RewriteFallBack | ✗ | ✗ | - | ✓ |
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## .htaccess File Format
|
||
|
|
|
||
|
|
`.htaccess` files follow the Apache format:
|
||
|
|
|
||
|
|
```apache
|
||
|
|
# Comments start with #
|
||
|
|
RewriteEngine on
|
||
|
|
|
||
|
|
RewriteCond %{REQUEST_URI} ^/old/(.*)$
|
||
|
|
RewriteRule ^old/(.*)$ /new/$1 [R=301,L]
|
||
|
|
|
||
|
|
# Multiple conditions (AND logic by default)
|
||
|
|
RewriteCond %{HTTP_HOST} www\.example\.com$
|
||
|
|
RewriteCond %{HTTPS} off
|
||
|
|
RewriteRule (.*) https://www.example.com/$1 [R=301,L]
|
||
|
|
|
||
|
|
RewriteBase /subdir/
|
||
|
|
RewriteFallBack /custom/fallback.php
|
||
|
|
RewriteCond !-f
|
||
|
|
RewriteCond !-d
|
||
|
|
RewriteRule ^(.*)$ index.php?route=$1 [QSA,L]
|
||
|
|
```
|
||
|
|
|
||
|
|
When a file doesn't exist after URL rewriting, the module falls back to:
|
||
|
|
1. `RewriteFallBack` path if specified in `.htaccess`, or
|
||
|
|
2. `/index.php` as default fallback
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Module Features
|
||
|
|
|
||
|
|
### FastCGI Integration
|
||
|
|
The module automatically passes environment variables (set via `[E=VAR:VAL]` flags) to FastCGI applications without manual configuration.
|
||
|
|
|
||
|
|
### Environment Variables
|
||
|
|
Environment variables persist across request phases and are available for downstream modules.
|
||
|
|
|
||
|
|
### .htaccess Caching
|
||
|
|
`.htaccess` files are cached by modification time in the request pool to improve performance on repeated requests. The `RewriteFallBack` directive is also stored in the cache entry and retrieved when needed.
|
||
|
|
|
||
|
|
### RewriteFallBack Directive
|
||
|
|
The `RewriteFallBack` directive allows customizing the fallback path used when a rewritten file doesn't exist:
|
||
|
|
|
||
|
|
1. **Caching:** The fallback path from `.htaccess` is cached per request to avoid repeated parsing
|
||
|
|
2. **Fallback Logic:** When `try_files` fails, the module redirects to the configured fallback instead of `/index.php`
|
||
|
|
3. **Query String Preservation:** Original query string is preserved and appended to fallback path
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Notes
|
||
|
|
|
||
|
|
- Rewrite rules are processed in order as defined in configuration or `.htaccess` files
|
||
|
|
- The `[L]` flag stops processing at the current rule
|
||
|
|
- The `[N]` flag enables looping for infinite redirects prevention (max 10,000 rounds)
|
||
|
|
- Location-level rules override server-level rules unless `Inherit` option is set
|