Configuring web server cache correctly means that pages your visitors have already loaded are served instantly from memory or disk — no PHP processing, no database queries. The result is faster load times, less server load, and a better user experience.
Types of Cache in a Web Server Stack
Before touching any configuration, it helps to understand which cache layer you are modifying, since each one serves a different purpose.
| Type | Where it lives | What it stores |
|---|---|---|
| Full-page cache | Server (disk or RAM) | HTML generated by PHP/CMS |
| Opcode cache (OPcache) | PHP on the server | Compiled PHP scripts |
| Object cache (Redis/Memcached) | Server (RAM) | Database query results |
| Browser cache | User's device | Images, CSS, JS, fonts |
| CDN cache | Edge servers | Static assets and pages |
This guide focuses on browser cache (controlled from the server via HTTP headers) and full-page cache with Apache — the two most common configurations in shared hosting and VPS environments.
Configuring Browser Cache with HTTP Headers
The server tells the browser how long it can store a resource by sending the Cache-Control header. If the browser already has the file and it has not expired, no new request is made.
In Apache with .htaccess
The most common method in shared hosting and cPanel is editing the .htaccess file in the site root:
<IfModule mod_expires.c>
ExpiresActive On
# Images: 1 year
ExpiresByType image/webp "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"
# CSS and JavaScript: 1 month
ExpiresByType text/css "access plus 1 month"
ExpiresByType application/javascript "access plus 1 month"
# Web fonts: 1 year
ExpiresByType font/woff2 "access plus 1 year"
ExpiresByType font/woff "access plus 1 year"
# HTML: no cache (dynamic content)
ExpiresByType text/html "access plus 0 seconds"
</IfModule>
<IfModule mod_headers.c>
<FilesMatch "\.(jpg|jpeg|png|gif|webp|svg|ico|css|js|woff2|woff)$">
Header append Cache-Control "public, immutable"
</FilesMatch>
</IfModule>
With this block, static resources are stored in the visitor's browser for up to one year. On their next visit, those files load instantly from local cache.
In Nginx
If you have access to the server configuration file (VPS or dedicated server), the Nginx equivalent is:
location ~* \.(jpg|jpeg|png|gif|webp|svg|ico|css|js|woff2|woff)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
location ~* \.html$ {
expires -1;
add_header Cache-Control "no-store, no-cache, must-revalidate";
}
Full-Page Cache in Apache with mod_cache
Browser cache only helps on repeat visits. To speed up the first visit and reduce server load, you configure a full-page cache that serves pre-generated HTML without invoking PHP.
Enabling mod_cache in Apache
On servers where you have access to Apache configuration (VPS with cPanel or dedicated), you need mod_cache and mod_cache_disk:
# In the VirtualHost or httpd.conf
CacheEnable disk /
CacheRoot /var/cache/apache2/mod_cache_disk
CacheDirLevels 2
CacheDirLength 1
CacheDefaultExpire 3600
CacheMaxExpire 86400
CacheLastModifiedFactor 0.5
On shared hosting you will not have access to this configuration. The practical alternative is a CMS cache plugin (WP Rocket, LiteSpeed Cache, or W3 Total Cache in WordPress) that generates static HTML files and serves them directly via .htaccess rules.
OPcache: the PHP Cache That Gets Forgotten
PHP compiles every script into bytecode on each request. OPcache stores that bytecode in memory, eliminating repeated compilation. On a WordPress site with moderate traffic, this can reduce server response time by 30–50%.
Check if it is active via phpinfo() (look for the Zend OPcache section). If it is not active, ask your hosting provider or, on a VPS, edit php.ini:
opcache.enable=1
opcache.memory_consumption=128
opcache.interned_strings_buffer=16
opcache.max_accelerated_files=10000
opcache.revalidate_freq=2
opcache.fast_shutdown=1
Common Mistakes When Configuring Cache
A misconfigured cache can cause more problems than having no cache at all. These are the most frequent errors:
Caching authenticated user pages
Shopping carts, user dashboards, or any page with personalized content should never be cached. Make sure your configuration excludes session cookies:
CacheIgnoreHeaders Set-Cookie
In WordPress, most cache plugins automatically exclude logged-in users — verify that option is enabled.
Not using cache busting for static assets
If you update your CSS or JS but the filename stays the same, browsers will serve the cached version for months. The fix is to append a version query string or rename the file on deployment: style.css?v=1.4.2 or style.1.4.2.css.
Caching error responses
404s, 500 errors, and temporary redirects (302) must not be cached. Add this to your configuration:
<IfModule mod_headers.c>
Header always set Cache-Control "no-store" "expr=%{REQUEST_STATUS} == 404 || %{REQUEST_STATUS} == 500"
</IfModule>
TTL too short for static assets
Setting 1 hour on images and CSS is almost as bad as not caching: the browser has to revalidate the file on every visit. Use 1 month or 1 year for static resources and handle updates with cache busting.
If managing these configurations is not your focus, a specialist service can handle it. At elenlace.com we configure full server cache as part of our web maintenance plans. Also check out our performance section for more optimization guides.
Verifying That Cache Is Working
After applying the configuration, check the response headers from the browser (DevTools → Network → select a file → Headers) or with curl:
curl -I https://yoursite.com/style.css
You should see something like:
Cache-Control: public, max-age=31536000, immutable
Expires: Sat, 19 Jun 2027 12:00:00 GMT
If you see Cache-Control: no-cache or no-store on static resources, the configuration was not applied correctly.
Key Takeaways
- There are several cache layers: browser, full-page, OPcache, and CDN. Each solves a different problem.
- Browser cache is controlled from the server with
Cache-ControlandExpiresheaders — configure them in.htaccess(Apache) or in thelocationblock (Nginx). - Static assets (images, CSS, JS) should be cached for 1 month to 1 year; dynamic HTML should never be cached.
- OPcache can reduce PHP response time by 30–50% and should be enabled in any production PHP installation.
- The most common mistakes are: caching authenticated user sessions, skipping cache busting, and setting TTLs too short.
- Verify your configuration with DevTools or
curl -Iafter applying changes.
Prefer to leave the configuration to the experts? Contact us at elenlace.com and we'll optimize your server cache so your site loads fast from the very first click.
FAQ
Can I configure server cache on shared hosting?
Yes, but with limitations. On shared hosting you can control browser cache via .htaccess (if your provider has mod_expires and mod_headers enabled). Full-page cache at the server level requires access to Apache or Nginx configuration, which is available only on VPS and dedicated servers.
How long should I cache my site's HTML?
Generally, HTML should not be aggressively cached at the server level because content can change (new posts, updated stock, user sessions). If you use a CMS, let the cache plugin manage the TTL and make sure it automatically invalidates cache when you publish changes.
Can cache prevent my updates from showing?
Yes, if you skip cache busting. When you update CSS, JS, or images, change the filename or append a version parameter. Many CMS platforms and cache plugins do this automatically when clearing cache after a publish action.
Are Redis or Memcached necessary for WordPress?
They are not essential for small sites, but they are highly recommended for WooCommerce stores or sites with medium-to-high traffic. They store database query results in RAM, dramatically reducing server load during peak hours.
Compare providers
Other providers and guides worth comparing: