偽靜態(URL Rewrite)是現代 Web 應用的標配,能提升 SEO、隱藏技術細節、美化 URL。本文系統講解 Apache mod_rewrite 配置全流程、正則規則編寫技巧、常見框架遷移方案與 Nginx 對照配置。
客戶端請求靜態化 URL(如 /article/123.html)→ Apache mod_rewrite 攔截 → 內部重寫為動態腳本(/index.php?action=article&id=123)→ PHP 處理返回內容 → 客戶端瀏覽器地址欄保持原 URL。
# httpd.conf
LoadModule rewrite_module modules/mod_rewrite.so # 取消註釋
# 或動態載入
# LoadModule rewrite_module /usr/lib/apache2/modules/mod_rewrite.so
驗證:phpinfo() → Loaded Modules 搜尋 mod_rewrite,或 apachectl -M | grep rewrite
# httpd.conf
Options FollowSymLinks
AllowOverride All # 關鍵:允許 .htaccess 覆蓋 RewriteRule
Require all granted
# .htaccess 置於網站根目錄
RewriteEngine On
# 1. 防止訪問隱藏檔案
RewriteRule ^\.(.*)$ - [F,L]
# 2. 靜態資源直接服務(不經 PHP)
RewriteCond %{REQUEST_FILENAME} -f [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^ - [L]
# 3. 核心重寫規則:所有非靜態請求轉發到 index.php
RewriteRule ^(.*)$ index.php [QSA,L]
# QSA (Query String Append):保留原查詢字串
# L (Last):停止後續規則處理
# /controller/action/params → index.php?c=controller&a=action¶ms
RewriteRule ^([a-zA-Z0-9_-]+)/([a-zA-Z0-9_-]+)(/.*)?$ index.php?c=$1&a=$2$3 [QSA,L]
# 實際匹配:
# /user/login → c=user, a=login
# /article/view/123 → c=article, a=view, /123
# GET /api/users → index.php?resource=users&action=list
# GET /api/users/123 → index.php?resource=users&action=get&id=123
# POST /api/users → index.php?resource=users&action=create
# PUT /api/users/123 → index.php?resource=users&action=update&id=123
# DELETE /api/users/123 → index.php?resource=users&action=delete&id=123
RewriteRule ^api/([a-zA-Z0-9_-]+)/?$ index.php?resource=$1&action=list [QSA,L]
RewriteRule ^api/([a-zA-Z0-9_-]+)/([0-9]+)/?$ index.php?resource=$1&action=get&id=$2 [QSA,L]
# POST/PUT/DELETE 需在 PHP 中根據 $_SERVER['REQUEST_METHOD'] 判斷
# /article-123.html → index.php?action=article&id=123
RewriteRule ^([a-zA-Z0-9_-]+)-([0-9]+)\.html$ index.php?action=$1&id=$2 [QSA,L]
# /category/news/page-2.html → index.php?cat=news&page=2
RewriteRule ^category/([a-zA-Z0-9_-]+)/page-([0-9]+)\.html$ index.php?cat=$1&page=$2 [QSA,L]
# /zh/article/123 → index.php?lang=zh&c=article&a=view&id=123
RewriteRule ^(zh|en|ja)/([a-zA-Z0-9_-]+)/([a-zA-Z0-9_-]+)(/.*)?$ index.php?lang=$1&c=$2&a=$3$4 [QSA,L]
| 模式 | 含義 | 範例匹配 |
|---|---|---|
[a-zA-Z0-9_-]+ | 字母、數字、底線、連字符,至少一個 | user-center, article_123 |
[0-9]+ | 純數字,至少一位 | 123, 0, 99999 |
(.*) | 任意字元(含空),貪婪匹配 | /foo/bar/baz |
(.*?) | 任意字元,非貪婪匹配 | 盡可能少匹配 |
\. | 字面點號(需轉義) | .html, .php |
^...$ | 錨定開頭結尾,精確匹配 | 防止部分匹配 |
[L] / [last]:停止處理後續規則[QSA] / [qsappend]:追加原查詢字串[R=301] / [redirect=301]:外部重定向(瀏覽器地址變化)[NC] / [nocase]:大小寫不敏感[F] / [forbidden]:返回 403[G] / [gone]:返回 410[P] / [proxy]:代理請求(需 mod_proxy)[E=VAR:VAL] / [env=VAR:VAL]:設定環境變數<?php
// index.php 入口
$requestUri = $_SERVER['REQUEST_URI']; // /article-123.html?source=seo
$scriptName = $_SERVER['SCRIPT_NAME']; // /index.php
// 解析路由
$route = parse_url($requestUri, PHP_URL_PATH); // /article-123.html
$query = parse_url($requestUri, PHP_URL_QUERY); // source=seo
// 配合 .htaccess 規則解析
if (preg_match('#^([a-zA-Z0-9_-]+)-([0-9]+)\.html$#', $route, $matches)) {
$action = $matches[1]; // article
$id = (int)$matches[2]; // 123
parse_str($query, $queryParams);
// 現在有 $action, $id, $queryParams['source']
}
# /etc/apache2/sites-available/example.com.conf
ServerName example.com
ServerAlias www.example.com
DocumentRoot /var/www/example.com/public
Options -Indexes +FollowSymLinks
AllowOverride All
Require all granted
# 直接在 VirtualHost 寫規則(效能優於 .htaccess)
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^ index.php [QSA,L]
ErrorLog ${APACHE_LOG_DIR}/example.com_error.log
CustomLog ${APACHE_LOG_DIR}/example.com_access.log combined
優勢:避免 Apache 遍歷目錄尋找 .htaccess,效能提升 10-20%。
# Nginx 對應配置
server {
listen 80;
server_name example.com;
root /var/www/example.com/public;
index index.php;
# 靜態資源直接服務
location / {
try_files $uri $uri/ /index.php?$query_string;
}
# PHP 處理
location ~ \.php$ {
fastcgi_pass unix:/run/php/php8.1-fpm.sock;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
fastcgi_intercept_errors on;
}
# 隱藏檔案保護
location ~ /\.(?!well-known) {
deny all;
}
}
核心差異: Apache 用 RewriteRule 正則重寫,Nginx 用 try_files 靜態檢查 + 回退到 PHP,語義更清晰、效能更高。
LogLevel alert rewrite:trace3RewriteLog "/var/log/apache2/rewrite.log"(舊版)var_dump($_SERVER['REQUEST_URI'], $_GET, $_POST)[ ] 將 .htaccess RewriteRule 轉換為 try_files + location [ ] 正則捕獲組 $1 $2 → Nginx $1 $2 (相容) [ ] QSA 旗標 → $query_string 變數自動保留 [ ] R=301 重定向 → return 301 /new-url; [ ] 環境變數 E=VAR → fastcgi_param VAR value; [ ] 拒絕訪問 [F] → deny all; / return 403; [ ] 代理 [P] → proxy_pass https://backend; [ ] 虛擬主機配置分離 [ ] 測試所有路由、靜態資源、PHP 執行、錯誤頁面
掌握 mod_rewrite 正則規則編寫與 Apache/Nginx 雙平台配置,是 PHP 後端工程師的核心基本功。建議新專案直接採用 Nginx + PHP-FPM 架構,遺留 Apache 專案按需遷移。