强曰为道
与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

Apache HTTP Server 完全指南 / 从 Nginx 迁移

从 Nginx 迁移

从 Nginx 迁移到 Apache 需要理解两者配置差异,本章提供详细的配置映射和迁移策略。

1. 架构差异

1.1 核心区别

特性NginxApache
架构事件驱动进程/线程模型
配置块式声明指令式
.htaccess不支持支持
模块编译时静态动态/静态
URL 重写rewritemod_rewrite
反向代理proxy_passProxyPass
负载均衡upstreamProxy balancer
虚拟主机server {}VirtualHost

1.2 配置文件结构对比

Nginx:

/etc/nginx/
├── nginx.conf           # 主配置
├── conf.d/              # 额外配置
├── sites-available/     # 可用站点
├── sites-enabled/       # 启用站点
└── modules-enabled/     # 启用模块

Apache:

/etc/apache2/
├── apache2.conf         # 主配置
├── ports.conf           # 端口配置
├── conf-available/      # 可用配置
├── conf-enabled/        # 启用配置
├── mods-available/      # 可用模块
├── mods-enabled/        # 启用模块
├── sites-available/     # 可用站点
└── sites-enabled/       # 启用站点

2. 配置映射

2.1 基本服务器配置

Nginx:

worker_processes auto;
worker_connections 1024;
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
types_hash_max_size 2048;
server_tokens off;

Apache:

# MPM 配置
<IfModule mpm_event_module>
    StartServers             3
    MinSpareThreads         75
    MaxSpareThreads        250
    ThreadsPerChild         25
    MaxRequestWorkers      400
    MaxConnectionsPerChild 10000
</IfModule>

# 基本配置
KeepAlive On
MaxKeepAliveRequests 100
KeepAliveTimeout 5
ServerTokens Prod
ServerSignature Off

2.2 虚拟主机映射

Nginx:

server {
    listen 80;
    server_name www.example.com example.com;
    root /var/www/example.com/public;
    index index.html index.php;
    
    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }
    
    location ~ \.php$ {
        fastcgi_pass unix:/run/php/php8.2-fpm.sock;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
    }
    
    location ~* \.(jpg|jpeg|png|gif|ico|css|js|woff2)$ {
        expires 1y;
        add_header Cache-Control "public, immutable";
    }
    
    error_log /var/log/nginx/example-error.log;
    access_log /var/log/nginx/example-access.log;
}

Apache:

<VirtualHost *:80>
    ServerName www.example.com
    ServerAlias example.com
    DocumentRoot /var/www/example.com/public
    
    <Directory "/var/www/example.com/public">
        Options -Indexes +FollowSymLinks
        AllowOverride All
        Require all granted
        
        # try_files 等价
        <IfModule mod_rewrite.c>
            RewriteEngine On
            RewriteCond %{REQUEST_FILENAME} !-f
            RewriteCond %{REQUEST_FILENAME} !-d
            RewriteRule ^ index.php [QSA,L]
        </IfModule>
    </Directory>
    
    # PHP-FPM
    <FilesMatch \.php$>
        SetHandler "proxy:unix:/run/php/php8.2-fpm.sock|fcgi://localhost"
    </FilesMatch>
    
    # 静态文件缓存
    <IfModule mod_expires.c>
        <LocationMatch "\.(jpg|jpeg|png|gif|ico|css|js|woff2)$">
            ExpiresDefault "access plus 1 year"
            Header set Cache-Control "public, immutable"
        </LocationMatch>
    </IfModule>
    
    ErrorLog /var/log/apache2/example-error.log
    CustomLog /var/log/apache2/example-access.log combined
</VirtualHost>

2.3 反向代理映射

Nginx:

upstream backend {
    server 127.0.0.1:8080;
    server 127.0.0.1:8081;
    keepalive 32;
}

server {
    listen 80;
    server_name api.example.com;
    
    location / {
        proxy_pass http://backend;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_http_version 1.1;
        proxy_set_header Connection "";
    }
}

Apache:

# 启用模块
# sudo a2enmod proxy proxy_http headers

<Proxy balancer://backend>
    BalancerMember http://127.0.0.1:8080
    BalancerMember http://127.0.0.1:8081
    ProxySet lbmethod=byrequests
