Configure mod_expires or mod_headers on your Apache origin server to set Cache-Control and Expires response headers, so that Alibaba Cloud CDN edge nodes and browsers cache your static content for the right TTL (Time to Live).
Choose a module
Apache provides two modules for controlling cache headers. Choose based on your requirements:
|
Module |
Pros |
Cons |
When to use |
|
|
Simple syntax; quickly sets |
Cannot set complex directives such as |
Simple scenarios that only need |
|
|
Full |
Slightly more complex syntax |
Production environments that need fine-grained cache policies |
Prerequisites
Before you begin, verify that the required Apache modules are enabled.
Check which modules are loaded:
apachectl -M | grep -E "expires|headers"
The output should include expires_module and headers_module. If either is missing, enable it:
Debian / Ubuntu:
sudo a2enmod headers expires
sudo systemctl restart apache2
CentOS / RHEL / Alibaba Cloud Linux:
Modules are typically compiled in by default. If a module is missing, check /etc/httpd/conf.modules.d/ and make sure the corresponding LoadModule line is not commented out, then restart Apache:
sudo systemctl restart httpd
Place configuration in a dedicated file under /etc/httpd/conf.d/ (CentOS/RHEL) or /etc/apache2/conf-available/ (Debian/Ubuntu) — for example, cache.conf. Avoid .htaccess files: Apache parses .htaccess on every request, which adds unnecessary overhead and defeats the performance gains you are trying to achieve.
Configuration examples
All examples use <IfModule> guards so Apache starts without error even if the module is absent. Replace or extend ExpiresByType lines to match your content types.
Set cache TTL by content type
Use mod_expires for straightforward max-age rules, or mod_headers when you need full Cache-Control control.
mod_expires
<IfModule mod_expires.c>
ExpiresActive On
# Default TTL for all content types
ExpiresDefault "access plus 1 day"
# Images: cache for 1 month
ExpiresByType image/jpeg "access plus 1 month"
ExpiresByType image/png "access plus 1 month"
ExpiresByType image/gif "access plus 1 month"
ExpiresByType image/svg+xml "access plus 1 month"
ExpiresByType image/webp "access plus 1 month"
# CSS and JavaScript: cache for 1 week
ExpiresByType text/css "access plus 1 week"
ExpiresByType application/javascript "access plus 1 week"
# Fonts: cache for 1 year
ExpiresByType font/woff2 "access plus 1 year"
ExpiresByType font/woff "access plus 1 year"
# HTML and data: do not cache
ExpiresByType text/html "access plus 0 seconds"
ExpiresByType application/json "access plus 0 seconds"
</IfModule>
mod_headers equivalent
<IfModule mod_headers.c>
# Images
<FilesMatch "\.(jpg|jpeg|png|gif|svg|webp)$">
Header set Cache-Control "max-age=2592000, public"
</FilesMatch>
# CSS and JavaScript
<FilesMatch "\.(css|js)$">
Header set Cache-Control "max-age=604800, public"
</FilesMatch>
# Fonts
<FilesMatch "\.(woff|woff2)$">
Header set Cache-Control "max-age=31536000, public, immutable"
</FilesMatch>
# HTML and data: no caching
<FilesMatch "\.(html|json)$">
Header set Cache-Control "no-store"
</FilesMatch>
</IfModule>
Set a global default TTL
When most of your content is static, set a global default and override specific types as needed.
mod_expires
<IfModule mod_expires.c>
ExpiresActive On
ExpiresDefault "access plus 1 week"
</IfModule>
mod_headers equivalent
<IfModule mod_headers.c>
Header set Cache-Control "max-age=604800, public"
</IfModule>
Disable caching for dynamic content
Prevent CDN edge nodes and browsers from caching responses that change per request.
mod_expires
<IfModule mod_expires.c>
ExpiresActive On
ExpiresByType text/html "access plus 0 seconds"
ExpiresByType application/json "access plus 0 seconds"
</IfModule>
mod_headers equivalent
<IfModule mod_headers.c>
<FilesMatch "\.(html|json|php)$">
Header set Cache-Control "no-store"
</FilesMatch>
</IfModule>
Directive reference
mod_expires directives
|
Directive |
Description |
|
|
Enables |
|
|
Sets the default TTL for all content types not matched by |
|
|
Sets the TTL for a specific MIME type |
Time base options:
|
Value |
Meaning |
|
|
TTL is calculated from the time of the request |
|
|
TTL is calculated from the file's last-modified timestamp |
Duration units: years, months, weeks, days, hours, minutes, seconds
Common duration values:
|
Expression |
Equivalent |
Use case |
|
|
31,536,000 seconds |
Versioned assets with cache-busting filenames |
|
|
2,592,000 seconds |
Images and other infrequently updated assets |
|
|
604,800 seconds |
CSS and JavaScript files |
|
|
No caching |
HTML pages and API responses |
Verify the configuration
After reloading Apache, confirm the headers are present in the response:
curl -I https://<your-domain>/path/to/static-file.jpg
Look for Cache-Control or Expires in the output:
HTTP/1.1 200 OK
Cache-Control: max-age=2592000, public
Expires: Thu, 10 Jul 2026 04:00:00 GMT
Alternatively, open Chrome DevTools, go to the Network tab, reload the page, and click a static resource. The Response Headers panel shows the Cache-Control and Expires values set by your configuration.
Best practices
Prefer
mod_headersfor production. It supports the full range ofCache-Controldirectives (no-store,immutable,stale-while-revalidate) thatmod_expirescannot generate.Use long TTLs for versioned assets. If your build pipeline appends a hash to filenames (for example,
app.a1b2c3.js), set TTLs of one year or more. The filename change acts as a cache-busting mechanism.Set
no-storefor HTML and API responses. This ensures browsers and CDN edge nodes always fetch the latest page structure and data.Reload, do not restart. After editing the configuration file, run
apachectl graceful(orsystemctl reload apache2/systemctl reload httpd) to apply changes without dropping active connections.Test with
curl -Ibefore going to production. Verify headers on each content type you configured, not just one.