You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.htaccess子文件夹过期头配置不生效问题排查

问题:子文件夹.htaccess的过期头规则未生效

我在WordPress站点的主.htaccess文件中配置了过期头规则,为给某个子文件夹设置例外的过期头,在该子文件夹内添加了独立的.htaccess文件,该子文件夹包含test.min.css文件。但测试发现,test.min.css仍使用主.htaccess中的max-age=9072000过期头配置。

主.htaccess配置

#Expires headers configuration added by BREEZE WP CACHE plugin
<IfModule mod_env.c>
   SetEnv BREEZE_BROWSER_CACHE_ON 1
</IfModule>
<IfModule mod_expires.c>
   ExpiresActive On
   ExpiresDefault "access plus 1 month"
   # Assets
   ExpiresByType text/css "access plus 1 month"
   ExpiresByType application/javascript "access plus 1 month"
   ExpiresByType application/x-javascript "access plus 1 month"
   ExpiresByType text/javascript "access plus 1 month"
   # Media assets 
   ExpiresByType audio/ogg "access plus 1 year"
   ExpiresByType image/bmp "access plus 1 year"
   ExpiresByType image/gif "access plus 1 year"
   ExpiresByType image/jpeg "access plus 1 year"
   ExpiresByType image/png "access plus 1 year"
   ExpiresByType image/svg+xml "access plus 1 year"
   ExpiresByType image/webp "access plus 1 year"
   ExpiresByType video/mp4 "access plus 1 year"
   ExpiresByType video/ogg "access plus 1 year"
   ExpiresByType video/webm "access plus 1 year"
   # Font assets 
   ExpiresByType application/vnd.ms-fontobject "access plus 1 year"
   ExpiresByType font/eot "access plus 1 year"
   ExpiresByType font/opentype "access plus 1 year"
   ExpiresByType application/x-font-ttf "access plus 1 year"
   ExpiresByType application/font-woff "access plus 1 year"
   ExpiresByType application/x-font-woff "access plus 1 year"
   ExpiresByType font/woff "access plus 1 year"
   ExpiresByType application/font-woff2 "access plus 1 year"
   # Data interchange
   ExpiresByType application/xml "access plus 0 seconds"
   ExpiresByType application/json "access plus 0 seconds"
   ExpiresByType application/ld+json "access plus 0 seconds"
   ExpiresByType application/schema+json "access plus 0 seconds"
   ExpiresByType application/vnd.geo+json "access plus 0 seconds"
   ExpiresByType text/xml "access plus 0 seconds"
   ExpiresByType application/rss+xml "access plus 1 hour"
   ExpiresByType application/rdf+xml "access plus 1 hour"
   ExpiresByType application/atom+xml "access plus 1 hour"
   # Manifest files
   ExpiresByType application/manifest+json "access plus 1 week"
   ExpiresByType application/x-web-app-manifest+json "access plus 0 seconds"
   ExpiresByType text/cache-manifest  "access plus 0 seconds"
   # Favicon
   ExpiresByType image/vnd.microsoft.icon "access plus 1 week"
   ExpiresByType image/x-icon "access plus 1 week"
   # HTML no caching
   ExpiresByType text/html "access plus 0 seconds"
   # Other
   ExpiresByType application/xhtml-xml "access plus 1 month"
   ExpiresByType application/pdf "access plus 1 month"
   ExpiresByType application/x-shockwave-flash "access plus 1 month"
   ExpiresByType text/x-cross-domain-policy "access plus 1 week"
</IfModule>
#End of expires headers configuration


RewriteEngine On
RewriteRule ^.well-known/acme-challenge - [L]


# BEGIN Imagify: webp file type
<IfModule mod_mime.c>
  AddType image/webp .webp
</IfModule>
# END Imagify: webp file type


# BEGIN Force https
  RewriteEngine On
  RewriteCond %{HTTP:X-Forwarded-Proto} !https
  RewriteRule ^(.*)$ https://%{HTTP_HOST}/$1 [L,R=301,NE]
# END Force https