</Proxy>

<VirtualHost *:80>
    ServerName api.example.com
    
    ProxyPreserveHost On
    ProxyPass / balancer://backend/
    ProxyPassReverse / balancer://backend/
    
    RequestHeader set X-Real-IP "%{REMOTE_ADDR}s"
    RequestHeader set X-Forwarded-For "%{REMOTE_ADDR}s"
    RequestHeader set X-Forwarded-Proto "http"
</VirtualHost>

2.4 负载均衡映射

Nginx:

upstream app {
    least_conn;
    server app1.example.com weight=3;
    server app2.example.com weight=2;
    server app3.example.com backup;
    
    keepalive 32;
}

server {
    location / {
        proxy_pass http://app;
    }
}

Apache:

<Proxy balancer://app>
    BalancerMember http://app1.example.com loadfactor=3
    BalancerMember http://app2.example.com loadfactor=2
    BalancerMember http://app3.example.com status=+H
    
    ProxySet lbmethod=bybusyness
</Proxy>

<VirtualHost *:80>
    ServerName www.example.com
    
    ProxyPass / balancer://app/
    ProxyPassReverse / balancer://app/
</VirtualHost>

2.5 SSL 配置映射

Nginx:

server {
    listen 443 ssl http2;
    server_name www.example.com;
    
    ssl_certificate /etc/ssl/certs/example.com.crt;
    ssl_certificate_key /etc/ssl/private/example.com.key;
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;
    ssl_prefer_server_ciphers off;
    
    add_header Strict-Transport-Security "max-age=31536000" always;
    add_header X-Content-Type-Options "nosniff" always;
}

server {
    listen 80;
    server_name www.example.com;
    return 301 https://$host$request_uri;
}

Apache:

# HTTP 重定向
<VirtualHost *:80>
    ServerName www.example.com
    RewriteEngine On
    RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
</VirtualHost>

# HTTPS 站点
<VirtualHost *:443>
    ServerName www.example.com
    
    SSLEngine on
    SSLCertificateFile /etc/ssl/certs/example.com.crt
    SSLCertificateKeyFile /etc/ssl/private/example.com.key
    
    SSLProtocol all -SSLv3 -TLSv1 -TLSv1.1
    SSLCipherSuite ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256
    SSLHonorCipherOrder off
    
    Header always set Strict-Transport-Security "max-age=31536000"
    Header always set X-Content-Type-Options "nosniff"
    
    Protocols h2 http/1.1
</VirtualHost>

2.6 URL 重写映射

Nginx:

# 重定向
location /old-page {
    return 301 /new-page;
}

# 正则重写
rewrite ^/blog/(\d+)/(.*)$ /posts/$1/$2 permanent;

# 条件重写
if ($http_host ~* ^www\.(.*)$) {
    return 301 https://$1$request_uri;
}

Apache:

# 重定向
Redirect 301 /old-page /new-page

# mod_rewrite 重写
RewriteEngine On
RewriteRule ^/blog/(\d+)/(.*)$ /posts/$1/$2 [R=301,L]

# 条件重写
RewriteCond %{HTTP_HOST} ^www\.(.+)$ [NC]
RewriteRule ^(.*)$ https://%1/$1 [R=301,L]

2.7 缓存配置映射

Nginx:

location ~* \.(jpg|jpeg|png|gif|ico|css|js)$ {
    expires 1y;
    add_header Cache-Control "public";
    access_log off;
}

Apache:

<IfModule mod_expires.c>
    <LocationMatch "\.(jpg|jpeg|png|gif|ico|css|js)$">
        ExpiresDefault "access plus 1 year"
        Header set Cache-Control "public"
    </LocationMatch>
</IfModule>

# 不记录静态文件日志
SetEnvIf Request_URI "\.(jpg|jpeg|png|gif|ico|css|js)$" dontlog
CustomLog /var/log/apache2/access.log combined env=!dontlog

3. 功能映射表

3.1 核心功能

