When implementing redirects in Apache, we often need to control caching behavior. The standard approach using RedirectMatch
from mod_alias unfortunately strips away any custom headers we try to set. Here's what actually happens when you combine them:
ServerName example.com
Header set Cache-Control "no-store, no-cache, must-revalidate"
Header set Pragma "no-cache"
Header set Expires "0"
RedirectMatch ^.*$ https://new.example.com/
The issue stems from how Apache processes these directives. The RedirectMatch
creates an immediate response, bypassing later header modifications. You can verify this by checking the response:
curl -I http://example.com
HTTP/1.1 302 Found
Location: https://new.example.com/
# Missing Cache-Control headers here
Here are three reliable approaches to implement redirects with proper cache control:
1. Using mod_rewrite
ServerName example.com
Header always set Cache-Control "no-store, no-cache, must-revalidate"
Header always set Pragma "no-cache"
Header always set Expires "0"
RewriteEngine On
RewriteRule ^.*$ https://new.example.com/ [R=302,L]
2. Using Location with Header
ServerName example.com
Header always set Cache-Control "no-store, no-cache, must-revalidate"
Header always set Pragma "no-cache"
Header always set Expires "0"
Redirect 302 / https://new.example.com/
3. Custom ErrorDocument (For Specific Status Codes)
ServerName example.com
Header always set Cache-Control "no-store, no-cache, must-revalidate"
ErrorDocument 302 https://new.example.com/
RewriteEngine On
RewriteRule ^.*$ - [R=302,L]
- Use
Header always set
instead of justHeader set
to ensure headers are applied to all responses - For permanent redirects (301), browser caching behavior differs from temporary (302) redirects
- Test with
curl -v
to verify both the redirect and headers are present - The
Vary
header might need adjustment depending on your caching requirements
While implementing these solutions, remember:
# For better performance with many redirects:
Header always unset ETag
FileETag None
Header always merge Cache-Control "public"
The optimal solution depends on your specific Apache version and modules enabled. Always test in your environment.
When configuring Apache to handle redirects while setting cache-control headers, many developers encounter an unexpected behavior: the redirect works, but the cache headers disappear from the response.
<VirtualHost *:80>
ServerName __default__
Header set Cache-Control "no-store, no-cache, must-revalidate"
Header set Pragma "no-cache"
Header set Expires "Thu, 01 Dec 1994 16:00:00 GMT"
RedirectMatch ^.*$ http://portal.example.com/
</VirtualHost>
The core issue stems from how Apache processes directives from different modules. mod_alias (handling RedirectMatch) executes late in the request cycle, often overriding headers set earlier by mod_headers.
For full control over both redirects and headers, replace RedirectMatch with mod_rewrite and explicit Header directives:
<VirtualHost *:80>
ServerName __default__
# Force no-caching
Header always set Cache-Control "no-store, no-cache, must-revalidate"
Header always set Pragma "no-cache"
Header always set Expires "Thu, 01 Dec 1994 16:00:00 GMT"
# Handle redirect via mod_rewrite
RewriteEngine On
RewriteRule ^.*$ http://portal.example.com/ [R=302,L]
</VirtualHost>
- always keyword ensures headers are set even for redirect responses
- mod_rewrite executes earlier in the request processing pipeline
- Explicit 302 status code maintains the temporary redirect behavior
Test with curl to confirm both redirect and headers are present:
curl -I http://your-server.example.com
HTTP/1.1 302 Found
Date: Mon, 01 Jan 2024 00:00:00 GMT
Server: Apache
Cache-Control: no-store, no-cache, must-revalidate
Pragma: no-cache
Expires: Thu, 01 Dec 1994 16:00:00 GMT
Location: http://portal.example.com/
For even more control over the response body during redirects:
<VirtualHost *:80>
ErrorDocument 302 "<html><body>Redirecting...</body></html>"
Header always set Cache-Control "no-store, no-cache, must-revalidate"
RewriteEngine On
RewriteRule ^.*$ - [R=302,L]
</VirtualHost>