I am getting this error on the lighthouse report.
"uses-long-cache-ttl": {
"id": "uses-long-cache-ttl",
"title": "Serve static assets with an efficient cache policy",
"description": "A long cache lifetime can speed up repeat visits to your page. [Learn more about efficient cache policies](https://developer.chrome.com/docs/lighthouse/performance/uses-long-cache-ttl/).",
"score": 0.5,
"scoreDisplayMode": "metricSavings",
"numericValue": 14606.099999999997,
"numericUnit": "byte",
"displayValue": "30 resources found",
"details": {
"type": "table",
"headings": [
{
"key": "url",
"valueType": "url",
"label": "URL"
},
{
"key": "cacheLifetimeMs",
"valueType": "ms",
"label": "Cache TTL",
"displayUnit": "duration"
},
{
"key": "totalBytes",
"valueType": "bytes",
"label": "Transfer Size",
"displayUnit": "kb",
"granularity": 1
}
],
"items": [
{
"url": "https://seriousdesign.net/js/pwa-manager.js",
"cacheLifetimeMs": 0,
"cacheHitProbability": 0,
"totalBytes": 3296,
"wastedBytes": 3296
},
{
"url": "https://seriousdesign.net/js/css-loader.js",
"cacheLifetimeMs": 0,
"cacheHitProbability": 0,
"totalBytes": 1875,
"wastedBytes": 1875
},
{
"url": "https://seriousdesign.net/js/critical.js",
"cacheLifetimeMs": 0,
"cacheHitProbability": 0,
"totalBytes": 1179,
"wastedBytes": 1179
},
{
"url": "https://seriousdesign.net/js/performance-monitor.js",
"cacheLifetimeMs": 0,
"cacheHitProbability": 0,
"totalBytes": 0,
"wastedBytes": 0
},
{
"url": "https://seriousdesign.net/js/script.js",
"cacheLifetimeMs": 0,
"cacheHitProbability": 0,
"totalBytes": 0,
"wastedBytes": 0
},
{
"url": "https://seriousdesign.net/css/style.css",
"debugData": {
"type": "debugdata",
"max-age": 604800
},
"cacheLifetimeMs": 604800000,
"cacheHitProbability": 0.9,
"totalBytes": 9434,
"wastedBytes": 943.3999999999997
},
{
"url": "https://seriousdesign.net/img/logos/linux.svg",
"debugData": {
"type": "debugdata",
"max-age": 604800
},
"cacheLifetimeMs": 604800000,
"cacheHitProbability": 0.9,
"totalBytes": 9411,
"wastedBytes": 941.0999999999998
},
{
"url": "https://seriousdesign.net/img/logos/php.svg",
"debugData": {
"type": "debugdata",
"max-age": 604800
},
"cacheLifetimeMs": 604800000,
"cacheHitProbability": 0.9,
"totalBytes": 5687,
"wastedBytes": 568.6999999999998
},
{
"url": "https://seriousdesign.net/img/logos/playwright.svg",
"debugData": {
"type": "debugdata",
"max-age": 604800
},
"cacheLifetimeMs": 604800000,
"cacheHitProbability": 0.9,
"totalBytes": 5506,
"wastedBytes": 550.5999999999999
},
{
"url": "https://seriousdesign.net/img/logos/excalidraw.svg",
"debugData": {
"type": "debugdata",
"max-age": 604800
},
"cacheLifetimeMs": 604800000,
"cacheHitProbability": 0.9,
"totalBytes": 5410,
"wastedBytes": 540.9999999999999
},
{
"url": "https://seriousdesign.net/img/logos/drupal.svg",
"debugData": {
"type": "debugdata",
"max-age": 604800
},
"cacheLifetimeMs": 604800000,
"cacheHitProbability": 0.9,
"totalBytes": 4148,
"wastedBytes": 414.7999999999999
},
{
"url": "https://seriousdesign.net/img/logos/project-management.svg",
"debugData": {
"type": "debugdata",
"max-age": 604800
},
"cacheLifetimeMs": 604800000,
"cacheHitProbability": 0.9,
"totalBytes": 4121,
"wastedBytes": 412.0999999999999
},
{
"url": "https://seriousdesign.net/img/logos/html.svg",
"debugData": {
"type": "debugdata",
"max-age": 604800
},
"cacheLifetimeMs": 604800000,
"cacheHitProbability": 0.9,
"totalBytes": 3290,
"wastedBytes": 328.99999999999994
},
{
"url": "https://seriousdesign.net/img/logos/node.svg",
"debugData": {
"type": "debugdata",
"max-age": 604800
},
"cacheLifetimeMs": 604800000,
"cacheHitProbability": 0.9,
"totalBytes": 3071,
"wastedBytes": 307.0999999999999
},
{
"url": "https://seriousdesign.net/img/logos/infrastructure.svg",
"debugData": {
"type": "debugdata",
"max-age": 604800
},
"cacheLifetimeMs": 604800000,
"cacheHitProbability": 0.9,
"totalBytes": 3010,
"wastedBytes": 300.99999999999994
},
{
"url": "https://seriousdesign.net/img/logos/css.svg",
"debugData": {
"type": "debugdata",
"max-age": 604800
},
"cacheLifetimeMs": 604800000,
"cacheHitProbability": 0.9,
"totalBytes": 2975,
"wastedBytes": 297.49999999999994
},
{
"url": "https://seriousdesign.net/img/logos/sketchup.svg",
"debugData": {
"type": "debugdata",
"max-age": 604800
},
"cacheLifetimeMs": 604800000,
"cacheHitProbability": 0.9,
"totalBytes": 2732,
"wastedBytes": 273.19999999999993
},
{
"url": "https://seriousdesign.net/img/logos/htmx.svg",
"debugData": {
"type": "debugdata",
"max-age": 604800
},
"cacheLifetimeMs": 604800000,
"cacheHitProbability": 0.9,
"totalBytes": 2589,
"wastedBytes": 258.8999999999999
},
{
"url": "https://seriousdesign.net/img/logos/excel.svg",
"debugData": {
"type": "debugdata",
"max-age": 604800
},
"cacheLifetimeMs": 604800000,
"cacheHitProbability": 0.9,
"totalBytes": 2582,
"wastedBytes": 258.19999999999993
},
{
"url": "https://seriousdesign.net/img/logos/software-development.svg",
"debugData": {
"type": "debugdata",
"max-age": 604800
},
"cacheLifetimeMs": 604800000,
"cacheHitProbability": 0.9,
"totalBytes": 2222,
"wastedBytes": 222.19999999999996
},
{
"url": "https://seriousdesign.net/img/logos/js.svg",
"debugData": {
"type": "debugdata",
"max-age": 604800
},
"cacheLifetimeMs": 604800000,
"cacheHitProbability": 0.9,
"totalBytes": 2177,
"wastedBytes": 217.69999999999996
},
{
"url": "https://seriousdesign.net/img/logos/symfony.svg",
"debugData": {
"type": "debugdata",
"max-age": 604800
},
"cacheLifetimeMs": 604800000,
"cacheHitProbability": 0.9,
"totalBytes": 1966,
"wastedBytes": 196.59999999999997
},
{
"url": "https://seriousdesign.net/img/logos/wordpress.svg",
"debugData": {
"type": "debugdata",
"max-age": 604800
},
"cacheLifetimeMs": 604800000,
"cacheHitProbability": 0.9,
"totalBytes": 1959,
"wastedBytes": 195.89999999999995
},
{
"url": "https://seriousdesign.net/img/logos/firmware-development.svg",
"debugData": {
"type": "debugdata",
"max-age": 604800
},
"cacheLifetimeMs": 604800000,
"cacheHitProbability": 0.9,
"totalBytes": 1958,
"wastedBytes": 195.79999999999995
},
{
"url": "https://seriousdesign.net/img/logos/software-architecture.svg",
"debugData": {
"type": "debugdata",
"max-age": 604800
},
"cacheLifetimeMs": 604800000,
"cacheHitProbability": 0.9,
"totalBytes": 1940,
"wastedBytes": 193.99999999999994
},
{
"url": "https://seriousdesign.net/img/logos/python.svg",
"debugData": {
"type": "debugdata",
"max-age": 604800
},
"cacheLifetimeMs": 604800000,
"cacheHitProbability": 0.9,
"totalBytes": 1621,
"wastedBytes": 162.09999999999997
},
{
"url": "https://seriousdesign.net/img/logos/figma.svg",
"debugData": {
"type": "debugdata",
"max-age": 604800
},
"cacheLifetimeMs": 604800000,
"cacheHitProbability": 0.9,
"totalBytes": 1444,
"wastedBytes": 144.39999999999998
},
{
"url": "https://seriousdesign.net/img/logos/kirby.svg",
"debugData": {
"type": "debugdata",
"max-age": 604800
},
"cacheLifetimeMs": 604800000,
"cacheHitProbability": 0.9,
"totalBytes": 1305,
"wastedBytes": 130.49999999999997
},
{
"url": "https://seriousdesign.net/img/logos/docker.svg",
"debugData": {
"type": "debugdata",
"max-age": 604800
},
"cacheLifetimeMs": 604800000,
"cacheHitProbability": 0.9,
"totalBytes": 1024,
"wastedBytes": 102.39999999999998
},
{
"url": "https://seriousdesign.net/img/logos/apify.svg",
"debugData": {
"type": "debugdata",
"max-age": 604800
},
"cacheLifetimeMs": 604800000,
"cacheHitProbability": 0.9,
"totalBytes": 979,
"wastedBytes": 97.89999999999998
}
],
"summary": {
"wastedBytes": 14606.099999999997
},
"sortedBy": [
"totalBytes"
],
"skipSumming": [
"cacheLifetimeMs"
]
},
"guidanceLevel": 3
},
It is a project built with symfony components, mounted on my hosttinger vps using apache 2
Perfect, this is super clear. Let’s break down what Lighthouse is telling you, why it matters, and what you should actually change in your Apache config on Hostinger for a Symfony-components site.
1. What Lighthouse is complaining about
Lighthouse audit: "Serve static assets with an efficient cache policy"
Translation:
-
Your static files (JS, CSS, SVG, etc.) should tell the browser:
- "Hey, you can keep this file in cache for a long time, don't re-download it every visit."
-
If you don’t, repeat visits reload everything = slower page + worse score.
The audit looks at two things for each file:
cacheLifetimeMs→ how long the browser is allowed to cache it, based on your HTTP headers.totalBytes→ size of that file.wastedBytes→ “how many bytes could have been saved on repeat visits if you cached this long-term.”
Interpreting your data
Group A: JS with cacheLifetimeMs: 0
/js/pwa-manager.js
/js/css-loader.js
/js/critical.js
/js/performance-monitor.js
/js/script.js
These files are served with no caching at all (no Cache-Control / Expires or explicitly max-age=0).
That means: browser downloads them every page load.
👉 Lighthouse hates that.
Group B: CSS + SVGs with cacheLifetimeMs: 604800000
Example:
/css/style.css
/img/logos/*.svg
You are sending a caching header here:
max-age = 604800 seconds = 7 days
Lighthouse is still nagging, even though it's already 7 days.
Why? Two reasons:
- Lighthouse is greedy. It prefers something like
cache-control: public, max-age=31536000, immutable(≈ 1 year) for static assets that rarely change. - Your assets are not fingerprinted (I’ll explain this in a sec), so Lighthouse assumes they might change and then the browser might keep an old version.
So: 7 days is “ok but could be longer,” 0 is “bad.”
2. What you SHOULD do (high level)
For static assets that don't change often → send long cache headers (weeks/months).
For assets that change often → you have 2 options:
- either don’t cache them long (what you have now)
- or start using cache busting / fingerprinting so you can cache them long
Let’s go step by step.
3. Fix #1 — Add long-lived caching headers in Apache
Because you’re on Apache 2 on Hostinger (not Symfony HttpKernel serving assets directly), the easiest fix is at Apache level.
a. Enable mod_expires and mod_headers
On a VPS you control Apache, so as root:
a2enmod expires
a2enmod headers
systemctl restart apache2
If you don’t have systemctl on Hostinger shared (only service restarts via panel), just make sure expires and headers are already enabled. On a VPS you’re fine.
b. Add caching rules for static files
You can do this either in the <VirtualHost> block for seriousdesign.net or in a .htaccess in your public web root (probably /var/www/.../public or similar, wherever Apache DocumentRoot points to).
Here’s a good starting point:
<IfModule mod_expires.c>
ExpiresActive On
# Images, fonts, SVGs: cache for 1 year
ExpiresByType image/svg+xml "access plus 1 year"
ExpiresByType image/png "access plus 1 year"
ExpiresByType image/jpeg "access plus 1 year"
ExpiresByType image/webp "access plus 1 year"
ExpiresByType image/gif "access plus 1 year"
ExpiresByType font/woff2 "access plus 1 year"
ExpiresByType font/woff "access plus 1 year"
ExpiresByType font/ttf "access plus 1 year"
ExpiresByType font/otf "access plus 1 year"
# CSS and JS: cache for 1 month
# (We’ll talk in a moment about how to safely make this 1 year)
ExpiresByType text/css "access plus 30 days"
ExpiresByType application/javascript "access plus 30 days"
ExpiresByType text/javascript "access plus 30 days"
</IfModule>
<IfModule mod_headers.c>
# Let browser know it's allowed to cache
<FilesMatch "\.(js|css)$">
Header set Cache-Control "public, max-age=2592000" # 30 days
</FilesMatch>
<FilesMatch "\.(svg|png|jpe?g|gif|webp|woff2?|ttf|otf)$">
Header set Cache-Control "public, max-age=31536000, immutable" # 1 year
</FilesMatch>
</IfModule>
What this does:
- Adds
ExpiresandCache-Controlheaders for each type. - SVG logos like
img/logos/php.svgwill now get ~1 year cache. - Lighthouse will be much happier.
👉 You’ll notice we’re giving CSS/JS only 30 days here, not 1 year. That’s intentional for now.
Why: if you update /css/style.css but keep the same URL, browsers might keep the old one for a full year = broken layout.
So we play it safer until we solve cache busting.
4. Fix #2 — Add cache busting (a.k.a. fingerprinting / versioned filenames)
This is how you get Lighthouse “green perfection mode.”
Problem right now:
- You’re serving plain static paths like
/css/style.css,/js/pwa-manager.js. - If you cache that for 1 year and then you edit the file, old users won’t get the update quickly.
Solution:
-
When you deploy, generate files with a hash or version in the filename.
- Example:
style.ab12c3.cssinstead ofstyle.css - Example:
pwa-manager.20251101.jsinstead ofpwa-manager.js
- Example:
-
Then update your HTML templates to load that file.
Why this helps:
-
You can now safely tell the browser “cache this for A YEAR, NEVER DOWNLOAD AGAIN” because
- when you change the file, it’ll have a new URL → browser treats it as a new resource.
Then you can raise JS/CSS cache to 1 year too:
<FilesMatch "\.(js|css)$">
Header set Cache-Control "public, max-age=31536000, immutable"
</FilesMatch>
This will crush Lighthouse’s complaint.
How to do versioning in your stack depends on how you're building assets:
-
If you’re already running a build step (Webpack, Vite, etc.) → it can output hashed filenames automatically.
-
If you’re serving hand-written JS/CSS (no bundler yet) → you can fake it short-term by adding a query string like
style.css?v=20251101.- Browsers cache per full URL including
?v=... - Some CDNs/proxies are picky, but for most normal browsers it works.
- Lighthouse usually accepts long cache + query param versioning as "good enough" for MVPs.
- Browsers cache per full URL including
5. Why some files show 0 bytes and totalBytes: 0
For:
/js/performance-monitor.js
/js/script.js
totalBytes: 0
That’s Lighthouse saying “you referenced this file but I didn’t really download meaningful content / it was maybe empty or 0 bytes”. Could be:
- The file exists but is empty/minified to nothing.
- The audit logic couldn't determine size because of response headers (happens with redirects, inline streaming, or 404 → 200 fallbacks).
Still, cacheLifetimeMs: 0 means Apache sent no caching headers. So they’re still being flagged.
6. Minimal actionable plan for you
Step 1. Add the Apache rules above (mod_expires + mod_headers) either in VirtualHost or .htaccess where your static assets live.
Step 2. Redeploy and re-run Lighthouse. You should already see a better score because:
- Before: JS = 0 seconds
- After: JS = ~30 days
- Before: SVGs = 7 days
- After: SVGs = 1 year
Step 3. (Optional but pro) Start adding versioning to your asset filenames (hash or ?v=). Then safely increase JS/CSS cache to 1 year too.
This is basically what well-tuned Symfony/AssetMapper or Webpack Encore does automatically: hashed filenames + long immutable cache headers.
7. Quick copy/paste for .htaccess
If you just want a drop-in .htaccess in your public web root, use this:
<IfModule mod_expires.c>
ExpiresActive On
# Images, fonts, SVGs: 1 year
ExpiresByType image/svg+xml "access plus 1 year"
ExpiresByType image/png "access plus 1 year"
ExpiresByType image/jpeg "access plus 1 year"
ExpiresByType image/webp "access plus 1 year"
ExpiresByType image/gif "access plus 1 year"
ExpiresByType font/woff2 "access plus 1 year"
ExpiresByType font/woff "access plus 1 year"
ExpiresByType font/ttf "access plus 1 year"
ExpiresByType font/otf "access plus 1 year"
# CSS / JS: 30 days for now
ExpiresByType text/css "access plus 30 days"
ExpiresByType application/javascript "access plus 30 days"
ExpiresByType text/javascript "access plus 30 days"
</IfModule>
<IfModule mod_headers.c>
<FilesMatch "\.(js|css)$">
Header set Cache-Control "public, max-age=2592000"
</FilesMatch>
<FilesMatch "\.(svg|png|jpe?g|gif|webp|woff2?|ttf|otf)$">
Header set Cache-Control "public, max-age=31536000, immutable"
</FilesMatch>
</IfModule>
Put that in the directory Apache is serving (where /css and /js and /img live). No PHP changes needed.
8. TL;DR for ProtonSystems-style “Site Audit” reports ✅
When you report this to a client (or to yourself in a Site Audit PDF 😎), you can phrase it like this:
-
Problem: Static assets are either not cached (0 seconds) or cached only ~7 days. This causes unnecessary downloads on repeat visits and hurts performance scores.
-
Impact: Repeat visitors reload JavaScript, CSS, and icons instead of using the browser cache.
-
Recommendation:
- Enable Apache’s
mod_expiresandmod_headers. - Add long-term caching headers (
Cache-Control: public, max-age=X, immutable) for static assets. - Introduce cache-busting (versioned filenames like
style.abc123.css) so we can safely cache for 1 year.
- Enable Apache’s
That’s literally Lighthouse’s ask.
If you paste me your current <VirtualHost> for seriousdesign.net or the current .htaccess in your public dir, I can show you the exact final version with no guessing.