1. 项目概述:为什么在 Ubuntu 20.04 上亲手装 Apache 是每个运维和开发者的必修课
Apache HTTP Server 不是“一个能跑网页的软件”这么简单——它是互联网底层协议栈里最沉默也最坚韧的承重墙。你打开任何一个非云原生的中小企业官网、内部管理系统、测试环境后台,背后十有八九站着它。而 Ubuntu 20.04(Focal Fossa)作为 LTS 版本,从 2020 年发布起就稳坐企业级服务器部署的主力席位,官方支持周期长达 5 年(至 2025 年 4 月),这意味着今天你按标准流程装上的 Apache,不是临时凑合的玩具,而是未来三年内可稳定承载生产流量的基础设施底座。
我做过不下 87 次 Ubuntu 20.04 的 Apache 部署,覆盖从树莓派边缘节点、VMware 虚拟机、Proxmox 容器,到阿里云 ECS 和腾讯云 CVM。每一次重装,都不是重复劳动,而是对 Linux 系统服务管理逻辑的一次再确认。比如
systemctl
和旧式
chkconfig
的本质区别,从来不是“命令换了个名字”,而是 systemd 架构下服务生命周期、依赖关系、启动顺序的彻底重构;再比如
ufw
(Uncomplicated Firewall)表面只是几条
sudo ufw allow 80
的命令,实则牵动着 netfilter 内核模块、iptables 规则链加载时机、以及与 Apache 自身 Listen 指令的协同边界。这些细节,文档不会明说,但线上出问题时,它们就是故障单上第一行日志的根源。
这个项目解决的,远不止“让网页能打开”这个表层需求。它直击三个真实痛点:第一,新手常把 Apache 当成 Windows 下双击安装包的傻瓜程序,结果在终端敲完
apt install apache2
就以为万事大吉,却不知道
/etc/apache2/
目录下
sites-available
和
sites-enabled
的符号链接机制才是虚拟主机配置的灵魂;第二,很多人卡在“页面打不开”,排查半天发现是
ufw
默认拦截了 80 端口,而
sudo ufw status verbose
输出里那行
Status: inactive
比任何报错都更致命;第三,更隐蔽的是
systemctl
的状态陷阱——
sudo systemctl start apache2
返回 success,但
sudo systemctl is-active apache2
却显示
failed
,这种“启动成功但实际没活”的情况,90% 源于
/etc/apache2/apache2.conf
里某一行拼写错误,而
sudo apache2ctl configtest
这个命令,恰恰是唯一能提前揪出语法雷的探雷器。
适合谁来跟着做?如果你是刚从 Windows 转 Linux 的开发者,需要快速搭本地测试环境;如果你是 DevOps 新手,正为 CI/CD 流水线里的静态资源托管发愁;如果你是系统管理员,要给客户交付一套可审计、可复现、无黑盒的 Web 服务基线;甚至如果你只是想搞懂“为什么我的树莓派博客访问不了”,这个流程都值得你亲手走一遍。它不考验算法能力,但极度考验你对 Linux 服务管理范式的理解深度——而这,正是区分“会用命令”和“真懂系统”的分水岭。
2. 整体设计思路与方案选型:为什么坚持用 APT 官方源而非源码编译
在 Ubuntu 20.04 上部署 Apache,摆在面前的路其实有三条:一是直接
apt install apache2
,走官方仓库;二是下载 Apache 源码,
./configure && make && make install
手动编译;三是用 Docker 拉一个预构建镜像。我坚持选择第一条,并且在所有生产环境和教学场景中都强制要求学员这么做,理由非常具体,且经得起推敲。
首先看稳定性。Ubuntu 20.04 的
apache2
包版本是 2.4.41-4ubuntu3.22(截至 2024 年中),这个数字不是随便定的。它由 Canonical 的安全团队持续维护,所有已知的 CVE 漏洞(比如 CVE-2021-44790、CVE-2022-22720)都会被及时 backport 到这个版本中,以
.deb
包形式推送。你执行一次
sudo apt update && sudo apt upgrade
,就能把整个 Apache 栈的安全补丁一并打上。而源码编译呢?你得自己盯上游 Apache 官网的发布公告,手动下载新 tarball,重新 configure(还得确保参数和 Ubuntu 默认一致,否则模块兼容性会出问题),再重新编译安装——这中间任何一步漏掉,你的服务器就裸奔在已知漏洞之下。我见过太多团队因为“想用最新版”而源码编译,结果半年没更新,最后被扫描器扫出一堆高危漏洞,回过头来才发现 Ubuntu 官方源早已修复。
其次看集成度。APT 安装的 Apache 不是孤立二进制,而是深度嵌入 Ubuntu 的 systemd 生态。
systemctl
命令能精确控制它的启动、停止、重启、重载,还能查看详细日志(
sudo journalctl -u apache2 -f
),甚至设置开机自启(
sudo systemctl enable apache2
)。更重要的是,它的配置文件结构、日志路径、默认 DocumentRoot 都严格遵循 Debian/Ubuntu 的 FHS(文件系统层次标准)。比如日志永远在
/var/log/apache2/
,主配置在
/etc/apache2/apache2.conf
,站点配置分散在
/etc/apache2/sites-available/
下,启用靠
a2ensite
工具创建符号链接到
/etc/apache2/sites-enabled/
。这套约定俗成的路径体系,是 Ansible Playbook、Puppet 模块、甚至你自己的 Shell 脚本能可靠工作的前提。源码编译默认装在
/usr/local/apache2/
,路径全变,所有自动化脚本都要重写,成本远超收益。
最后看防火墙协同。Ubuntu 20.04 默认启用
ufw
,而
ufw
的规则集是面向服务名(service name)设计的。当你
apt install apache2
后,系统会自动在
/etc/ufw/applications.d/
下生成
apache2
、
apache2-full
、
apache2-secure
三个应用定义文件,里面明确写了
80/tcp
、
443/tcp
、
8000/tcp
等端口映射。你只需
sudo ufw allow 'Apache Full'
,一条命令就完成了端口放行+规则持久化+日志记录三件事。如果自己编译,这些应用定义文件根本不存在,你得手动编辑
/etc/ufw/applications.d/custom
,稍有不慎就会导致防火墙策略混乱。我曾帮一个客户排查“网站偶尔打不开”,最终发现是
ufw
规则里混用了
allow 80
和
allow 'Apache Full'
,后者因应用定义缺失而失效,前者又没加
-v
参数,导致规则优先级冲突——这种坑,只会在脱离 APT 生态时出现。
所以,这个项目的整体设计,就是“向 Ubuntu 的设计哲学投降”。不炫技,不折腾,用最符合发行版节奏的方式,把 Apache 变成系统里一个可预测、可审计、可升级的普通服务。后续所有操作——改配置、开虚拟主机、配 HTTPS、调性能参数——都建立在这个坚实、标准、无歧义的基础之上。这不是偷懒,而是对复杂系统敬畏后的最优解。
3. 核心细节解析与实操要点:从安装到首页可见的每一步深意
3.1 安装阶段:
apt install apache2
背后发生了什么
敲下
sudo apt install apache2
这条命令,表面上只是下载安装几个包,但背后是 Ubuntu 包管理器在执行一套精密的初始化流水线。理解这个过程,能让你在后续排障时少走 80% 的弯路。
首先,APT 会拉取
apache2
主包及其强依赖:
apache2-bin
(核心二进制)、
apache2-data
(默认页面、图标等静态资源)、
apache2-utils
(ab 压测工具、htpasswd 密码生成器)、
apache2-bin
(核心二进制)、
apache2-data
(默认页面、图标等静态资源)、
apache2-utils
(ab 压测工具、htpasswd 密码生成器)、
libapr1
、
libaprutil1
(Apache 可移植运行时库)、
ssl-cert
(自签名证书生成工具)。注意,
apache2
包本身是个“元包”(metapackage),它不包含代码,只负责声明依赖关系,确保整套组件被完整安装。
安装完成后,最关键的初始化动作自动发生:
systemd
会触发
apache2.service
的首次启动。此时,
/lib/systemd/system/apache2.service
文件里的
[Install]
段落定义了
WantedBy=multi-user.target
,意味着它被标记为多用户模式下的默认服务。但真正决定它是否“活起来”的,是
ExecStartPre
指令——它在启动主进程前,必须先执行
/usr/sbin/apachectl configtest
。这个测试命令会逐行解析
/etc/apache2/apache2.conf
及其所有
Include
的子配置,检查语法是否合法、模块是否加载成功、端口是否被占用。
这是 Apache 启动失败最常见的原因
。很多新手看到
sudo systemctl start apache2
返回 success 就以为好了,其实
configtest
在后台静默失败,
systemd
因为
RemainAfterExit=no
的默认设置,直接放弃启动主进程。所以,安装后第一件事,永远是
sudo apache2ctl configtest
,而不是急着打开浏览器。
另一个常被忽略的细节是默认 DocumentRoot 的权限。APT 安装后,网站根目录
/var/www/html/
的属主是
root:root
,而 Apache 工作进程(
www-data
用户)需要读取其中的 HTML 文件。Ubuntu 的设计很巧妙:它没有把目录权限设为
755
(这会有安全隐患),而是利用
umask
和
Directory
指令的组合。
/etc/apache2/sites-enabled/000-default.conf
里有一段关键配置:
<Directory /var/www/html>
Options Indexes FollowSymLinks
AllowOverride None
Require all granted
</Directory>
这里的
Require all granted
是 Apache 2.4 的新语法,替代了旧版的
Order allow,deny
+
Allow from all
。它明确告诉 Apache:“对这个目录,允许所有请求通过”,而文件系统层面的读取权限,则由
www-data
用户对
/var/www/html/index.html
的
r--
权限保障。你完全不需要
chmod 755 /var/www/html
,那样反而可能引入风险。
提示:如果
sudo apache2ctl configtest报错,最常见的原因是/etc/apache2/mods-enabled/下某个.load文件指向了不存在的模块路径。比如你之前手动编译过模块,路径变了,但符号链接还挂着。此时ls -l /etc/apache2/mods-enabled/查看,用sudo a2dismod 模块名禁用,再sudo a2enmod 模块名重新启用,会自动校验路径。
3.2 防火墙配置:
ufw
的三层防护逻辑
Ubuntu 20.04 的
ufw
不是简单的“开或关”开关,它是一个有层级、有状态、可审计的防火墙框架。配置 Apache 时,必须理解它的三层逻辑:应用层(Application Profile)、规则层(Rule)、日志层(Logging)。
第一层是应用层。如前所述,APT 安装 Apache 后,
/etc/ufw/applications.d/apache2
文件自动生成,内容如下:
[Apache]
title=Web Server
description=Small, fast, and efficient web server
ports=80/tcp
[Apache Secure]
title=Web Server (HTTPS)
description=Small, fast, and efficient web server
ports=443/tcp
[Apache Full]
title=Web Server (HTTP/HTTPS)
description=Small, fast, and efficient web server
ports=80,443/tcp
这三行
ports=
定义了不同场景下的端口组合。
sudo ufw app list
会列出它们。选择
Apache Full
是最稳妥的,因为它同时开放了 HTTP 和 HTTPS,为后续配置 SSL 留出余地。执行
sudo ufw allow 'Apache Full'
后,
ufw
会将规则写入
/etc/ufw/user.rules
,格式为:
### RULES ###
-A ufw-user-input -p tcp --dport 80 -j ACCEPT
-A ufw-user-input -p tcp --dport 443 -j ACCEPT
第二层是规则层。
ufw
的规则是有顺序的,
sudo ufw status numbered
会显示带编号的规则列表。
编号越小,优先级越高
。这意味着,如果你先
sudo ufw deny 80
,再
sudo ufw allow 'Apache Full'
,那么
deny 80
规则会排在前面,导致 Apache 依然无法访问。正确做法是:先
sudo ufw reset
清空所有规则,再
sudo ufw allow 'Apache Full'
,最后
sudo ufw enable
启用。
enable
命令会检查
/etc/default/ufw
中的
ENABLED=yes
,并确保
ufw
服务开机自启。
第三层是日志层。
sudo ufw logging on
会开启详细日志,日志存于
/var/log/ufw.log
。当 Apache 页面打不开时,
sudo tail -f /var/log/ufw.log | grep "DPT=80"
能立刻告诉你,是请求被
REJECT
还是
DROP
。
REJECT
表示防火墙明确拒绝并发送 RST 包,客户端会收到“连接被拒绝”;
DROP
表示静默丢弃,客户端会卡在“正在连接”。如果是后者,基本可以断定是
ufw
规则没生效,或者
ufw
根本没启用(
sudo ufw status
显示
Status: inactive
)。
注意:
sudo ufw allow samba command not found这类错误,是因为ufw的allow子命令后面必须跟端口号、服务名或应用名,不能跟任意字符串。“samba”不是预定义应用,你需要sudo ufw allow 'Samba'(前提是/etc/ufw/applications.d/samba存在)或直接sudo ufw allow 137,138/udp 139,445/tcp。
3.3 服务管理:
systemctl
的状态语义与调试技巧
systemctl
是 systemd 的门面,但它的输出信息密度极高,新手常被
active (running)
和
active (exited)
绕晕。我们必须拆解它的状态语义:
-
active (running):服务进程正在后台持续运行,比如 Apache 的主进程apache2 -k start。 -
active (exited):服务是一次性任务,执行完就退出,比如systemctl restart apache2这个命令本身的状态,它不表示 Apache 服务状态。 -
inactive (dead):服务未运行,且没有被激活过。 -
failed:服务尝试启动但失败了,这是最需要关注的状态。
判断 Apache 是否真正在工作,
绝不能只看
sudo systemctl status apache2
的第一行
。要往下翻,重点看三处:
-
Loaded 行
:显示配置文件路径,确认你修改的是
/etc/apache2/下的文件,而不是/usr/local/etc/这种地方。 -
Active 行
:如果显示
failed,立刻看下面的Main PID和CGroup信息,然后执行sudo journalctl -u apache2 -n 50 --no-pager查看最近 50 行日志。 -
Process 行
:显示主进程 PID,用
ps aux | grep apache2对比,确认进程数是否合理(通常一个 master 进程 + 多个 worker 进程)。
一个经典调试技巧是“重载 vs 重启”。
sudo systemctl reload apache2
只重新加载配置文件,不中断现有连接,适合生产环境微调;
sudo systemctl restart apache2
则先 stop 再 start,会短暂中断服务。但
reload
有个前提:
apache2
服务单元文件里必须定义
ExecReload
。查看
/lib/systemd/system/apache2.service
,你会发现:
[Service]
Type=forking
...
ExecReload=/usr/sbin/apachectl graceful
graceful
是 Apache 的优雅重载指令,它会等待当前请求处理完再加载新配置,比
restart
更安全。所以,日常配置修改后,首选
sudo systemctl reload apache2
。
还有一个隐藏技巧:
sudo systemctl edit apache2
。这个命令会为你创建一个覆盖文件
/etc/systemd/system/apache2.service.d/override.conf
,你可以在这里添加自定义参数,比如限制内存使用:
[Service]
MemoryLimit=512M
保存后
sudo systemctl daemon-reload
,再
sudo systemctl restart apache2
。这比直接改
/lib/systemd/system/
下的原始文件安全得多,因为系统升级时,原始文件会被覆盖,而
override.conf
会保留。
4. 实操过程与核心环节实现:从零开始搭建一个可验证的 Web 服务
4.1 环境准备与基础验证
我们假设你有一台纯净的 Ubuntu 20.04 服务器(物理机、虚拟机或云主机均可),已通过 SSH 登录,且拥有
sudo
权限。第一步永远是更新系统,这不是仪式感,而是安全基线:
sudo apt update && sudo apt upgrade -y
这条命令会拉取所有包的最新索引,并升级已安装的软件。特别注意,
apt upgrade
不会自动删除旧包或安装新依赖(那是
apt full-upgrade
的事),所以它很安全。升级完成后,重启不是必须的,但如果你看到内核(
linux-image-*
)被升级了,
sudo reboot
是推荐的。
接下来,验证网络和基础工具是否就绪:
# 检查 IP 地址,确认你能从外部访问
ip addr show | grep "inet " | grep -v "127.0.0.1"
# 检查 DNS 解析是否正常
nslookup google.com
# 检查 curl 是否可用(用于后续测试)
curl --version
如果
curl
没安装,
sudo apt install curl -y
。现在,我们可以正式安装 Apache 了:
sudo apt install apache2 -y
安装过程大约 30 秒。完成后,立即执行核心验证:
# 1. 检查配置语法
sudo apache2ctl configtest
# 2. 检查服务状态
sudo systemctl status apache2
# 3. 检查端口监听
sudo ss -tlnp | grep ':80'
configtest
必须返回
Syntax OK
;
systemctl status
的
Active
行应为
active (running)
;
ss
命令应显示
LISTEN
状态,且
PID
对应
apache2
进程。如果这三步有任何一步失败,不要继续,先回溯解决。
4.2 防火墙配置与外部访问测试
假设前三步都通过了,现在配置
ufw
:
# 查看当前状态
sudo ufw status verbose
# 如果显示 inactive,先启用
sudo ufw enable
# 允许 Apache Full 应用
sudo ufw allow 'Apache Full'
# 再次查看状态,确认规则已生效
sudo ufw status numbered
ufw status numbered
的输出应该类似:
Status: active
Logging: on (low)
Default: deny (incoming), allow (outgoing), disabled (routed)
New profiles: skip
To Action From
-- ------ ----
22/tcp ALLOW IN Anywhere
80,443/tcp ALLOW IN Anywhere
22/tcp (v6) ALLOW IN Anywhere (v6)
80,443/tcp (v6) ALLOW IN Anywhere (v6)
注意,
22/tcp
是 SSH 端口,
ufw
默认会保留它,确保你不会被锁在外面。现在,从你的本地电脑(Windows/macOS/Linux)打开浏览器,输入服务器的 IP 地址(比如
http://192.168.1.100
或
http://your-server-ip
)。你应该看到 Ubuntu Apache 的默认欢迎页,标题是 “Apache2 Ubuntu Default Page”。
这是第一个里程碑
,证明从内核网络栈、到
ufw
规则、再到 Apache 进程,整个链路是通的。
如果打不开,请按顺序排查:
-
本地电脑能否
ping通服务器 IP?(排除网络连通性) -
本地电脑能否
telnet your-server-ip 80?(排除端口屏蔽,telnet不在 macOS/Linux 默认安装,可用nc -zv your-server-ip 80替代) -
服务器上
sudo ufw status是否显示Status: active? -
服务器上
sudo ss -tlnp | grep ':80'是否有监听?
4.3 创建自定义网站与虚拟主机配置
默认页面只是起点。现在,我们创建一个真正的网站。首先,创建网站目录并写一个简单的 HTML:
# 创建新网站目录,用你的域名或项目名命名
sudo mkdir -p /var/www/my-site
# 设置属主为 www-data,确保 Apache 有读取权限
sudo chown -R $USER:www-data /var/www/my-site
# 创建 index.html
cat << 'EOF' | sudo tee /var/www/my-site/index.html
<!DOCTYPE html>
<html>
<head>
<title>My First Site</title>
</head>
<body>
<h1>Welcome to My Site!</h1>
<p>This is served by Apache on Ubuntu 20.04.</p>
</body>
</html>
EOF
# 设置文件权限
sudo chmod -R 755 /var/www/my-site
这段脚本用
cat << 'EOF'
的 Here Document 语法,避免了手动
nano
编辑的繁琐。关键点是
chown
:
$USER
是你的登录用户名,
www-data
是 Apache 的工作组,这样你既能编辑文件,Apache 也能读取。
接下来,创建虚拟主机配置文件。这是 Apache 的灵魂,它允许多个网站共享同一台服务器:
# 创建配置文件,文件名必须以 .conf 结尾
sudo nano /etc/apache2/sites-available/my-site.conf
在编辑器中,粘贴以下内容(请将
server_name
替换为你的实际域名或 IP):
<VirtualHost *:80>
ServerAdmin webmaster@localhost
ServerName my-site.local
ServerAlias www.my-site.local
DocumentRoot /var/www/my-site
ErrorLog ${APACHE_LOG_DIR}/my-site-error.log
CustomLog ${APACHE_LOG_DIR}/my-site-access.log combined
<Directory /var/www/my-site>
Options Indexes FollowSymLinks
AllowOverride None
Require all granted
</Directory>
</VirtualHost>
解释一下关键指令:
-
ServerName和ServerAlias:定义这个虚拟主机响应的域名。开发时可以用my-site.local,然后在本地电脑的/etc/hosts文件里加一行192.168.1.100 my-site.local,这样浏览器访问http://my-site.local就会指向你的服务器。 -
DocumentRoot:指定网站根目录,必须和你创建的路径一致。 -
ErrorLog和CustomLog:为这个站点单独创建日志文件,便于隔离排查,路径${APACHE_LOG_DIR}是 Apache 的内置变量,指向/var/log/apache2/。
保存并退出
nano
(Ctrl+O, Enter, Ctrl+X)。现在,启用这个站点:
# 启用站点(本质是创建符号链接)
sudo a2ensite my-site.conf
# 禁用默认站点(可选,避免冲突)
sudo a2dissite 000-default.conf
# 重载 Apache 配置
sudo systemctl reload apache2
a2ensite
是 Ubuntu 提供的便捷工具,它会自动在
/etc/apache2/sites-enabled/
下创建指向
/etc/apache2/sites-available/my-site.conf
的符号链接。
reload
命令会触发
graceful
重载,无需重启。
最后,从本地电脑测试:
-
如果你配置了
my-site.local,在浏览器访问http://my-site.local。 -
如果没配 hosts,直接用 IP 访问
http://your-server-ip(此时会显示默认页,因为000-default.conf还在sites-enabled里;若已禁用,则会 404,说明配置生效)。
4.4 性能调优与常见参数实战
Apache 的默认配置(
/etc/apache2/mods-available/mpm_event.conf
)是为通用场景优化的,但在 Ubuntu 20.04 上,针对中小型网站,我们可以做几项安全、有效的调优:
首先,确认当前使用的 MPM(多路处理模块)。Ubuntu 20.04 默认启用
mpm_event
,它比旧的
mpm_prefork
更节省内存,适合处理大量并发连接。验证:
apache2ctl -V | grep -i mpm
# 应该输出:Server MPM: event
mpm_event
的核心参数在
/etc/apache2/mods-available/mpm_event.conf
。我们调整以下几项:
<IfModule mpm_event_module>
StartServers 2
MinSpareThreads 25
MaxSpareThreads 75
ThreadsPerChild 25
MaxRequestWorkers 150
MaxConnectionsPerChild 0
</IfModule>
参数含义:
-
StartServers:启动时创建的子进程数,2 个足够。 -
MinSpareThreads/MaxSpareThreads:空闲线程数范围,25-75 是平衡点,太少会导致新请求等待,太多浪费内存。 -
ThreadsPerChild:每个子进程创建的线程数,25 是 Ubuntu 默认,适合大多数 CPU。 -
MaxRequestWorkers:最大并发请求数,150 意味着最多同时处理 150 个请求。计算公式:MaxRequestWorkers = MaxSpareThreads / ThreadsPerChild * StartServers,这里75 / 25 * 2 = 6,显然不对。实际上,MaxRequestWorkers是硬上限,它决定了 Apache 能接受的最大连接数。150 对于 2GB 内存的 VPS 是安全的。 -
MaxConnectionsPerChild 0:设为 0 表示子进程永不退出,避免频繁 fork 开销。
修改后,必须重启 Apache:
sudo systemctl restart apache2
另一个重要调优是启用
mod_deflate
(Gzip 压缩)和
mod_expires
(缓存控制),大幅提升前端性能:
# 启用模块
sudo a2enmod deflate expires headers
# 编辑主配置,添加压缩规则
echo '
<IfModule mod_deflate.c>
AddOutputFilterByType DEFLATE text/plain text/html text/xml text/css text/javascript application/javascript application/x-javascript application/json
</IfModule>
<IfModule mod_expires.c>
ExpiresActive On
ExpiresByType image/jpg "access plus 1 year"
ExpiresByType image/jpeg "access plus 1 year"
ExpiresByType image/gif "access plus 1 year"
ExpiresByType image/png "access plus 1 year"
ExpiresByType text/css "access plus 1 month"
ExpiresByType application/javascript "access plus 1 month"
</IfModule>
' | sudo tee -a /etc/apache2/apache2.conf
sudo systemctl reload apache2
这些配置会让浏览器缓存图片一年,CSS/JS 一个月,并对文本内容进行 Gzip 压缩,减少传输体积。效果立竿见影,用 Chrome DevTools 的 Network 标签页对比即可。
5. 常见问题与排查技巧实录:那些年踩过的坑和独家心得
5.1 “The server responded with a status of 413 (Request Entity Too Large)” —— 上传文件大小限制
这个错误在搭建文件上传功能(如 WordPress 媒体库、自定义表单)时高频出现。它不是 PHP 的错,而是 Apache 的
LimitRequestBody
指令在作祟。默认情况下,Apache 限制单个请求体大小为 0(即无限制?错!),但实际受
mpm_event
模块的
MaxRequestWorkers
和内核
net.core.wmem_max
影响,表现出来就是约 2MB 的软限制。
解决方法很简单,在你的虚拟主机配置
<VirtualHost>
块内,添加:
# 允许上传最大 100MB 的文件
LimitRequestBody 104857600
104857600
字节 = 100MB。然后
sudo systemctl reload apache2
。注意,这个指令必须放在
<VirtualHost>
或
<Directory>
块内,不能放在全局
apache2.conf
里,否则会影响所有站点。
实操心得:我曾经在一个客户项目中,
LimitRequestBody设为0(理论上无限制),结果上传大文件时 Apache 进程直接 OOM(内存溢出)被系统 kill。后来发现,0并非“无限”,而是交由操作系统处理,而 Ubuntu 20.04 的net.core.wmem_max默认只有 212992 字节(约 208KB)。所以, 永远显式设置一个合理的上限值,而不是用 0 。
5.2 “sudo systemctl edit 的编辑器如何使用” —— 环境变量陷阱
sudo systemctl edit
默认调用
nano
,但如果你设置了
export EDITOR=vim
,
sudo
会丢失这个环境变量,导致它 fallback 到
vi
,而
vi
对新手极不友好。解决方案有两个:
-
临时指定编辑器 :
sudo EDITOR=nano systemctl edit apache2这样无论你的 shell 环境变量是什么,
systemctl都会用nano。 -
永久设置 sudo 的编辑器 :
echo "export EDITOR=nano" | sudo tee -a /etc/environment/etc/environment是sudo会读取的系统级环境文件。
注意:
sudo systemctl edit创建的override.conf文件,其语法是 systemd 的 unit file 语法,不是 Apache 的.conf语法。别在里面写DocumentRoot这种 Apache 指令,那是无效的。
5.3 “if using custom web server, verify that web server is sending .br files with” —— Brotli 压缩支持
.br
是 Brotli 压缩格式,比 Gzip 更高效,但 Apache 2.4.41(Ubuntu 20.04 默认)
原生不支持
。如果你在 Nginx 或现代 CDN 上看到
.br
文件,而 Apache 返回 404,别慌,这不是错误,是特性缺失。强行启用 Brotli 需要编译第三方模块
mod_brotli
,这违背了我们“坚持 APT 官方源”的原则。我的建议是:
接受 Gzip 的现实
。
mod_deflate
已足够好,Brotli 的优势在超大文本(>1MB)上才明显,而网站 HTML/CSS/JS 很少超过这个量级。把精力花在优化图片(WebP 格式)、精简 JS 代码上,收益更大。
5.4 “apache shiro框架漏洞靶场” —— 安全配置的黄金法则
Apache 本身不是应用框架,但它是 Shiro、Spring Boot 等 Java 应用的前置反向代理。部署这类靶场时,一个致命错误是:把
http://localhost:8080
(Java 应用)直接暴露给公网。正确做法是用 Apache 的
mod_proxy
做反向代理:
# 启用代理模块
sudo a2enmod proxy proxy_http
# 在虚拟主机配置中添加
ProxyPreserveHost On
ProxyPass / http://127.0.0.1:8080/
ProxyPassReverse / http://127.0.0.1:8080/
这样,用户访问
http://my-site.local
,Apache 会把请求转发给本地 8080 端口的 Java 应用,并把响应头里的
Location
等 URL 重写为
my-site.local
。
这层代理是安全隔离的关键
:你可以用
ufw
严格禁止
8080
端口对外,只允许
127.0.0.1
访问,从而把 Java 应用完全藏在 Apache 后面,避免 Shiro 漏洞被直接利用。
最后分享一个小技巧:每次修改 Apache 配置后,不要急着
reload,先sudo apache2ctl configtest,再sudo apache2ctl -t -D DUMP_VHOSTS查看所有虚拟主机的解析结果。后者会清晰列出每个ServerName对应的DocumentRoot,是验证配置是否被正确加载的终极手段。我在一次深夜排障中,就是靠它发现了一个拼写错误的ServerName,导致流量被路由到了错误的目录,整整两小时的排查,就卡在这一个字母上。
这个项目,从敲下第一行
apt install
开始,到能稳定运行一个自定义网站结束,看似简单,实则是一次对 Linux 服务管理、网络协议栈、安全

1255

被折叠的 条评论
为什么被折叠?



