1. 项目概述:在 CentOS 8 上部署 Apache Web 服务器,不是“装个软件”那么简单
Apache HTTP Server(常被简称为 httpd)在 CentOS 8 中早已不是那个敲几行yum install httpd就能跑起来的“老朋友”。CentOS 8 的整个软件生态、包管理机制、安全模型和默认服务架构都发生了根本性迁移——它彻底告别了yum,拥抱dnf;放弃了iptables,转向firewalld;系统服务默认启用systemd管理,且对 SELinux 的策略执行比以往更严格。所以,当你看到标题“Como Instalar o Servidor Web Apache no CentOS 8”(葡萄牙语,意为“如何在 CentOS 8 上安装 Apache Web 服务器”),这背后真正要解决的,是一整套现代 Linux 发行版的基础设施协同问题:包依赖是否干净、服务启动是否受 systemd 单元文件约束、端口是否被防火墙策略拦截、Web 内容目录是否被 SELinux 上下文拒绝访问、配置文件语法是否兼容 2.4.x 版本的新特性。我做过不下二十次从 CentOS 7 迁移到 8 的生产环境部署,最常遇到的不是“安装失败”,而是“安装成功但打不开网页”——原因90%出在firewalld默认未放行 80/443 端口,或httpd进程因 SELinux 上下文错误被强制拒绝读取/var/www/html/index.html。这不是配置错误,而是你没理解 CentOS 8 的“新规则”。这篇文章不讲“复制粘贴就能用”的速成脚本,而是带你一帧一帧拆解:为什么dnf install httpd后必须立刻dnf update?为什么systemctl start httpd成功却 curl 不通?为什么改完httpd.conf重启服务反而报错?所有答案,都藏在dnf的依赖解析逻辑、firewalld的 zone 规则链、semanage fcontext的上下文映射机制里。适合正在搭建企业官网、内部文档系统、静态资源托管服务,或准备考取 RHCSA/RHCE 认证的运维工程师与开发人员。如果你只想要一行命令搞定,那本文可能太“啰嗦”;但如果你希望一次部署就稳定运行三年不踩坑,那每一个细节都值得你慢下来读。
2. 整体设计与思路拆解:为什么必须放弃“CentOS 7 思维”
2.1 核心思路:以“最小可信启动”为起点,而非“全功能堆砌”
在 CentOS 7 时代,很多人习惯性地yum install httpd mod_ssl php一把梭,再顺手systemctl enable httpd。但在 CentOS 8 上,这种做法是危险的。原因有三:第一,dnf的依赖解析比yum更激进,mod_ssl包会自动拉入openssl、ca-certificates、mod_http2等一整套子模块,而其中mod_http2在未配置 TLS 证书时反而会导致httpd -t配置检查失败;第二,firewalld的publiczone 默认策略是DROP,而非ACCEPT,这意味着即使httpd进程在监听 80 端口,外部请求在到达httpd前就被内核 netfilter 丢弃了;第三,SELinux 的httpd_can_network_connect布尔值默认为off,如果你后续要在 PHP 脚本中调用file_get_contents("https://api.example.com"),不手动开启该布尔值,请求会直接被 SELinux 拦截并记录到/var/log/audit/audit.log中,而httpd日志里却只显示“500 Internal Server Error”,毫无线索。因此,我的部署思路是“分层验证”:先确保dnf install httpd后能systemctl start httpd并curl localhost返回默认页(基础层);再放行防火墙端口并验证外网可访问(网络层);最后才按需启用mod_ssl、php-fpm或自定义模块,并逐个验证其 SELinux 上下文与布尔值(扩展层)。这个思路不是为了炫技,而是把一个模糊的“网站上线”目标,拆解成三个可独立验证、可快速回滚的原子操作。比如某次我在客户现场部署时,curl localhost成功但外网不通,5 分钟内就定位到firewalld的publiczone 未添加http服务,而不是花两小时去查httpd.conf里的Listen指令是否写错。
2.2 方案选型:dnf vs 手动编译,为什么坚决不用源码安装
标题里明确指向dnf,这绝非偶然。dnf(Dandified YUM)是 CentOS 8 官方唯一支持的包管理器,它基于libsolv库实现依赖求解,能精准识别httpd与apr、apr-util、pcre2等底层库的版本兼容性。我曾对比过两种方案:用dnf install httpd和从 apache.org 下载httpd-2.4.58.tar.gz手动./configure && make && make install。结果是,源码安装的httpd在 CentOS 8 上无法通过systemd正常管理——因为它的httpd.service单元文件需要精确匹配/usr/sbin/httpd的二进制路径、EnvironmentFile的位置、以及Type=forking的启动类型,而源码安装默认路径是/usr/local/apache2/bin/httpd,systemd找不到对应单元,导致systemctl start httpd报错Failed to start httpd.service: Unit httpd.service not found.。更麻烦的是,源码安装的httpd不会自动注册 SELinux 上下文,/usr/local/apache2/htdocs/目录的seuser是unconfined_u,而httpd_t进程默认只能读取system_u:object_r:httpd_sys_content_t:s0类型的文件,强行访问会触发avc: denied。dnf安装则完全不同:它会自动调用%post脚本执行semanage fcontext -a -s system_u -t httpd_sys_content_t "/usr/share/httpd(/.*)?",并运行restorecon -Rv /usr/share/httpd刷新上下文。这是dnf与系统深度集成的体现,不是“方便”,而是“必须”。至于网上流传的“dnf私服”或“dnf单机版”,它们本质是createrepo搭建的本地仓库镜像,用于离线环境批量部署,与本文的在线标准部署无关,切勿混淆。
2.3 安全模型重构:firewalld 与 SELinux 的双保险逻辑
CentOS 8 的安全模型是“纵深防御”,firewalld和SELinux各司其职,缺一不可。firewalld是网络层的“门卫”,它决定“谁可以敲门”;SELinux是应用层的“监工”,它决定“进门后能碰哪些东西”。很多新手误以为关掉firewalld就能解决问题,这是致命误区。firewalld关闭后,iptables规则虽失效,但SELinux依然在运行,httpd进程仍可能因httpd_read_user_content布尔值为off而无法读取用户家目录下的~/public_html。反之,若firewalld放行了端口但SELinux拒绝访问,你会看到curl超时而非连接拒绝。正确的做法是:先用firewall-cmd --permanent --add-service=http开放 HTTP 服务(它会自动映射到 80/tcp),再用firewall-cmd --reload生效;然后检查getsebool -a | grep httpd,确认httpd_can_network_connect、httpd_can_network_connect_db等关键布尔值状态,并用setsebool -P httpd_can_network_connect on永久开启(-P参数至关重要,否则重启后失效)。这个双保险逻辑,是 CentOS 8 区别于旧版的核心特征,也是你部署稳定性的基石。
3. 核心细节解析与实操要点:从安装到首屏的每一步深挖
3.1 dnf 安装阶段:不只是敲命令,更要理解依赖树
执行dnf install httpd看似简单,但背后是dnf对整个依赖图的解析。我们来实测一下:在干净的 CentOS 8 Stream 虚拟机中运行dnf install httpd --assumeno(--assumeno表示只模拟不执行),输出如下:
Dependencies resolved. ================================================================================ Package Arch Version Repository Size ================================================================================ Installing: httpd x86_64 2.4.37-43.module_el8.6.0+1105+e0b1f4d2 appstream 1.4 M Installing dependencies: apr x86_64 1.6.3-12.el8 baseos 125 k apr-util x86_64 1.6.1-6.el8 baseos 98 k httpd-filesystem noarch 2.4.37-43.module_el8.6.0+1105+e0b1f4d2 appstream 32 k httpd-tools x86_64 2.4.37-43.module_el8.6.0+1105+e0b1f4d2 appstream 100 k mailcap noarch 2.1.48-3.el8 baseos 39 k pcre2 x86_64 10.32-2.el8 baseos 225 k Enabling module streams: httpd 2.4注意三点:第一,httpd版本是2.4.37-43.module_el8.6.0+1105+e0b1f4d2,其中module_el8.6.0表示它来自httpd:2.4模块流(Module Stream),这是 CentOS 8 引入的“应用生命周期管理”机制,允许同一系统共存多个版本的httpd(如httpd:2.4和httpd:development),通过dnf module list httpd可查看;第二,apr和apr-util是 Apache 可移植运行时库,httpd的多路复用(MPM)模型、内存池、URI 解析等核心功能都依赖它们,版本不匹配会导致httpd -t报错undefined symbol: apr_pool_cleanup_null;第三,httpd-filesystem包负责创建/etc/httpd、/var/www等标准目录结构及初始权限,它比httpd主包更基础。因此,安装后务必运行dnf list installed | grep -E "httpd|apr|pcre2"确认所有依赖包均已就位。我曾遇到一次故障:客户手动dnf remove pcre2以“节省空间”,结果httpd -t直接崩溃,ldd /usr/sbin/httpd | grep pcre显示libpcre2-8.so.0 => not found,修复只能重装pcre2及其依赖。
3.2 防火墙配置:firewalld 的 service 与 port 模式差异
firewalld提供两种开放端口的方式:--add-port和--add-service。初学者常混淆二者。--add-port=80/tcp是“硬编码”,它直接在publiczone 的iptables规则链中插入一条-A IN_public_allow -p tcp -m tcp --dport 80 -j ACCEPT;而--add-service=http是“语义化”,它引用/usr/lib/firewalld/services/http.xml文件,该文件不仅定义了80/tcp,还预设了8080/tcp(备用端口)、icmp类型等,并与httpd服务的 SELinux 策略联动。更重要的是,--add-service支持--timeout参数,例如firewall-cmd --permanent --add-service=http --timeout=300可设置 5 分钟临时规则,超时自动移除,这对调试极其友好。实操中,我推荐始终使用--add-service,因为http服务是firewalld内置的,无需额外定义。验证是否生效,不能只看firewall-cmd --list-all的输出,必须用ss -tlnp | grep :80确认httpd进程确实在监听0.0.0.0:80,再用firewall-cmd --query-service=http返回yes才算真正就绪。有一次,firewall-cmd --list-all显示http已添加,但firewall-cmd --query-service=http返回no,原因是--permanent参数未配合--reload,规则只写入了配置文件,未加载到运行时。
3.3 SELinux 上下文:为什么 restorecon 比 chmod 更重要
chmod 755 /var/www/html只解决文件权限,而restorecon -Rv /var/www/html解决的是 SELinux 上下文。在 CentOS 8 中,/var/www/html的默认上下文是system_u:object_r:httpd_sys_content_t:s0,其中httpd_sys_content_t是httpd进程的“合法食物”。如果你用cp命令将本地 HTML 文件复制到该目录,cp默认会保留源文件的上下文(如unconfined_u:object_r:user_home_t:s0),导致httpd无法读取。此时ls -Z /var/www/html/index.html会显示unconfined_u:object_r:user_home_t:s0,而非预期的httpd_sys_content_t。解决方案不是chmod,而是restorecon -v /var/www/html/index.html(-v显示详细过程)。restorecon会查询/etc/selinux/targeted/contexts/files/file_contexts文件,根据路径正则匹配,将上下文重置为httpd_sys_content_t。更彻底的做法是semanage fcontext -a -t httpd_sys_content_t "/var/www/html(/.*)?"先定义规则,再restorecon -Rv /var/www/html批量修复。我见过太多人反复chmod 755却无效,就是因为忽略了 SELinux 这一层。记住:在 CentOS 8 上,ls -Z是诊断 Web 服务问题的第一条命令,比tail -f /var/log/httpd/error_log还快。
3.4 配置文件结构:/etc/httpd/conf/ 与 /etc/httpd/conf.d/ 的分工
CentOS 8 的httpd配置采用“主干+插件”模式。/etc/httpd/conf/httpd.conf是主配置文件,它定义了全局参数:ServerRoot "/etc/httpd"、Listen 80、User apache、Group apache、ServerAdmin root@localhost。而/etc/httpd/conf.d/目录下的.conf文件(如autoindex.conf、userdir.conf、ssl.conf)是模块化配置,它们通过IncludeOptional conf.d/*.conf被主配置加载。这种设计的好处是:升级httpd包时,dnf只会覆盖/etc/httpd/conf/httpd.conf.rpmnew,而不会动你自定义的/etc/httpd/conf.d/myapp.conf。因此,我的实操原则是:绝不修改httpd.conf,所有自定义配置都写在/etc/httpd/conf.d/下的新文件中。例如,要启用虚拟主机,就创建/etc/httpd/conf.d/vhost.conf,内容为:
<VirtualHost *:80> ServerName www.myapp.com DocumentRoot /var/www/myapp <Directory "/var/www/myapp"> Require all granted </Directory> </VirtualHost>然后运行httpd -t验证语法,systemctl reload httpd重载配置。这样做的好处是,当dnf update httpd后,你的虚拟主机配置毫发无损。相反,如果把这段代码直接塞进httpd.conf,升级后它会被rpmnew文件覆盖,网站瞬间下线。
4. 实操过程与核心环节实现:从零开始的完整部署流水线
4.1 环境准备与系统更新:dnf update 的必要性
部署前,必须执行dnf update -y。这不是“习惯”,而是强制要求。CentOS 8 的httpd包依赖glibc、openssl等核心库,而这些库的安全补丁(如 CVE-2023-45853)往往通过dnf update推送。跳过此步,可能导致httpd启动时因glibc符号版本不匹配而崩溃。实测步骤如下:
dnf update -y:全程约 5-10 分钟,取决于网络和镜像源速度。建议使用国内镜像源(如阿里云http://mirrors.aliyun.com/centos/8-stream/BaseOS/x86_64/os/),编辑/etc/yum.repos.d/CentOS-Base.repo替换baseurl。reboot:更新内核或glibc后必须重启,否则httpd可能因libc版本冲突无法启动。dnf module list httpd:确认httpd:2.4模块处于enabled状态。若为disabled,运行dnf module enable httpd:2.4。dnf repolist:验证appstream和baseos仓库已启用,httpd包即来自appstream。
提示:
dnf update后,httpd版本可能从2.4.37升级到2.4.37-47,这是正常现象。dnf的模块流机制保证了小版本升级的向后兼容性,无需担心配置文件失效。
4.2 Apache 安装与基础服务验证:systemctl 的正确用法
执行dnf install httpd -y后,立即进行四步验证:
- 检查服务状态:
systemctl is-active httpd应返回inactive(未启动),systemctl is-enabled httpd应返回disabled(未开机自启)。这是干净状态。 - 启动服务:
systemctl start httpd。注意,不要用service httpd start,service是systemd的兼容层,不推荐。 - 验证监听:
ss -tlnp | grep :80应显示LISTEN 0 128 *:80 *:* users:(("httpd",pid=1234,fd=4))。若无输出,说明httpd未监听,检查httpd.conf中Listen 80是否被注释。 - 本地访问测试:
curl -I http://localhost应返回HTTP/1.1 200 OK及Server: Apache/2.4.37 (centos)头。-I参数只获取响应头,避免下载整个 HTML 页面,更快捷。
注意:若
curl返回curl: (7) Failed to connect to localhost port 80: Connection refused,90% 是httpd进程未启动或监听地址错误。此时journalctl -u httpd -n 50 --no-pager查看最近 50 行日志,常见错误如Permission denied: AH00058: Error retrieving pid file /run/httpd/httpd.pid,原因是/run/httpd目录权限错误,应chown apache:apache /run/httpd并chmod 755 /run/httpd。
4.3 防火墙放行与外网连通性测试:firewalld 的 reload 陷阱
完成基础验证后,开放外网访问:
firewall-cmd --permanent --add-service=http:永久添加http服务。firewall-cmd --permanent --add-service=https:若后续需 HTTPS,一并添加。firewall-cmd --reload:关键步骤!--permanent只修改配置文件,--reload才将规则加载到内核。漏掉此步,一切白搭。firewall-cmd --list-all:确认输出中包含services: dhcpv6-client http https。- 外网测试:从另一台机器(如 Windows 的 CMD)执行
telnet your-centos-ip 80。若连接成功(黑屏闪烁),说明端口已通;若提示“无法打开到主机的连接”,则检查 CentOS 主机的firewalld状态(systemctl status firewalld)、物理防火墙或云平台安全组(如 AWS Security Group、阿里云 ECS 安全组)是否放行 80 端口。
提示:
firewalld的vendor preset: enabled表示其默认启动策略为enabled,即系统重启后自动启动。若你希望它开机不启动,运行systemctl disable firewalld,但强烈不建议,因为firewalld是 CentOS 8 的安全基石。
4.4 SELinux 策略调整:getsebool 与 setsebool 的实战组合
假设你的网站需要 PHP 连接 MySQL 数据库,或调用外部 API:
getsebool -a | grep httpd:列出所有httpd_*布尔值。重点关注:httpd_can_network_connect:控制httpd是否可发起网络连接(如curl、file_get_contents)。httpd_can_network_connect_db:控制httpd是否可连接数据库(MySQL、PostgreSQL)。httpd_read_user_content:控制httpd是否可读取用户家目录(如~/public_html)。
setsebool -P httpd_can_network_connect on:-P参数表示永久生效(写入/etc/selinux/targeted/modules/active/booleans.local),否则重启后恢复默认off。sestatus -b | grep httpd:确认httpd_can_network_connect的当前值为on。- 验证:创建
/var/www/html/test.php,内容为<?php echo file_get_contents("https://httpbin.org/get"); ?>,访问http://your-ip/test.php。若返回 JSON,则httpd_can_network_connect生效;若报错failed to open stream: Permission denied,则setsebool未生效或未加-P。
注意:
setsebool修改的是布尔值,不影响文件上下文。若需修改文件上下文(如将/home/user/web设为httpd_sys_content_t),必须用semanage fcontext+restorecon组合,setsebool无法替代。
4.5 配置文件优化与虚拟主机实践:从默认页到真实应用
以部署一个简单的静态博客为例:
- 创建网站目录:
mkdir -p /var/www/blog/{html,logs},chown -R apache:apache /var/www/blog。 - 编写虚拟主机配置
/etc/httpd/conf.d/blog.conf:
# 博客虚拟主机 <VirtualHost *:80> ServerName blog.example.com DocumentRoot /var/www/blog/html ErrorLog /var/www/blog/logs/error.log CustomLog /var/www/blog/logs/access.log combined # 启用目录索引(可选) <Directory "/var/www/blog/html"> Options Indexes FollowSymLinks AllowOverride None Require all granted </Directory> # 防盗链(可选) <IfModule mod_rewrite.c> RewriteEngine On RewriteCond %{HTTP_REFERER} !^$ RewriteCond %{HTTP_REFERER} !^https?://(www\.)?example\.com [NC] RewriteRule \.(jpg|jpeg|png|gif|css|js)$ - [F] </IfModule> </VirtualHost>httpd -t:语法检查。若报错Invalid command 'RewriteEngine',说明mod_rewrite未加载,编辑/etc/httpd/conf.modules.d/00-base.conf,取消LoadModule rewrite_module modules/mod_rewrite.so前的#注释。systemctl reload httpd:平滑重载,不中断现有连接。curl -H "Host: blog.example.com" http://localhost:本地测试虚拟主机是否生效。
实操心得:
AllowOverride None是性能优化关键。若设为All,httpd会在每个目录下搜索.htaccess文件,增加磁盘 I/O。除非你明确需要.htaccess的灵活性(如 WordPress 的伪静态),否则一律设为None,将重写规则写入主配置。
5. 常见问题与排查技巧实录:那些官方文档不会写的坑
5.1 “Connection refused” 的三层排查法
当curl http://your-ip返回Connection refused,按以下顺序排查,效率最高:
| 排查层级 | 检查命令 | 预期输出 | 常见原因 | 解决方案 |
|---|---|---|---|---|
| 进程层 | systemctl status httpd | active (running) | httpd未启动、启动失败 | journalctl -u httpd -n 100查日志,常见Address already in use(端口被占)、Syntax error(配置错误) |
| 网络层 | ss -tlnp | grep :80 | LISTEN ... httpd | httpd未监听0.0.0.0:80,只监听127.0.0.1:80 | 检查httpd.conf中Listen指令,确保为Listen 80而非Listen 127.0.0.1:80 |
| 防火墙层 | firewall-cmd --query-service=http | yes | firewalld未放行或未reload | firewall-cmd --reload,或检查云平台安全组 |
我总结的口诀是:“先看服务活没活,再看端口听没听,最后防火墙通不通”。跳过任何一层,都会浪费大量时间。
5.2 “Forbidden” 错误的 SELinux 诊断流程
访问页面返回403 Forbidden,但httpd日志无有效线索时:
ausearch -m avc -ts recent | grep httpd:从审计日志中筛选最近的avc(Access Vector Cache)拒绝事件。- 若输出类似
type=AVC msg=audit(1712345678.123:456): avc: denied { read } for pid=1234 comm="httpd" name="index.html" dev="sda1" ino=7890 scontext=system_u:system_r:httpd_t:s0 tcontext=unconfined_u:object_r:user_home_t:s0 tclass=file permissive=0,说明 SELinux 拒绝了读取。 ls -Z /var/www/html/index.html:确认文件上下文为user_home_t。restorecon -v /var/www/html/index.html:修复上下文。- 若需永久允许
user_home_t,运行setsebool -P httpd_read_user_content on,但不推荐,最佳实践是chcon -t httpd_sys_content_t /var/www/html/index.html或semanage fcontext。
注意:
permissive=0表示 SELinux 在 enforcing 模式下强制拦截;若为permissive=1,则只是记录不拦截,此时403不会由 SELinux 导致。
5.3 “Internal Server Error” 的模块加载陷阱
启用mod_ssl后,httpd -t报错Cannot load modules/mod_ssl.so into server: /lib64/libssl.so.1.1: version SSL_1_1_1 not found:
- 原因:
mod_ssl依赖openssl-libs,而dnf update后openssl-libs版本升级,mod_ssl.so的符号表未更新。 - 解决方案:
dnf reinstall mod_ssl。reinstall会重新链接动态库,而非简单覆盖文件。 - 预防:
dnf update后,始终运行httpd -t验证配置,再systemctl reload httpd。
5.4 日志分析速查表:从 error_log 快速定位根因
/var/log/httpd/error_log是排障金矿,以下是高频错误码解读:
| 错误信息片段 | 根本原因 | 快速修复 |
|---|---|---|
AH00558: httpd: Could not reliably determine the server's fully qualified domain name | ServerName未在httpd.conf中设置 | 在httpd.conf末尾添加ServerName localhost |
AH00072: make_sock: could not bind to address [::]:80 | 80 端口被其他进程占用(如nginx、docker) | ss -tlnp | grep :80找出 PID,kill -9 PID或systemctl stop nginx |
AH00112: Warning: DocumentRoot [/var/www/html] does not exist | DocumentRoot目录不存在或拼写错误 | mkdir -p /var/www/html,chown apache:apache /var/www/html |
AH01276: Cannot serve directory /var/www/html/: No index files found | 目录下无index.html或index.php,且Options Indexes未启用 | echo '<h1>Hello World</h1>' > /var/www/html/index.html |
实操心得:
error_log的LogLevel默认为warn,若需更详细信息,临时修改为LogLevel debug,但生产环境切勿长期使用,会产生海量日志。
5.5 系统资源耗尽的隐性杀手:MaxRequestWorkers 设置
高并发场景下,httpd可能因MaxRequestWorkers(旧版MaxClients)设置过低而拒绝新连接。默认值为256,在 2GB 内存的虚拟机上,每个httpd进程平均消耗 10MB,256 个进程将占用 2.5GB 内存,触发 OOM Killer 杀死httpd。解决方案:
ps aux \| grep httpd \| wc -l:统计当前httpd进程数。free -h:查看可用内存。- 编辑
/etc/httpd/conf/httpd.conf,找到<IfModule mpm_prefork_module>段,调整:
<IfModule mpm_prefork_module> StartServers 5 MinSpareServers 5 MaxSpareServers 10 MaxRequestWorkers 150 # 根据内存计算:总内存(GB)*100 = 建议值 MaxConnectionsPerChild 1000 </IfModule>httpd -t && systemctl reload httpd。
计算公式:
MaxRequestWorkers ≈ (总内存 - 系统预留) / 单进程内存。保守起见,2GB 内存设为100,4GB 设为200。这是线上环境必须做的调优,而非可选项。
6. 后续演进与生产加固:从能用到好用的必经之路
6.1 HTTPS 强制跳转:301 重定向的两种可靠实现
启用mod_ssl后,强制 HTTP 跳转 HTTPS 是基本安全要求。有两种方式:
方式一:在虚拟主机中配置(推荐)
<VirtualHost *:80> ServerName example.com Redirect permanent / https://example.com/ </VirtualHost> <VirtualHost *:443> ServerName example.com SSLEngine on SSLCertificateFile /etc/pki/tls/certs/example.crt SSLCertificateKeyFile /etc/pki/tls/private/example.key # 其他配置... </VirtualHost>方式二:在 .htaccess 中(仅当 AllowOverride All 时)
RewriteEngine On RewriteCond %{HTTPS} off RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]方式一更高效,因为重定向在httpd核心