# BEGIN Protect other domains trying to steal content
  RewriteCond %{HTTP_REFERER} !^https://(www\.)? [NC] 
  RewriteCond %{HTTP_REFERER} !^https://(www\.)?.*$ [NC]
# END Protect content (incl scorm) to be accessed from other domains


# BEGIN WordPress
# The directives (lines) between "BEGIN WordPress" and "END WordPress" are
# dynamically generated, and should only be modified via WordPress filters.
# Any changes to the directives between these markers will be overwritten.
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
RewriteBase /
RewriteRule ^index\.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]
</IfModule>
# END WordPress


# MalCare WAF
<Files ".user.ini">
  <IfModule mod_authz_core.c>
    Require all denied
  </IfModule>
  <IfModule !mod_authz_core.c>
    Order deny,allow
    Deny from all
  </IfModule>
</Files>
# END MalCare WAF


# Wordfence WAF
<Files ".user.ini">
  <IfModule mod_authz_core.c>
    Require all denied
  </IfModule>
  <IfModule !mod_authz_core.c>
    Order deny,allow
    Deny from all
  </IfModule>
</Files>
# END Wordfence WAF

子文件夹.htaccess初始配置

<IfModule mod_expires.c>
   ExpiresActive On
   ExpiresDefault "access plus 6 hours"
   ExpiresByType text/css "access plus 6 hours"
</IfModule>

更新说明与响应头

已移除子文件夹.htaccess中的<IfModule mod_expires.c>包装,且mod_expires模块已启用。请求test.min.css的响应头如下:

HTTP/2 200 OK
server: nginx
date: Fri, 14 Oct 2022 12:06:43 GMT
content-type: text/css
last-modified: Tue, 11 Oct 2022 19:41:25 GMT
vary: Accept-Encoding
etag: W/"63***425"
cache-control: public, max-age=9072000
content-encoding: gzip
X-Firefox-Spdy: h2

核心原因分析

  • 服务器环境不兼容:响应头显示服务器为nginx,而.htaccess是Apache专属配置文件,Nginx默认不会解析.htaccess规则。如果站点是Nginx反向代理Apache,或直接用Nginx运行,子文件夹的.htaccess规则完全不会被执行。
  • Cache-Control头优先级冲突:主.htaccess中Breeze插件生成的规则可能直接输出了Cache-Control: public, max-age=9072000,而mod_expires生成的Expires头会被转换为Cache-Control,但已存在的Cache-Control头优先级更高,子文件夹的规则无法覆盖。
  • WordPress重写规则接管请求:主.htaccess的WordPress重写规则可能将静态文件请求转发到index.php,导致子文件夹的静态文件由WordPress程序输出,而非Apache直接处理,子文件夹的.htaccess规则因此失效。

解决方案

针对Nginx环境

直接放弃.htaccess,在Nginx站点配置文件中为目标子文件夹添加独立过期规则:

location /your-subfolder/ {
    expires 6h;
    add_header Cache-Control "public, max-age=21600";
}

替换your-subfolder为实际子文件夹名称,修改后重启Nginx服务。

针对Apache环境

  1. 放行静态文件避免WordPress接管
    在主.htaccess的WordPress重写规则前添加以下代码,确保子文件夹的静态文件直接由Apache处理:

    # 放行目标子文件夹的静态文件,跳过WordPress重写
    RewriteRule ^your-subfolder/.*\.(css|js|png|jpg)$ - [L]
    
  2. 强制设置Cache-Control头
    在子文件夹的.htaccess中添加Header指令,直接覆盖主配置的Cache-Control头:

    ExpiresActive On
    ExpiresDefault "access plus 6 hours"
    ExpiresByType text/css "access plus 6 hours"
    # 强制设置Cache-Control,覆盖主配置
    Header set Cache-Control "public, max-age=21600"
    

    确保服务器已启用mod_headers模块。

  3. 检查Breeze插件设置
    进入Breeze缓存插件后台,查看静态文件缓存规则,尝试排除目标子文件夹,避免插件自动生成的规则覆盖自定义配置。


内容的提问来源于stack exchange,提问作者Nick

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.16 15:01:18