Nginx 功能Apache 等价
server {}<VirtualHost>
location {}<Directory>, <Location>
listenListen
server_nameServerName, ServerAlias
rootDocumentRoot
indexDirectoryIndex
try_filesmod_rewrite
return 301Redirect 301
rewriteRewriteRule
proxy_passProxyPass
fastcgi_passSetHandler proxy:fcgi://
error_pageErrorDocument
access_logCustomLog
error_logErrorLog
expiresmod_expires
add_headerHeader set
gzipmod_deflate

3.2 变量映射

Nginx 变量Apache 变量
$host%{HTTP_HOST}i
$remote_addr%{REMOTE_ADDR}s
$request_uri%{REQUEST_URI}s
$query_string%{QUERY_STRING}s
$scheme%{REQUEST_SCHEME}s
$server_port%{SERVER_PORT}s
$http_user_agent%{HTTP_USER_AGENT}i
$http_referer%{HTTP_REFERER}i
$request_method%{REQUEST_METHOD}s
$uri%{REQUEST_URI}s
$args%{QUERY_STRING}s
$cookie_name%{COOKIE_name}i

3.3 标志映射

Nginx 标志Apache 标志
permanent (301)[R=301,L]
redirect (302)[R=302,L]
last[L]
break[L]
last (rewrite)[L]
flag (proxy)[P,L]

4. 渐进迁移策略

4.1 迁移计划

阶段 1:并行部署(1-2 周)
├── 安装和配置 Apache
├── 同步网站文件
├── 在非生产环境测试
└── 修复兼容性问题

阶段 2:灰度切换(1 周)
├── 切换 10% 流量到 Apache
├── 监控性能和错误
├── 逐步增加流量比例
└── 持续观察

阶段 3:全面切换(1-2 天)
├── 切换 100% 流量
├── 保留 Nginx 作为备份
├── 监控 24 小时
└── 确认稳定后下线 Nginx

4.2 灰度发布

使用负载均衡器:

# Nginx 作为前端(灰度阶段)
upstream old_backend {
    server 127.0.0.1:8080;  # Nginx
}

upstream new_backend {
    server 127.0.0.1:8081;  # Apache
}

split_clients "${remote_addr}" $backend {
    10%   new_backend;
    *     old_backend;
}

server {
    location / {
        proxy_pass http://$backend;
    }
}

使用 DNS 权重:

www.example.com  A  192.168.1.100  # Nginx (90% 流量)
www.example.com  A  192.168.1.101  # Apache (10% 流量)

4.3 验证清单

#!/bin/bash
# migration-verify.sh

APACHE_URL="http://localhost:8081"
NGINX_URL="http://localhost:8080"

# 测试 URL 列表
URLS=(
    "/"
    "/about"
    "/api/data"
    "/static/style.css"
    "/images/logo.png"
)

echo "=== 迁移验证 ==="

for url in "${URLS[@]}"; do
    APACHE_CODE=$(curl -s -o /dev/null -w "%{http_code}" "$APACHE_URL$url")
    NGINX_CODE=$(curl -s -o /dev/null -w "%{http_code}" "$NGINX_URL$url")
    
    if [ "$APACHE_CODE" == "$NGINX_CODE" ]; then
        echo "✅ $url - Apache: $APACHE_CODE, Nginx: $NGINX_CODE"
    else
        echo "❌ $url - Apache: $APACHE_CODE, Nginx: $NGINX_CODE"
    fi
done

5. 常见迁移问题

5.1 try_files 等价

# Nginx: try_files $uri $uri/ /index.php?$query_string;

# Apache (mod_rewrite)
<IfModule mod_rewrite.c>
    RewriteEngine On
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteRule ^ index.php [QSA,L]
</IfModule>

# 或使用 FallbackResource
<Directory "/var/www/html">
    FallbackResource /index.php
</Directory>

5.2 嵌套 location

# Nginx:
# location /api/v1/ {
#     location /api/v1/users { ... }
# }

# Apache 使用嵌套 Directory 或 Location
<Location "/api/v1">
    # API v1 通用配置
    
    <Location "/api/v1/users">
        # 用户端点配置
    </Location>
</Location>

5.3 条件判断

# Nginx:
# if ($request_method = POST) { ... }

# Apache 使用 mod_rewrite
RewriteCond %{REQUEST_METHOD} ^POST$ [NC]
RewriteRule ^ /handle-post.php [L]

