.htaccess 文件是 Apache Web 服务器最强大的配置工具之一。它可以控制重定向、强制 HTTPS、设置安全头、管理缓存等,无需修改主服务器配置。本全面的 .htaccess 重定向速查表提供了可直接复制粘贴的生产级示例,涵盖所有常见场景。每个代码片段都包含详细注释。
基本重定向
重定向是 .htaccess 文件最常见的用途。无论是移动单个页面、重组整个目录还是迁移到新域名,Apache 的 Redirect 和 RewriteRule 指令都能轻松实现。始终使用 301(永久)重定向以便 SEO 权重传递到新 URL。
重定向单个 URL
# Redirect a single URL (301 permanent)
Redirect 301 /old-page.html https://example.com/new-page.html
# Using RewriteRule for more control
RewriteEngine On
RewriteRule ^old-page\.html$ /new-page.html [R=301,L]
# Redirect with pattern matching (e.g., old product URLs)
RewriteRule ^products/([0-9]+)\.html$ /shop/item/$1 [R=301,L]重定向整个目录
# Redirect entire directory to new location
RedirectMatch 301 ^/blog/(.*)$ https://example.com/articles/$1
# Using RewriteRule (preserves subdirectory structure)
RewriteEngine On
RewriteRule ^blog/(.*)$ /articles/$1 [R=301,L]
# Redirect directory but keep filenames
RewriteRule ^old-folder/(.+)$ /new-folder/$1 [R=301,L]域名到域名重定向
# Redirect entire old domain to new domain
RewriteEngine On
RewriteCond %{HTTP_HOST} ^(www\.)?olddomain\.com$ [NC]
RewriteRule ^(.*)$ https://newdomain.com/$1 [R=301,L]
# Redirect specific domain alias to primary domain
RewriteEngine On
RewriteCond %{HTTP_HOST} ^olddomain\.net$ [NC,OR]
RewriteCond %{HTTP_HOST} ^olddomain\.org$ [NC]
RewriteRule ^(.*)$ https://newdomain.com/$1 [R=301,L]HTTPS 强制跳转
强制 HTTPS 对安全和 SEO 至关重要。Google 将 HTTPS 作为排名信号,现代浏览器会对不安全的 HTTP 连接发出警告。这些规则使用 mod_rewrite 将所有 HTTP 流量重定向到 HTTPS,并提供处理 www 前缀的选项。
HTTP 到 HTTPS(不含 www)
# Force HTTPS (redirect HTTP to HTTPS, non-www)
RewriteEngine On
# Redirect www to non-www
RewriteCond %{HTTP_HOST} ^www\.example\.com$ [NC]
RewriteRule ^(.*)$ https://example.com/$1 [R=301,L]
# Redirect HTTP to HTTPS
RewriteCond %{HTTPS} off
RewriteRule ^(.*)$ https://example.com/$1 [R=301,L]HTTP 到 HTTPS(含 www)
# Force HTTPS with www prefix
RewriteEngine On
# Redirect non-www to www
RewriteCond %{HTTP_HOST} ^example\.com$ [NC]
RewriteRule ^(.*)$ https://www.example.com/$1 [R=301,L]
# Redirect HTTP to HTTPS
RewriteCond %{HTTPS} off
RewriteRule ^(.*)$ https://www.example.com/$1 [R=301,L]强制非 www 并使用 HTTPS
# Force non-www + HTTPS in a single pass
# Works on shared hosting and most Apache setups
RewriteEngine On
# Handle both www removal and HTTPS enforcement together
RewriteCond %{HTTP_HOST} ^www\.(.+)$ [NC]
RewriteRule ^(.*)$ https://%1/$1 [R=301,L]
RewriteCond %{HTTPS} off
RewriteRule ^(.*)$ https://%{HTTP_HOST}/$1 [R=301,L]
# Alternative: Using environment variables (some hosts)
# RewriteCond %{ENV:HTTPS} !on
# RewriteRule ^(.*)$ https://%{HTTP_HOST}/$1 [R=301,L]尾部斜杠处理
不一致的尾部斜杠会造成重复内容问题,影响 SEO。搜索引擎将 /about 和 /about/ 视为不同的 URL。选择一种风格并在整个站点中一致地执行。
添加尾部斜杠
# Add trailing slash to all URLs (except files with extensions)
RewriteEngine On
# Only apply to URLs without a file extension
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_URI} !\.[a-zA-Z0-9]{1,5}$
RewriteRule ^(.+[^/])$ %{REQUEST_URI}/ [R=301,L]
# Simpler version (may cause issues with some file types)
# RewriteRule ^(.*[^/])$ $1/ [R=301,L]移除尾部斜杠
# Remove trailing slash from all URLs (except directories)
RewriteEngine On
# Do not remove trailing slash from actual directories
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)/$ /$1 [R=301,L]
# Remove trailing slash except for root URL
# RewriteCond %{REQUEST_URI} !^/$
# RewriteRule ^(.*)/$ /$1 [R=301,L]查询字符串处理
重定向带查询字符串的 URL 需要特别注意。默认情况下,Apache 会将原始查询字符串追加到重定向目标。可以使用 QSA(查询字符串追加)和 ?(丢弃)标志来保留、丢弃或修改查询参数。
重定向并保留查询字符串
# Redirect preserving the original query string (default behavior)
# /search?q=test -> /find?q=test
RewriteEngine On
RewriteRule ^search$ /find [R=301,L]
# Redirect and APPEND additional query parameters (QSA flag)
# /page?id=5 -> /new-page?id=5&ref=old
RewriteRule ^page$ /new-page?ref=old [R=301,L,QSA]
# Match specific query string and redirect
RewriteCond %{QUERY_STRING} ^id=([0-9]+)$
RewriteRule ^product\.php$ /products/%1? [R=301,L]重定向并丢弃查询字符串
# Redirect and DISCARD all query parameters
# /old-page?any=params -> /new-page (clean URL)
RewriteEngine On
RewriteRule ^old-page$ /new-page? [R=301,L]
# The trailing ? strips the query string
# Discard specific query parameters only
# /page?utm_source=x&id=5 -> /page?id=5 (strip tracking params)
RewriteCond %{QUERY_STRING} (^|&)utm_[^&]*
RewriteRule ^(.*)$ /$1? [R=301,L]重定向并修改查询参数
# Rewrite query parameter to path segment
# /index.php?page=about -> /about
RewriteEngine On
RewriteCond %{QUERY_STRING} ^page=(.+)$
RewriteRule ^index\.php$ /%1? [R=301,L]
# Rewrite path segment to query parameter
# /category/electronics -> /shop.php?cat=electronics
RewriteRule ^category/([a-zA-Z0-9-]+)$ /shop.php?cat=$1 [L]
# Rename a query parameter
# /search?q=test -> /search?query=test
RewriteCond %{QUERY_STRING} ^q=(.+)$
RewriteRule ^search$ /search?query=%1 [R=301,L]自定义错误页面
自定义错误页面通过提供有用信息来改善用户体验。ErrorDocument 指令允许为任何 HTTP 状态码定义自定义页面。
# Custom error pages
# Place error page files in your document root
# 404 Not Found - page does not exist
ErrorDocument 404 /errors/404.html
# 403 Forbidden - access denied
ErrorDocument 403 /errors/403.html
# 500 Internal Server Error
ErrorDocument 500 /errors/500.html
# 401 Unauthorized - authentication required
ErrorDocument 401 /errors/401.html
# 503 Service Unavailable - maintenance mode
ErrorDocument 503 /errors/maintenance.html
# You can also use inline messages (not recommended for production)
# ErrorDocument 404 "Page not found. Please check the URL."
# Or redirect to an external URL
# ErrorDocument 404 https://example.com/not-found
# ── Maintenance mode (redirect all traffic to maintenance page) ──
# Uncomment during maintenance, recomment when done
# RewriteEngine On
# RewriteCond %{REMOTE_ADDR} !^123\.456\.789\.000$ # Allow your IP
# RewriteCond %{REQUEST_URI} !/errors/maintenance.html$ [NC]
# RewriteCond %{REQUEST_URI} !\.(css|js|png|jpg|gif|ico)$ [NC]
# RewriteRule ^(.*)$ /errors/maintenance.html [R=503,L]通过 .htaccess 设置安全头
HTTP 安全头保护您的网站免受点击劫持、跨站脚本(XSS)和内容注入等常见攻击。通过 .htaccess 添加这些头是最简单的方法,需要启用 mod_headers。
# Security headers via .htaccess
# Requires mod_headers to be enabled: a2enmod headers
<IfModule mod_headers.c>
# X-Frame-Options: Prevent clickjacking by blocking iframes
# Options: DENY | SAMEORIGIN | ALLOW-FROM uri
Header always set X-Frame-Options "SAMEORIGIN"
# X-Content-Type-Options: Prevent MIME-type sniffing
Header always set X-Content-Type-Options "nosniff"
# X-XSS-Protection: Enable browser XSS filter (legacy)
Header always set X-XSS-Protection "1; mode=block"
# Referrer-Policy: Control referrer information
Header always set Referrer-Policy "strict-origin-when-cross-origin"
# Content-Security-Policy: Control resource loading
# Customize the sources based on your site's needs
Header always set Content-Security-Policy "default-src 'self'; \
script-src 'self' 'unsafe-inline' https://cdn.example.com; \
style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; \
img-src 'self' data: https:; \
font-src 'self' https://fonts.gstatic.com; \
connect-src 'self' https://api.example.com; \
frame-ancestors 'self';"
# Strict-Transport-Security (HSTS): Force HTTPS for 2 years
# Only add this if your site fully supports HTTPS
Header always set Strict-Transport-Security \
"max-age=63072000; includeSubDomains; preload"
# Permissions-Policy: Disable unused browser features
Header always set Permissions-Policy \
"camera=(), microphone=(), geolocation=(), interest-cohort=()"
# Remove X-Powered-By header (hides PHP/server version)
Header unset X-Powered-By
Header always unset X-Powered-By
</IfModule>
# Hide Apache version in server headers
ServerSignature Off浏览器缓存规则
浏览器缓存通过告诉浏览器在本地存储静态资源,显著提高回访者的页面加载速度。mod_expires 模块自动设置 Cache-Control 和 Expires 头。
# Browser caching with mod_expires
# Enable the module: a2enmod expires
<IfModule mod_expires.c>
ExpiresActive On
# Default expiration: 1 month
ExpiresDefault "access plus 1 month"
# HTML files: short cache (content changes frequently)
ExpiresByType text/html "access plus 1 hour"
# CSS and JavaScript: long cache (use fingerprinted filenames)
ExpiresByType text/css "access plus 1 year"
ExpiresByType application/javascript "access plus 1 year"
ExpiresByType text/javascript "access plus 1 year"
# Images: cache for 1 month
ExpiresByType image/jpeg "access plus 1 month"
ExpiresByType image/png "access plus 1 month"
ExpiresByType image/gif "access plus 1 month"
ExpiresByType image/webp "access plus 1 month"
ExpiresByType image/avif "access plus 1 month"
ExpiresByType image/svg+xml "access plus 1 month"
ExpiresByType image/x-icon "access plus 1 year"
# Fonts: cache for 1 year
ExpiresByType font/woff2 "access plus 1 year"
ExpiresByType font/woff "access plus 1 year"
ExpiresByType font/ttf "access plus 1 year"
ExpiresByType application/font-woff2 "access plus 1 year"
ExpiresByType application/font-woff "access plus 1 year"
# JSON/XML data: short cache
ExpiresByType application/json "access plus 1 hour"
ExpiresByType application/xml "access plus 1 hour"
# PDF and documents
ExpiresByType application/pdf "access plus 1 month"
</IfModule>
# Alternative: Cache-Control headers with mod_headers
<IfModule mod_headers.c>
# Immutable cache for fingerprinted assets
<FilesMatch "\.(js|css)$">
Header set Cache-Control "public, max-age=31536000, immutable"
</FilesMatch>
# Short cache for HTML
<FilesMatch "\.html$">
Header set Cache-Control "public, max-age=3600, must-revalidate"
</FilesMatch>
# No cache for dynamic content
<FilesMatch "\.(php|cgi)$">
Header set Cache-Control "no-store, no-cache, must-revalidate"
</FilesMatch>
</IfModule>Gzip / Brotli 压缩
压缩可将传输响应的大小减少 60-90%,显著提升页面加载速度。Apache 支持 Gzip(通过 mod_deflate)和 Brotli(通过 mod_brotli,需 Apache 2.4.26+)。
Gzip 压缩(mod_deflate)
# Gzip compression using mod_deflate
# Enable the module: a2enmod deflate
<IfModule mod_deflate.c>
# Compress text-based content types
AddOutputFilterByType DEFLATE text/plain
AddOutputFilterByType DEFLATE text/html
AddOutputFilterByType DEFLATE text/xml
AddOutputFilterByType DEFLATE text/css
AddOutputFilterByType DEFLATE text/javascript
AddOutputFilterByType DEFLATE application/xml
AddOutputFilterByType DEFLATE application/xhtml+xml
AddOutputFilterByType DEFLATE application/rss+xml
AddOutputFilterByType DEFLATE application/atom+xml
AddOutputFilterByType DEFLATE application/javascript
AddOutputFilterByType DEFLATE application/x-javascript
AddOutputFilterByType DEFLATE application/json
AddOutputFilterByType DEFLATE application/ld+json
AddOutputFilterByType DEFLATE application/vnd.ms-fontobject
AddOutputFilterByType DEFLATE font/opentype
AddOutputFilterByType DEFLATE font/ttf
AddOutputFilterByType DEFLATE font/woff
AddOutputFilterByType DEFLATE font/woff2
AddOutputFilterByType DEFLATE image/svg+xml
AddOutputFilterByType DEFLATE image/x-icon
# Do not compress images (already compressed)
SetEnvIfNoCase Request_URI \.(?:gif|jpe?g|png|webp|avif)$ no-gzip
# Handle browser quirks
BrowserMatch ^Mozilla/4 gzip-only-text/html
BrowserMatch ^Mozilla/4\.0[678] no-gzip
BrowserMatch \bMSIE !no-gzip !gzip-only-text/html
# Add Vary header for proper caching
Header append Vary Accept-Encoding
</IfModule>Brotli 压缩(mod_brotli)
# Brotli compression using mod_brotli (Apache 2.4.26+)
# Enable the module: a2enmod brotli
<IfModule mod_brotli.c>
# Compress text-based content types with Brotli
AddOutputFilterByType BROTLI_COMPRESS text/plain
AddOutputFilterByType BROTLI_COMPRESS text/html
AddOutputFilterByType BROTLI_COMPRESS text/xml
AddOutputFilterByType BROTLI_COMPRESS text/css
AddOutputFilterByType BROTLI_COMPRESS text/javascript
AddOutputFilterByType BROTLI_COMPRESS application/xml
AddOutputFilterByType BROTLI_COMPRESS application/xhtml+xml
AddOutputFilterByType BROTLI_COMPRESS application/javascript
AddOutputFilterByType BROTLI_COMPRESS application/json
AddOutputFilterByType BROTLI_COMPRESS application/ld+json
AddOutputFilterByType BROTLI_COMPRESS font/opentype
AddOutputFilterByType BROTLI_COMPRESS font/ttf
AddOutputFilterByType BROTLI_COMPRESS font/woff
AddOutputFilterByType BROTLI_COMPRESS font/woff2
AddOutputFilterByType BROTLI_COMPRESS image/svg+xml
# Brotli compression quality (0-11, default: 11)
# Lower = faster compression, larger files
# Higher = slower compression, smaller files
BrotliCompressionQuality 6
# Brotli window size (10-24, default: 22)
BrotliCompressionWindow 22
</IfModule>
# Fallback: Use Gzip if Brotli is not available
# Both modules can coexist; Apache serves Brotli to
# browsers that support it and Gzip to others.常见错误及修复方法
调试 .htaccess 问题可能令人沮丧。以下是开发者最常犯的错误及其解决方法,了解这些陷阱能为您节省大量排查时间。
无限重定向循环
最常见的错误是创建重定向循环,重写后的 URL 再次匹配同一规则。始终使用 RewriteCond 在应用规则前检查当前状态,并使用 [L] 标志在匹配后停止处理。
# BAD: Creates an infinite loop
# (The rewritten URL "/new" matches "^(.*)$" again)
RewriteRule ^(.*)$ /new/$1 [R=301]
# GOOD: Use RewriteCond to prevent the loop
RewriteEngine On
RewriteCond %{REQUEST_URI} !^/new/ [NC]
RewriteRule ^(.*)$ /new/$1 [R=301,L]
# GOOD: Another approach - check if already redirected
RewriteEngine On
RewriteCond %{ENV:REDIRECT_STATUS} ^$
RewriteRule ^(.*)$ /new/$1 [R=301,L]错误的 RewriteBase
RewriteBase 定义目录级重写的基础 URL 路径。如果 .htaccess 位于子目录中,RewriteBase 必须匹配该路径。使用错误的值会导致规则生成不正确的 URL。
# If .htaccess is in the document root:
RewriteEngine On
RewriteBase /
# If .htaccess is in /blog/ subdirectory:
RewriteEngine On
RewriteBase /blog/
# If .htaccess is in /app/public/ subdirectory:
RewriteEngine On
RewriteBase /app/public/
# Common mistake: Using RewriteBase /blog when file is at root
# This causes all rewritten URLs to be prefixed with /blog规则顺序很重要
Apache 从上到下处理 .htaccess 规则。如果通用规则出现在特定规则之前,它会先匹配,特定规则永远不会执行。始终将特定规则放在通用规则之前,并使用 [L] 标志防止进一步处理。
# BAD: General rule before specific rule
RewriteEngine On
RewriteRule ^(.*)$ /index.php?page=$1 [L] # Catches everything!
RewriteRule ^about$ /about-us.html [R=301,L] # Never reached!
# GOOD: Specific rules first, general rules last
RewriteEngine On
RewriteRule ^about$ /about-us.html [R=301,L] # Specific: runs first
RewriteRule ^contact$ /contact-us.html [R=301,L] # Specific: runs second
RewriteCond %{REQUEST_FILENAME} !-f # Skip existing files
RewriteCond %{REQUEST_FILENAME} !-d # Skip existing dirs
RewriteRule ^(.*)$ /index.php?page=$1 [L] # General: fallback| 错误 | 修复方法 |
|---|---|
| 缺少 RewriteEngine On | 始终在重写规则块顶部添加 "RewriteEngine On" |
| 模式中未转义点号 | 在正则模式中使用 \. 而不是 .(点号匹配任意字符) |
| 忘记 [L] 标志 | 添加 [L] 在匹配后停止规则处理,防止级联问题 |
| 混用 Redirect 和 RewriteRule | 坚持使用一种方法;混用会导致不可预测的行为 |
| 未清除浏览器缓存测试 | 301 重定向会被浏览器缓存;测试时使用 302,生产环境再改为 301 |
| .htaccess 文件权限错误 | 设置权限为 644(所有者读写,组/其他人只读) |
常见问题
什么是 .htaccess?应该放在哪里?
.htaccess(超文本访问)是 Apache Web 服务器的分布式配置文件。将其放在网站根目录(通常是 public_html 或 www)。它影响所在目录及所有子目录。您也可以在子目录中放置额外的 .htaccess 文件来覆盖父级规则。文件必须命名为 ".htaccess",包含前导点号。
301 和 302 重定向有什么区别?
301 重定向是永久性的——它告诉搜索引擎将所有链接权重(SEO 价值)传递到新 URL 并更新索引。302 重定向是临时性的——搜索引擎保留原始 URL。永久移动(域名更改、URL 重组)使用 301,临时情况(A/B 测试、维护页面)使用 302。浏览器会积极缓存 301 重定向,因此测试时使用 302。
为什么我的 .htaccess 规则不起作用?
最常见的原因:1)mod_rewrite 未启用——运行 "a2enmod rewrite" 并重启 Apache;2)Apache 配置中 AllowOverride 设置为 None——改为 "AllowOverride All";3).htaccess 文件权限不正确(应为 644);4)语法错误——检查 Apache 错误日志;5)浏览器缓存了之前的 301 重定向——清除缓存或使用隐身模式测试。
.htaccess 能在 Nginx 上使用吗?
不能,.htaccess 是 Apache Web 服务器特有的。Nginx 不支持 .htaccess 文件。如果使用 Nginx,需要将等效指令直接添加到 Nginx 服务器块配置中。有在线工具可以帮助将常见 .htaccess 规则转换为 Nginx 配置语法,但转换并非完全一一对应。
网站改版后如何重定向旧 URL?
创建旧 URL 到新 URL 的映射,为每个添加 Redirect 或 RewriteRule。对于大规模更改,使用 RewriteMap 和外部文件。对于基于模式的更改(如 /blog/post-name 到 /articles/post-name),使用带正则捕获组的 RewriteRule:RewriteRule ^blog/(.*)$ /articles/$1 [R=301,L]。始终使用 curl -I 测试重定向以验证状态码和目标。
.htaccess 如何影响网站性能?
Apache 在每次请求时都会读取 .htaccess 文件,这增加了少量性能开销。对于高流量网站,最好将规则移至 Apache 主配置(httpd.conf 或虚拟主机文件),并将 AllowOverride 设为 None。但对于大多数网站,性能影响可以忽略不计。.htaccess 的便利性(无需重启服务器、适用于共享主机)远大于最小的性能成本。
本 .htaccess 速查表涵盖了最基本的重定向模式、安全配置和性能优化。始终使用 curl 或在线重定向检查器彻底测试更改,并在部署到生产环境之前备份可用的 .htaccess 文件。