Force Varnish caching on LWS

Procédure

In this documentation, we will explain step by step how to force Varnish and browsers to keep your resources in cache, even when they send Pragma: no-cache or Cache-Control: no-cache requests.

1. Context and LWS architecture

The shared hosting and managed cPanel/VPS plans from LWS rely on the stack:

Browser ⇄ NGINX (SSL + HTTP/2) ⇄ Varnish Cache ⇄ Apache/PHP-FPM

  • NGINX handles TLS and compresses content.
  • Varnish (Fastest Cache) is positioned as a reverse proxy to cache HTTP responses in memory.
  • Apache or PHP-FPM generate the final response when Varnish does not have a HIT.

When everything is correctly configured, Varnish can deliver up to 1000 times faster than direct access to PHP, while easing the server CPU load.

2. Reminder: how does Varnish decide to cache?

Element Impact on cache How to influence it
Method Only GET and HEAD are eligible Avoid POST for public pages
Response headers Cache-Control, Expires, Pragma Determine duration and scope Configure via .htaccess (see §3)
Cookies / Set-Cookie A present cookie = no caching by default Remove or neutralize unnecessary cookies
HTTP status 200, 203, 301, 302, 404, 410 are cacheable No action required, but avoid 500s!

Trucs et astuces


By default, Varnish ignores the Cache-Control: no-cache sent by the browser for static files, but respects the "revalidation logic" if the object is already in cache.

3. Control lifetime via .htaccess

Enable the mod_headers module (this is the default at LWS) then place the following snippet wherever you want:

⌨ Command

  Header set Cache-Control "public, max-age=3600, s-maxage=3600, stale-while-revalidate=60, stale-if-error=86400"
  Header set Expires "Thu, 31 Dec 2037 23:55:55 GMT"
  • max-age : duration (in seconds) in the browser.
  • s-maxage : specific duration for shared caches (Varnish/CDN).
  • stale-while-revalidate : allows Varnish to serve stale content while refreshing it in the background.
  • stale-if-error : serves the stale version if the backend responds with an error (fail-safe).

3.1. Folder targeting

Placing the same .htaccess file in the "/images/" folder applies the rule only to the "images" folder.

3.2. Extension targeting

⌨ Command

  
    Header set Cache-Control "public, max-age=2592000, s-maxage=2592000, immutable"
  

immutable : tells the browser that no revalidation is needed as long as the object has not expired; ideal for versioned files (style.483bf.css).

3.3. Short cache for HTML

⌨ Command

  Header set Cache-Control "public, max-age=300, s-maxage=600, must-revalidate"

Expires after 5 min on the client side and 10 min on the Varnish side, then revalidation is mandatory.

4. Practical usage scenarios

Real case .htaccess fragment Why?
Landing page updated every hour max-age=600, s-maxage=1200 Visitors get data that is “almost live” without saturating PHP
Versioned CSS/JS max-age=31536000, immutable Almost no traffic on the server, instant loading
E-commerce product images max-age=604800 Reduces TTFB, speeds up the catalog
Back office / wp-admin no-store, private Avoids storing sensitive data in the shared cache

5. Purge or invalidate the cache

From your hosting environment (your site’s PHP script, Cron task, or SSH terminal), purging the Varnish cache does not require any token:

  • A page:
⌨ Command
curl -X PURGE https://exemple.com/chemin/ressource.jpg
  • An entire folder, by regular expression:
⌨ Command
curl -X PURGE -H 'X-Purge-Method: regex' 'https://exemple.com/images/.*'
  • The entire site:
⌨ Command
curl -X FULLPURGE https://exemple.com
  • In PHP, for example after updating content:
Code
$ch = curl_init('https://exemple.com/chemin/page.html');
curl_setopt_array($ch, [CURLOPT_CUSTOMREQUEST => 'PURGE', CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 20]);
curl_exec($ch);
$code = curl_getinfo($ch, CURLINFO_HTTP_CODE); // 200 : cache purge ; 405 : purge refused
curl_close($ch);

A complete script, ready for a Cron task, is available in the help article Speed up your site with Fastest Cache. You can also purge the cache from your dashboard, using the “Purge cache” button in Fastest Cache.

6. Testing and debugging

⌨ Command
curl -I https://exemple.com/style.css

Look at:

  • Age: 356 → Varnish served a response that is 356 s old.
  • Cache-Control: public, max-age=2592000, s-maxage=2592000, immutable
  • X-Cache: HIT (or MISS).

With Chrome/Edge: DevTools > Network > Disable cache can simulate a first-time visitor.

7. Common pitfalls

  1. Unnecessary cookies : many WordPress themes set a cookie even for public pages. Remove or disable them via functions.php.
  2. Query strings : by default, ?v=123 creates a new entry in the cache. Prefer versioning in the filename instead.
  3. Sensitive content : forms, customer account pages ➡ no-store, private, max-age=0.
  4. Brotli/Gzip compression : enable them before the Varnish layer to avoid double caching.

8. Summary of best practices

  • Use s-maxage ⩾ max-age to keep the proxy cache longer than the browser cache.
  • Set immutable on any file whose name contains a hash.
  • Do not mix outdated Expires with max-age except for older browsers.
  • Document your TTLs in a CACHE_POLICY.md file for the team.

9. Useful resources

✅ Your Varnish cache is now under control!

help.lws.net/a/1949
Was this article helpful?

Yes

No

Read 7 648 times

Thank you! Feel free to ask questions about our documentation if you would like more information, and help us improve it.

Similar articles

A question about this article?

Ask the LWS team and the community. Answers are published after moderation.

Ask the LWS team and its community a question

GDPR: Controller: LWS-Ligne Web Services. Purpose: moderation and public publication of your question, possible notification of an answer. Legal basis: consent (Art. 6(1)(a) GDPR). Email retention: 90 days after notification, 12 months maximum without an answer. To exercise your rights, see the GDPR section of our Terms and Conditions.