☰
Linux Apache HTTP Server Directory 指令中 Options 参数有哪些核心选项
2026/10/5 13:24:03 网站建设 项目流程

前言

<Directory>段里几乎每个配置模板都出现过Options Indexes FollowSymLinks这一行,但真正问「Indexes到底开的是什么、FollowSymLinks关掉之后为什么 RewriteRule 会失效、+和-前缀到底改了谁」时,能答全的人并不多。这些选项名长得像形容词,看不出背后挂的是哪个模块,于是很容易被当成「样板文字」原样复制。

顺便澄清一个术语:按 Apache 官方文档的分类,<Directory>属于「配置段(Sections)」而不是「指令(Directives)」,真正的指令是它内部的Options、Require、AllowOverride这些。标题里把两者合称不影响理解,但在查手册时按这个分类去找,能少走弯路。

先明确一个容易混淆的前提:Options不是访问控制。它管的是「这个目录里允许哪些功能被启用」,比如能不能列目录、能不能跟随符号链接、能不能跑 CGI;「谁能访问」是Require的职责。很多人遇到 403 时在Options上来回改,方向从一开始就错了——Options -Indexes会让没有索引文件的目录返回 403,但它并不会拒绝一个有索引文件的目录。

本文逐个说明这九个选项各自依赖哪个模块、开启后具体改变什么行为、以及在生产环境里该开还是该关。示例基于 RHEL 9(httpd 2.4.57)与 Debian 12(apache2 2.4.62),两边的默认配置文件差异会直接贴出来对照。

一、Options 的默认值与作用域

语法:

Options [+|-]option [[+|-]option] ...

上下文:server config、virtual host、directory、.htaccess;覆盖权限:Options(也就是.htaccess里能不能改它,由AllowOverride决定)。

关于默认值,Apache 2.4 官方文档标注为FollowSymLinks(2.2 时代标注的是All)。但不要依赖文档里的默认值,实际部署中它一定会被上层配置覆盖。你机器上真正生效的值,是各发行版自带配置与你自己写的段合并后的结果:

# Debian 12 /etc/apache2/apache2.conf(片段) <Directory /> Options FollowSymLinks AllowOverride None Require all denied </Directory> <Directory /var/www/> Options Indexes FollowSymLinks AllowOverride None Require all granted </Directory>
# RHEL 9 /etc/httpd/conf/httpd.conf(片段) <Directory /> AllowOverride none Require all denied </Directory> <Directory "/var/www"> AllowOverride None Require all granted </Directory> <Directory "/var/www/html"> Options Indexes FollowSymLinks AllowOverride None Require all granted </Directory>

注意 RHEL 的<Directory />段里没有Options行,Debian 的有。这就是同一份站点配置在两台机器上表现不同的典型来源,所以写配置时永远显式写清楚。

二、九个可选值逐条过

选项依赖模块开启后发生什么生产建议
All—除MultiViews外的全部选项都打开不要用
None—全部选项关闭需要的目录单独加
ExecCGImod_cgi / mod_cgid该目录下的脚本可被当作 CGI 执行只在确切的 CGI 目录开
FollowSymLinkscore允许跟随符号链接指向的路径2.4 的常见默认,可用
SymLinksIfOwnerMatchcore仅当符号链接与目标的属主一致时才跟随多租户场景更安全,有额外开销
Includesmod_include允许服务端包含(SSI)一般只开IncludesNOEXEC
IncludesNOEXECmod_include允许 SSI,但禁用#exec cmd与#exec cgi需要 SSI 时的推荐值
Indexesmod_autoindex目录没有索引文件时生成文件列表默认关掉
MultiViewsmod_negotiation内容协商,URL 可省略扩展名与语言后缀默认关掉

几点补充:


  • All不含MultiViews,这不是笔误,官方文档就是这么定义的。也就是说Options All并不会把MultiViews带上。

  • None是「全关」的简写,后续可以用+单独加回来:Options None后跟Options +FollowSymLinks,效果是只开这一个。

  • 依赖模块没加载时,写这些选项不会报错,只是完全没效果。这是「配置写了却像没生效」的经典来源:ExecCGI写了但mod_cgid没加载,脚本会被当普通文件原样吐出来。用apachectl -M(Debian 是apache2ctl -M)确认模块状态。

  • Indexes打开等于把目录结构暴露给任何人。如果那个目录里恰好没有DirectoryIndex指向的索引文件,访客就会看到完整文件列表,甚至包括.bak、.sql这类不该外露的文件。


三、+/-前缀与继承:最容易踩的地方

这是Options最反直觉的一条规则:

写了Options但不带+/-前缀时,是整体替换上层继承来的值;带了前缀则是在该层已有值上增删。

举例说明。假设上层已经生效的是Options Indexes FollowSymLinks:

# 子目录段: <Directory "/srv/site/upload"> Options -Indexes # ✅ 结果:FollowSymLinks + 去掉 Indexes </Directory> <Directory "/srv/site/upload"> Options FollowSymLinks # ✅ 结果:只有 FollowSymLinks,Indexes 被整体替换掉 </Directory> <Directory "/srv/site/upload"> Options -Indexes +ExecCGI # ✅ 结果:FollowSymLinks + ExecCGI </Directory>

第三种写法才是「只想改一点点」时的正确姿势。第二种写法虽然结果碰巧对,但它的语义是「这个目录的选项就是这样子」,将来上层加了什么,这里都不会跟着变——这正是模板复制粘贴后行为不一致的根源。

