Apache 的 mod_rewrite 无法可靠实现时间限制,应使用 mod_authz_core + mod_authz_host 的 Require expr 指令,在授权阶段执行时间判断;示例规则为周一至周五 8:30–17:30 允许访问 /admin/,需注意时区、版本兼容性及缓存/代理干扰。

Apache 的 mod_rewrite 无法直接做时间限制,得用 mod_authz_core + mod_authz_host 配合 Require 指令
Apache 2.4 默认不支持基于时间的访问控制(比如“只允许工作日 9:00–18:00 访问 /api/”),mod_rewrite 的 RewriteCond 虽然能读取时间变量(如 %{TIME_HOUR}),但它只能用于重写逻辑,不能可靠拦截请求——尤其在启用缓存、代理或 HTTPS 终止后,时间判断容易错位。真正稳定的方式是使用内置的 Require 时间条件,它由 mod_authz_core 和 mod_authz_host 提供,且在认证/授权阶段执行,位置靠前、不可绕过。
前提是确保这两个模块已启用(大多数现代 Apache 发行版默认开启):
LoadModule authz_core_module modules/mod_authz_core.so LoadModule authz_host_module modules/mod_authz_host.so
然后在 、 或虚拟主机配置中使用:
-
Require all denied+Require expr组合是主流写法 -
expr支持time_hour、time_wday、time_year等变量,但注意:它们返回的是整数,不是字符串 - 时间值按服务器本地时区解析,不是 UTC,也不是客户端时区 —— 这点极易被忽略
怎么写一个“仅允许周一至周五 8:30–17:30 访问 /admin/”的规则
关键在于把时间范围拆解成布尔表达式:time_wday >= 1 && time_wday = 8 && (time_hour 。注意 time_wday 周一为 1,周日为 0;time_min 是 0–59 的整数,没有补零。
完整配置示例(放在 块内):
<location><requireall>
Require all denied
Require expr "time_wday >= 1 && time_wday = 8 && (time_hour
</requireall></location>
说明:
- 必须用
包裹,否则Require all denied会被后续Require expr覆盖 - 不能写成
Require expr "..."单独一行,那样等价于“满足条件就放行”,而没写Require all denied的话,默认是允许的 - 测试时建议先用宽松条件(如
time_hour == $(date +%H))验证语法是否生效,再收紧逻辑
为什么重启 Apache 后时间规则不生效?常见配置陷阱
最常踩的坑不是表达式写错,而是作用域和权限模型冲突:
- 如果该路径下同时存在
AllowOverride All和.htaccess文件,而.htaccess里有Require all granted,它会覆盖主配置中的Require expr—— 因为Require指令在.htaccess中也有效,且优先级更高 -
Require expr在 Apache 2.4.13+ 才支持完整时间变量,低于此版本只能用time_hour和time_wday,不支持time_min或time_sec - 若站点启用了
mod_cache,静态资源可能被缓存并跳过授权检查 —— 时间规则对缓存响应无效,需配合CacheIgnoreHeaders Authorization或禁用相关路径缓存 - 虚拟主机中若用了
ProxyPass,Require规则默认不作用于反向代理流量,必须加块并重复定义
替代方案:用 mod_rewrite + HTTP 状态码做降级兼容
如果你用的是 Apache 2.2 或无法升级,mod_rewrite 是唯一选择,但只能返回 403 或重定向,不能真正阻止请求处理(例如 PHP 脚本仍会执行)。这时要额外加防护:
RewriteCond %{TIME_WDAY} ^1|2|3|4|5$
RewriteCond %{TIME_HOUR} ^08|09|10|11|12|13|14|15|16$
RewriteRule ^/admin/ - [F]
RewriteCond %{TIME_WDAY} ^1|2|3|4|5$
RewriteCond %{TIME_HOUR} ^17$
RewriteCond %{TIME_MIN} <p>注意点:</p>
-
%{TIME_WDAY}返回 0–6 字符串,不是数字,所以要用正则匹配 -
%{TIME_HOUR}补零(如 08),所以不能写>=8,得枚举或用^0[89]|1[0-6]$这类模式 - 这个方案无法防止脚本执行,务必在应用层(如 PHP 的
index.php开头)再次校验date('N') === '1' && date('G') >= 8等,否则有绕过风险
时间规则的精度依赖系统时钟同步,NTP 不稳会导致窗口漂移;跨多台服务器部署时,各节点时区或时间差超过 60 秒,就可能产生非预期放行或拦截。