# 或使用 <If>
<If "%{REQUEST_METHOD} == 'POST'">
    # POST 请求配置
</If>

5.4 map 等价

# Nginx:
# map $http_host $backend {
#     default 127.0.0.1:8080;
#     ~^api   127.0.0.1:3000;
# }

# Apache 使用 RewriteMap
RewriteMap backend txt:/etc/apache2/backend.map

# backend.map 内容:
# api.example.com 127.0.0.1:3000
# default 127.0.0.1:8080

RewriteCond ${backend:%{HTTP_HOST}|default} ^(.+)$
RewriteRule ^(.*)$ http://%1/$1 [P,L]

6. 性能对比

6.1 基准测试

# 静态文件测试
# Nginx
wrk -t12 -c400 -d30s http://nginx-server/index.html

# Apache
wrk -t12 -c400 -d30s http://apache-server/index.html

# PHP 测试
# Nginx + PHP-FPM
wrk -t12 -c100 -d30s http://nginx-server/info.php

# Apache + PHP-FPM
wrk -t12 -c100 -d30s http://apache-server/info.php

6.2 预期差异

场景NginxApache说明
静态文件更快略慢Nginx 事件驱动优势
PHP相当相当都使用 PHP-FPM
反向代理更快相当Nginx 代理更轻量
.htaccess不支持支持Apache 独有功能
内存使用更低更高进程模型差异

7. 回滚计划

7.1 快速回滚

#!/bin/bash
# rollback-to-nginx.sh

# 1. 切换端口
sudo sed -i 's/Listen 80/Listen 8081/' /etc/apache2/ports.conf
sudo systemctl restart apache2

# 2. 恢复 Nginx
sudo sed -i 's/listen 8080/listen 80/' /etc/nginx/sites-enabled/default
sudo systemctl restart nginx

# 3. 更新负载均衡/防火墙规则
# 根据实际环境配置

echo "已回滚到 Nginx"

7.2 数据同步

# 同步网站文件
rsync -avz /var/www/nginx/ /var/www/apache/

# 同步 SSL 证书
rsync -avz /etc/nginx/ssl/ /etc/apache2/ssl/

# 同步日志(用于分析)
rsync -avz /var/log/nginx/ /var/log/nginx-backup/

8. 业务场景

8.1 WordPress 迁移

<VirtualHost *:80>
    ServerName wordpress.example.com
    DocumentRoot /var/www/wordpress
    
    <Directory "/var/www/wordpress">
        AllowOverride All
        Require all granted
        
        # WordPress 伪静态
        <IfModule mod_rewrite.c>
            RewriteEngine On
            RewriteBase /
            RewriteRule ^index\.php$ - [L]
            RewriteCond %{REQUEST_FILENAME} !-f
            RewriteCond %{REQUEST_FILENAME} !-d
            RewriteRule . /index.php [L]
        </IfModule>
    </Directory>
</VirtualHost>

8.2 Laravel 迁移

<VirtualHost *:80>
    ServerName laravel.example.com
    DocumentRoot /var/www/laravel/public
    
    <Directory "/var/www/laravel/public">
        AllowOverride None
        Require all granted
        
        <IfModule mod_rewrite.c>
            RewriteEngine On
            RewriteCond %{REQUEST_FILENAME} !-d
            RewriteCond %{REQUEST_FILENAME} !-f
            RewriteRule ^ index.php [L]
        </IfModule>
    </Directory>
</VirtualHost>

9. 注意事项

  1. 充分测试:迁移前在测试环境全面测试
  2. 灰度发布:使用灰度发布降低风险
  3. 保留回滚:保留 Nginx 配置和环境
  4. 监控告警:迁移期间加强监控
  5. 性能验证:迁移后进行性能基准测试

10. 扩展阅读

11. 总结

从 Nginx 迁移到 Apache 需要:

  • 理解差异:掌握两者架构和配置差异
  • 配置映射:将 Nginx 配置正确转换为 Apache
  • 渐进迁移:使用灰度发布降低风险
  • 充分验证:功能、性能、安全全面验证
  • 保留回滚:保留快速回滚能力

Apache 在 .htaccess、动态模块、兼容性等方面有独特优势,合理迁移可以获得更好的运维体验。