还有一条同样反直觉的规则:Options的替换是「最近的显式设置胜出」,而不是逐项合并。子目录里写Options None,父级的FollowSymLinks就没了,两者不会取并集。想让某目录既保留父级又加点东西,必须用+。

四、FollowSymLinks 与 SymLinksIfOwnerMatch 的差别

这两个选项容易被认为「差不多,后者更严」,实际差别在语义和开销两方面。


  • FollowSymLinks:Apache 跟随符号链接,不做任何额外校验。速度最快。

  • SymLinksIfOwnerMatch:只有当符号链接本身的属主与它指向目标的属主相同时才跟随。Apache 需要为路径上的每个分量额外做一次lstat检查,这会带来额外的系统调用开销(这一点官方文档明确写过),在高并发静态站点上不是零成本。


更关键的是它与mod_rewrite的关系:.htaccess里的按目录重写规则要求FollowSymLinks处于开启状态。如果你为了安全把Options设成None或只用SymLinksIfOwnerMatch,RewriteRule会直接失效且不总是给出直观报错。这是「重写规则昨天还好好的,今天不生效」的经典成因。

安全上还有个组合陷阱:

# ❌ 危险组合:AllowOverride 开了 All,又允许 FollowSymLinks Options FollowSymLinks AllowOverride All

当.htaccess可被上传(比如用户可写目录、文件上传功能),攻击者放进.htaccess就能借助 PHP 等处理器读取任意文件、改变重写目标,符号链接的校验形同虚设。所以开启AllowOverride的目录,务必收紧Options。

五、按场景给三套配方

# 场景 A:普通静态站点(推荐) <Directory "/srv/site/public"> Options -Indexes +FollowSymLinks AllowOverride None Require all granted </Directory>
# 场景 B:需要 SSI,但绝不允许执行命令 <Directory "/srv/site/ssi"> Options -Indexes +FollowSymLinks +IncludesNOEXEC AllowOverride None Require all granted AddOutputFilter INCLUDES .shtml </Directory>
# 场景 C:CGI 目录(范围必须收窄,否则整个站都能执行脚本) <Directory "/srv/site/cgi-bin"> Options -Indexes +ExecCGI AllowOverride None Require all granted AddHandler cgi-script .cgi .pl </Directory>

CGI 目录这一条要特别强调:ExecCGI是目录级的开关,只要这个目录里出现可执行的脚本文件,不管扩展名是什么、只要被AddHandler认出来就会被执行。把一个宽泛的目录(比如整个站点根)设成+ExecCGI,等于把站点变成任意脚本执行环境。

常见坑点


  • ❌ 子目录里写Options FollowSymLinks想「在父级基础上加上它」。


✅ 不带+是整体替换,父级的Indexes等设置会被一并丢掉。要增量就写Options +FollowSymLinks。


  • ❌ 遇到 403 就去改Options,在各个值之间来回试。


✅ 先看 error_log 里的错误码:AH01630 client denied by server configuration是Require的问题,AH01276才是Options/DirectoryIndex的问题,AH00035是文件系统权限问题。三者互不相干。


  • ❌ 以为Options None就等于「绝对安全」。


✅None会同时关掉FollowSymLinks,从而让.htaccess中的按目录重写规则失效;同时也不阻止其他段里的Require all granted。安全和功能要按目录分别权衡,而不是全局一刀切。


  • ❌ 把Options Indexes留在生产配置里。


✅ 没有索引文件的目录会变成文件列表页,.git、备份文件、临时文件都可能被索引。显式写-Indexes,并确保每个站点的DocumentRoot下有正确的DirectoryIndex目标文件。


  • ❌ 只写Options -Indexes,却没意识到某些目录本身就没有索引文件。


✅ 这样访客得到的是 403 而不是页面。要么补索引文件,要么这些目录本就该 404,需要明确取舍而不是「顺手关掉」。


  • ❌ 打开了MultiViews,然后发现/admin这种「本不存在的路径」居然命中了admin.html。


✅MultiViews会做扩展名协商,把 URL 映射到admin.html、admin.php等实际文件上。安全规则(比如禁止访问某路径)会因此被绕过。不需要内容协商就写-MultiViews。


  • ❌ 在.htaccess里写Options +Indexes想覆盖服务器配置。


✅ 这需要AllowOverride Options(或AllowOverride All);默认是AllowOverride None,.htaccess里的Options会被完全忽略,且日志里会出现Options not allowed here。


  • ❌ 在某个目录开了Options +ExecCGI却忘了对应模块没加载,脚本被当纯文本输出。


✅ 用apachectl -M | grep -i cgi(Debian 用apache2ctl -M)确认cgi_module或cgid_module在列,并确认AddHandler/ScriptAlias指向正确。

总结

选项一句话记住该不该开
All/None全开的简写 / 全关的简写;All不含MultiViews只作起点,别留在生产配置
Indexes没索引文件时输出目录列表关
FollowSymLinks允许跟随符号链接,按目录重写的前提视情况开
SymLinksIfOwnerMatch属主一致才跟随,有额外lstat开销多租户场景用
Includes/IncludesNOEXEC前者能用#exec,后者禁用需要 SSI 就用后者
ExecCGI该目录的脚本可执行仅限收窄的 CGI 目录
MultiViewsURL 扩展名协商,易绕过规则关
前缀语法不带+/-是整体替换,带了才是增删增量改动一律带前缀


记住两句话就够了:Options决定「这个目录里能干什么」,Require决定「谁能进来」,两者别混;以及不带+/-的Options是覆盖而非叠加。把这两条记牢,再对照apachectl -M确认模块状态,<Directory>段就不会再写成凭运气的样板了。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询