.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环境
放行静态文件避免WordPress接管
在主.htaccess的WordPress重写规则前添加以下代码,确保子文件夹的静态文件直接由Apache处理:# 放行目标子文件夹的静态文件,跳过WordPress重写 RewriteRule ^your-subfolder/.*\.(css|js|png|jpg)$ - [L]强制设置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模块。检查Breeze插件设置
进入Breeze缓存插件后台,查看静态文件缓存规则,尝试排除目标子文件夹,避免插件自动生成的规则覆盖自定义配置。
内容的提问来源于stack exchange,提问作者Nick
相关产品推荐
相关产品推荐

