// Ein offener Brief an die "Bitte-mach-das-mal-selber"-Brigade

"Bitte Cache leeren." "Bitte Cookies löschen." Zwei Phrasen. Zwei Probleme. Eine einzige Selbstanzeige. 🤯

Worum es geht: Warum jeder Entwickler, der User auffordert, ihren Browser-Cache zu leeren oder ihre Cookies zu löschen, mit diesem einen Satz öffentlich beweist, dass er seinen eigenen Beruf nicht versteht — zwei seit Jahrzehnten gelöste Themen, von denen das ganze Team noch nie ernsthaft gehört hat, geschweige denn begriffen.

Wenn ein Team bei einem Release oder bei einer Support-Anfrage "Bitte leeren Sie Ihren Browser-Cache" oder "Bitte löschen Sie Ihre Cookies" sagt, dann sagt es in Wahrheit: "Wir haben keine Ahnung, wie unsere eigene Software funktioniert. Mach du das jetzt irgendwie, vielleicht klappt's."

Das ist kein Support. Das ist kein hilfreicher Hinweis. Das ist eine öffentliche Selbstauskunft über das Wissensniveau eines Teams, das sich Web-Entwickler nennt — verteilt auf zwei Themen, bei denen jede ernstzunehmende Junior-Stellenbeschreibung Grundkenntnisse voraussetzt.

Inhalt
  1. Die These
  2. Cache ist nicht Cookies — eine notwendige Klarstellung
  3. ▸ TEIL I — Der Cache-Bullshit
    1. Was hier eigentlich passiert
    2. Asset Fingerprinting
    3. HTTP-Header. Richtig.
    4. Service Worker — wenn, dann richtig
    5. API-Versionierung
  4. ▸ TEIL II — Der Cookie-Bullshit
    1. Was hier eigentlich passiert
    2. Server-side Session-Invalidierung
    3. Cookie-Versionierung & Migration
    4. Signierte Cookies + Validierung beim Lesen
    5. Saubere Auth-Flows
    6. Bonus: localStorage / IndexedDB — gleiche Krankheit
  5. Das Urteil

Die These

Es gibt zwei Phrasen, die im Software-Support so oft fallen, dass sie inzwischen wie professionelle Kommunikation klingen — und beide sind in Wahrheit Eingeständnisse:

  1. "Bitte leeren Sie Ihren Browser-Cache." — Übersetzt: Wir verstehen Cache-Control nicht.
  2. "Bitte löschen Sie Ihre Cookies." — Übersetzt: Wir verstehen unseren eigenen Zustand nicht.

Caching ist seit Mitte der Neunziger ein gelöstes Problem. Cookies sind seit 1994 Standard. Beides ist von Anfang an so designt, dass Entwickler den vollen Lebenszyklus kontrollieren — wie lange etwas gilt, wann es überschrieben wird, wann es verschwindet. Beides braucht keine Mithilfe des Endanwenders.

"Bitte leeren Sie Ihren Cache" und "Bitte löschen Sie Ihre Cookies" sind nicht die Worte von Profis, die in Eile sind. Es sind die Worte von Leuten, die das Werkzeug, mit dem sie täglich arbeiten, nie über die ersten zwei Tutorial-Kapitel hinaus verstanden haben — und das mit jedem dieser Sätze unfreiwillig kundtun.

🤡 Das ist nicht Faulheit. Das ist Unfähigkeit.

Wichtig zu verstehen, bevor wir in die Details gehen: Diese Leute sind nicht faul. Faulheit würde voraussetzen, dass sie wissen, wie es richtig geht, und sich aktiv dagegen entscheiden. Was hier passiert, ist etwas anderes — es ist Ahnungslosigkeit.

Sie sagen "Cache leeren", weil sie nicht wissen, dass dieser Satz nichts mit einer Lösung zu tun hat. Sie sagen "Cookies löschen", weil sie nie verstanden haben, was ein Cookie ist, wer es setzt, und dass sie es selbst sind. Sie haben keinen Plan, was ihre App tut, sobald sie aus dem Localhost rauskommt — und behelfen sich mit der einzigen Bedienoberfläche, die sie kennen: der von Strg+Shift+Entf.

Das ist kein Pfusch im Sinne von "haben wir verbockt". Das ist Pfusch im Sinne von "haben nie kapiert, dass es überhaupt etwas zu können gibt".

Cache ist nicht Cookies — eine notwendige Klarstellung

Bevor jemand sagt "ist doch dasselbe Thema": nein, ist es nicht. Cache und Cookies sind zwei komplett verschiedene Mechanismen mit zwei verschiedenen Lösungs-Räumen. Sie teilen nur eines: dass sie zu 100 % unter Kontrolle der App stehen, und dass es trotzdem als Norm gilt, ihre Probleme an den User zu delegieren.

 HTTP-CacheCookies
Was wird gespeichertResponse-Bodies: JS, CSS, HTML, Bilder, FontsAnwendungs-State: Session-IDs, Auth-Tokens, CSRF, Prefs, Warenkorb
Wer entscheidet, was rein gehtBrowser, anhand der vom Server gelieferten Cache-Control-HeaderDie App, direkt per Set-Cookie-Header oder document.cookie
Wie weit weg vom App-Code1 Hop (Header → Browser-Entscheidung)0 Hops — die App schreibt & liest direkt
Wie überschreibt man'sNeuer Dateiname (Hash) → neue URL → neuer Cache-EintragNeuer Set-Cookie mit selbem Namen → alter Wert weg, sofort
Wie löscht man'sServer lässt Datei verwaisen, Browser räumt selbst aufSet-Cookie: foo=; Max-Age=0 — eine Zeile, beim nächsten Response
Was es bei "Stale" anrichtetUser sieht veraltetes UI / alte AssetsUser sitzt in totem Session-State, kaputter Auth, falschen Prefs
Das "User soll löschen"-Argument istfalsch & faulfalscher & fauler

Der entscheidende Punkt: Cookies sind sogar direkter unter Kontrolle der App als der Cache. Der Cache ist passives Browser-Verhalten, das durch Header gesteuert wird. Cookies sind aktiver Zustand, den die App selbst beim Browser deponiert hat und jederzeit überschreiben kann. Wer Cookie-Probleme zum User-Problem erklärt, beweist nicht nur Unwissen über HTTP — sondern Unwissen über den eigenen Code. Das ist kein Versäumnis am Rand. Das ist Kernkompetenz, die nicht existiert.

👉 Du bist hier nicht das Problem. Sie sind es.
Teil I · Erster Tatbestand
Der Cache-Bullshit
"Bitte leeren Sie Ihren Browser-Cache" — oder: wir haben Cache-Control nie konfiguriert.

Was hier eigentlich passiert

Der Mechanismus, an dem diese Leute scheitern, ist so trivial, dass man sich beim Erklären schämt. Aber wenn ein Team es im Berufsalltag nicht versteht, muss man es offenbar nochmal aufschreiben:

  1. Der Browser fragt eine Datei an: app.js
  2. Der Server liefert sie mit einem Cache-Control-Header aus.
  3. Wenn dieser Header sagt "cache das eine Stunde", dann cached der Browser das eine Stunde.
  4. Wenn der Entwickler in der Zwischenzeit eine neue app.js deployt — und dabei den Dateinamen nicht ändert — dann lädt der Browser sie nicht erneut. Korrekt nach Spezifikation.

Das ist nicht ein Bug im Browser. Das ist nicht Schikane gegen Entwickler. Das ist das gewünschte Verhalten — denn sonst wäre jede Caching-Schicht im Internet sinnlos und jede Website zehnmal langsamer.

Die Aufgabe des Entwicklers ist es, dem Browser korrekt zu sagen, was er cachen darf und was nicht. Wer stattdessen Anwender anschreibt, sie sollten doch bitte das Caching komplett umgehen, weiß schlicht nicht, dass es einen Header gibt, der genau das regelt. Diese Leute schicken eine Bitte an Menschen ohne Zugriff auf den Server — weil sie selbst nicht wissen, dass sie diesen Zugriff haben.

Asset Fingerprinting

Statt app.js liefert man app.a3f9b2c1.js aus. Der Hash-Suffix wird aus dem Inhalt der Datei berechnet. Ändert sich der Inhalt, ändert sich der Hash. Ändert sich der Hash, ändert sich der Dateiname. Ändert sich der Dateiname, ist es aus Browser-Sicht eine komplett andere Datei — und wird zwangsläufig neu geladen.

Das ist nicht exotisch. Das ist Standard. Webpack hat das seit 2014. Vite seit Tag eins. Sogar Rails Asset Pipeline kann das seit 2011.

// vite.config.js — die default-Konfig macht das schon
export default {
  build: {
    rollupOptions: {
      output: {
        entryFileNames: 'assets/[name].[hash].js',
        chunkFileNames: 'assets/[name].[hash].js',
        assetFileNames: 'assets/[name].[hash][extname]'
      }
    }
  }
}

Im HTML steht dann <script src="/assets/app.a3f9b2c1.js">. Nach dem nächsten Deploy <script src="/assets/app.7e1d4a8f.js">. Der Browser sieht eine neue URL. Cache-Eintrag für die alte URL? Egal — wird nie wieder angefragt. Fertig.

Es kostet null Zeilen Mehraufwand. Wer's nicht hat, hat entweder eine 15 Jahre alte Stack-Konfiguration kopiert, ohne sie zu verstehen — oder ist im Build-Tool an einer Default-Funktion vorbeigelaufen, weil ihm bis heute niemand erklärt hat, wozu sie da ist. Beide Erklärungen sind gleich peinlich.

HTTP-Header. Richtig.

Asset Fingerprinting löst das Problem für alles, was vom HTML referenziert wird. Bleibt das HTML selbst. Das hat keinen Hash im Namen — denn die URL ist die Einstiegs-URL: /, /login, /dashboard.

Lösung: dem Browser sagen, dass HTML nie ohne Nachfrage aus dem Cache verwendet werden darf. Das geht in einer Zeile Server-Konfiguration:

Datei-TypCache-ControlBegründung
HTMLno-cacheBrowser muss bei jedem Aufruf nachfragen (ETag/304), kostet ~100 Bytes Traffic.
Gehashte Assetspublic, max-age=31536000, immutableEin Jahr cachen. Garantie: ändert sich nie.
API-Antwortenno-store oder ETag-basiertNiemals stumpf cachen, außer der Endpoint ist nachweislich idempotent.
# nginx, drei Zeilen
location / {
    add_header Cache-Control "no-cache";
}
location /assets/ {
    add_header Cache-Control "public, max-age=31536000, immutable";
}

Das hier ist der Fehler, den die Cache-Brigade macht:

# Default-Konfig, kein Cache-Control gesetzt
# → Browser entscheidet heuristisch anhand Last-Modified
# → Eine Datei, die vor 100 Tagen geändert wurde,
#   wird ~10 Tage gecached
# → Deploy heute, User sieht alten Stand bis morgen

Und statt das zu fixen — in einer Zeile — schreibt man dem User: "Bitte Cache leeren."

Service Worker — wenn, dann richtig

Manche Apps registrieren einen Service Worker und cachen aggressiv für Offline-Support. Das ist legitim. Es ist aber auch seit Jahren gelöst, wie man einen Service Worker korrekt aktualisiert:

// sw.js
self.addEventListener('install',  e => self.skipWaiting());
self.addEventListener('activate', e => e.waitUntil(clients.claim()));

Wer einen Service Worker betreibt ohne Update-Strategie, hat eine kaputte App ausgerollt. Die Lösung ist nicht "Cache leeren". Die Lösung ist: eine Update-Strategie zu implementieren.

API-Versionierung

Frontend erwartet Feld customerId, Backend liefert plötzlich customer_id. Weiße Seite, Fehler in der Konsole. Kein Cache-Problem. Ein Versionierungsproblem.

GET /api/v1/customers/123
GET /api/v2/customers/123

Frontend v1 redet mit /api/v1. Frontend v2 mit /api/v2. Beide Backends laufen parallel, bis das alte Frontend nachweislich nicht mehr im Umlauf ist. Breaking Change im Datenformat? Neue Versionsnummer. Nicht "der User soll mal Cache leeren".

Merkregel Cache Wenn ein User nach einem Deploy etwas anderes sieht als nach einem Hard-Reload,
dann hast du einen Fehler im Deployment gemacht — nicht der Browser des Users.
Teil II · Zweiter Tatbestand
Der Cookie-Bullshit
"Bitte löschen Sie Ihre Cookies" — oder: wir können den Zustand, den wir selbst gesetzt haben, nicht handhaben.

Cookies sind kein Caching. Cookies sind aktiver Anwendungs-State, den die App selbst beim Browser deponiert: Session-IDs, Auth-Tokens, CSRF-Tokens, Sprach-Präferenz, Warenkorb. Geschrieben per Set-Cookie-Header oder document.cookie. Bei jedem Request zurückgeschickt. Bei jedem Response von der App überschreibbar, neu setzbar, löschbar.

Wenn das Team also sagt "löschen Sie die Cookies", dann gibt es ohne es zu merken eine ganze Menge zu verstehen:

"Wir wissen nicht, dass wir den Zustand, den wir selbst beim User abgelegt haben, jederzeit erkennen, migrieren, invalidieren und korrigieren könnten. Wir haben keine Ahnung, was eigentlich passiert, wenn wir res.cookie(...) schreiben. Bitte machen Sie das, was wir nicht können — gehen Sie in die Browser-Settings und entsorgen Sie unsere Spuren persönlich. 💩"

Das ist keine Bug-Kategorie. Das ist Inkompetenz in Reinkultur. Cookies sind, wie weiter oben gezeigt, direkter unter Kontrolle der App als der Cache. Wenn ein Cookie kaputt, veraltet, manipuliert oder zu einer alten Schema-Version gehört, kann die App das beim nächsten Request erkennen — und beim selben Response ein frisches setzen oder das alte löschen. In einer einzigen HTTP-Antwort. Wer das nicht weiß, kennt das Protokoll nicht, das er den ganzen Tag spricht.

Server-side Session-Invalidierung

Wer Sessions nur als unsignierte ID in einem Cookie ablegt und sich darauf verlässt, dass der Browser sie eines Tages "irgendwann freiwillig" entsorgt, hat keine Session-Verwaltung — sondern eine Hoffnung.

Korrekt: Session-IDs werden serverseitig in einem Store gehalten (Redis, DB, Memory-Store — egal). Jede Session lässt sich sofort invalidieren:

# Logout, Force-Re-Auth, Suspicious-Activity:
session_store.delete(session_id)
# Nächster Request mit dieser SID → 401 → Re-Auth-Flow
# Das Cookie im Browser ist ab sofort wertlos.

Damit ist es egal, ob das Cookie noch im Browser herumliegt. Es referenziert eine tote Session, der Server schickt 401 zurück, die App leitet auf Login um. Nichts daran erfordert eine Aktion des Users.

Wer sagt "Cookies löschen", hat entweder keinen Session-Store, oder er hat einen und weiß nicht, wozu er existiert. Beides ist Stümperei der Sorte, bei der ein Senior in einer ernstzunehmenden Firma fragt, wie diese Leute durchs Vorstellungsgespräch gekommen sind.

Schema-Änderung an einem Cookie? Versionsnummer einbauen. Dann beim Lesen prüfen.

// Schreiben
res.cookie('prefs', JSON.stringify({
  v: 2,
  theme: 'dark',
  lang: 'de'
}));

// Lesen
const parsed = safeParse(req.cookies.prefs);
if (!parsed || parsed.v !== 2) {
  // Alte Version, Schrott, leer: ignorieren
  // Defaults setzen, frisches Cookie schreiben
  return DEFAULTS;
}
return parsed;

Ein altes Cookie aus der Welt vor dem Schema-Change? Wird beim ersten Lesen erkannt, verworfen, durch ein frisches überschrieben. Der User merkt nichts. Kein Login-Bildschirm, keine Support-Mail, keine Strg+Shift+Entf-Anleitung.

Wer's anders macht, hat sein Cookie-Format ohne Plan geändert — nicht aus bösem Willen, sondern weil er das Wort "Migration" in Verbindung mit "Client-State" zum ersten Mal hört, wenn der Support-Ticket-Stapel auf seinem Tisch liegt.

Wer Cookies unsigniert speichert und ihrem Inhalt blind vertraut, schreibt eine Sicherheitslücke und nennt sie "Feature". Cookies werden signiert (HMAC) oder verschlüsselt — jede ernsthafte App-Plattform der letzten 15 Jahre hat das eingebaut:

# Flask
app.secret_key = b'...'
session['user_id'] = 42   # → automatisch signiert & gesetzt
// Express
app.use(cookieSession({secret: process.env.SECRET}));

Beim Lesen: Signatur prüfen. Bei Mismatch oder Schrott: verwerfen, nicht "irgendwie damit weitermachen". Manipulierte, abgelaufene oder kaputte Cookies werden ignoriert, statt einen Halbzustand zu produzieren, den der User dann per Browser-Settings entsorgen darf.

So sieht der Fehler praktisch aus:

# Falsch: blind aus dem Cookie lesen
user_id = parseInt(req.cookies.user_id)
return db.users.find(user_id)
# → manipuliertes Cookie? Egal, wir nehmen's.
# → kaputtes Cookie? NaN, Crash, weiße Seite.
# → "Bitte löschen Sie Ihre Cookies."

Saubere Auth-Flows

Wenn ein Auth-Token "festhängt" oder eine Session in einem komischen Zustand ist, ist das kein Cookie-Verschmutzungs-Problem. Das ist ein kaputter Auth-Flow. Die etablierte Lösung steht in jedem OAuth/JWT-Tutorial seit zehn Jahren:

Wer das nicht hat und stattdessen "Cookies löschen" diktiert, hat von OAuth, OIDC, JWT-Best-Practices und Session-Hardening schlicht nie gehört — oder die Begriffe einmal im CV stehen, ohne dass je ein Funke Verständnis dahinter stand. Und verkauft das jetzt als Support.

Bonus: localStorage / IndexedDB — gleiche Krankheit

Wenn Support sagt "Browser-Daten löschen", landet man häufig bei localStorage, sessionStorage oder IndexedDB. Das sind keine Cookies. Das Argument bleibt aber identisch:

Die App schreibt rein. Die App liest aus. Die App ist verantwortlich.

Schema-Version mitspeichern, beim Lesen validieren, bei Mismatch wegwerfen. Drei Zeilen.

const SCHEMA = 3;
const raw = localStorage.getItem('state');
const data = raw ? JSON.parse(raw) : null;
if (!data || data.v !== SCHEMA) {
  localStorage.removeItem('state');  // selbst aufräumen
  return initFresh();
}
return data;

Wer das nicht tut und stattdessen Kunden in die Browser-DevTools schickt, ist nicht "zu beschäftigt, um es ordentlich zu machen". Er weiß einfach nicht, dass es ordentlich geht — und hat sich nie die Frage gestellt, weil er noch nie auf die Idee gekommen ist, dass er selbst für den Zustand zuständig ist, den er da abgelegt hat.

Merkregel Cookies Wenn ein Cookie den User in einen kaputten Zustand bringt,
dann hat deine App es dort hingelegt — und deine App hat den Hebel, es wegzuräumen.
Bei jedem einzelnen Response.

Das Urteil

Wenn ein Team bei einem Release oder einer Supportanfrage "Bitte Cache leeren" oder "Bitte Cookies löschen" sagt, dann beweist dieses Team mit jedem dieser Sätze, dass es seinen eigenen Stack nicht beherrscht. 🤬 Im Detail:

💩 Auf der Cache-Seite: Was diese Leute nicht wissen

💩 Auf der Cookie-Seite: Was diese Leute nicht wissen

🤯 Und in beiden Fällen

Es ist nicht so, dass sie diese Probleme kennen und nicht fixen wollen. Es ist so, dass sie nicht wissen, dass es Probleme sind. Sie haben sich an ihren eigenen Stümper-Workflow gewöhnt und halten ihn für Normalität. Cache-Leeren-Anweisungen sind in ihrer Welt eine Form von professioneller Kommunikation — nicht ein Hilferuf, sondern ein Standard-Tool.

Das ist kein Service. Das ist kein Support. Das ist keine Sorgfalt. Das ist die digitale Entsprechung eines "Handwerkers", der die Heizung eingebaut hat, ohne zu wissen, was ein Druckminderer ist, was ein Mischer macht, und warum man entlüften muss — und beim ersten Defekt sagt:

"Wenn morgen die Heizung wieder ausfällt, müssen Sie nur das Wasser ablassen, neu befüllen, entlüften, das Thermostat resetten — und falls Sie immer noch frieren, schmeißen Sie bitte auch das Bedienfeld weg und kaufen ein neues. Tschüss. 🔧🤡"

Wir würden diesen "Handwerker" verklagen, ihm die Gewerbeerlaubnis entziehen lassen und im Bekanntenkreis weiterempfehlen, ihn niemals zu bestellen. In der Software-Industrie ist exakt diese Kategorie Mensch der Median — und schreibt Mails an Kunden, die wie Service aussehen sollen.

An die Verantwortlichen, ganz konkret

Falls du dich beim Lesen ertappt fühlst — weil du dieser Tage in einer Mail an Kunden geschrieben hast, sie mögen doch bitte mit Strg+Shift+Entf ihren Cache und/oder ihre Cookies leeren — dann nimm das jetzt nicht persönlich, sondern fachlich. Es geht nicht darum, dass du ein schlechter Mensch bist. Es geht darum, dass du Dinge tust, deren technischen Hintergrund du nie verinnerlicht hast.

Konkrete Hausaufgaben, in der Reihenfolge, in der du sie noch nicht erledigt hast:

  1. Öffne dein Build-Tool. Lerne, was Asset Fingerprinting ist. Aktiviere es, wenn es nicht schon an ist.
  2. Öffne deine Server-Config. Lerne, was Cache-Control tut. Setze die zwei Regeln.
  3. Öffne deinen Session-Code. Lerne, was serverseitige Session-Invalidierung ist. Implementiere sie.
  4. Versioniere alles, was du in Cookies oder localStorage schreibst. Validiere beim Lesen.
  5. Bau einen Set-Cookie: ...; Max-Age=0-Mechanismus in deinen Logout- und Error-Recovery-Pfad ein.
  6. Versioniere deine API. Mach keine stillen Breaking Changes mehr.

Wenn dir bei einem dieser Punkte unklar ist, was gemeint ist, ist das die Antwort darauf, warum deine User regelmäßig Browser-Reinigungs-Anleitungen bekommen. Du hast jetzt sechs Stichworte zum Googeln — mehr Lernhilfe gibt's hier nicht, weil das alles Inhalt vom ersten Web-Development-Jahr ist.

Caching ist HTTP/1.1. Cookies sind RFC 2109. Beides ist älter als die meisten, die heute Web-Apps bauen. Wer das 2026 nicht versteht und trotzdem Web-Anwendungen in Produktion betreibt, ist im falschen Beruf — und sollte zur Sicherheit aller Beteiligten ernsthaft prüfen, ob er nicht in einer anderen Branche besser aufgehoben wäre. 🪦

// An open letter to the "please-do-it-yourself" brigade

"Please clear your cache." "Please delete your cookies." Two phrases. Two problems. One single confession. 🤯

What this is about: why any developer who tells users to clear their browser cache or delete their cookies publicly proves, in a single sentence, that they don't understand their own profession — two topics that have been solved problems for decades, that the entire team has never seriously heard of, let alone understood.

When a team says "Please clear your browser cache" or "Please delete your cookies" at a release or in a support reply, what they're actually saying is: "We have no clue how our own software works. You go fix it somehow, maybe it'll stop misbehaving."

That isn't support. That isn't a helpful hint. It's a public statement about the level of knowledge inside a team that calls itself "web developers" — spread across two topics where any serious junior job description assumes basic competence.

Contents
  1. The thesis
  2. Cache is not Cookies — a necessary distinction
  3. ▸ PART I — The Cache Bullshit
    1. What's actually happening
    2. Asset Fingerprinting
    3. HTTP headers. Done right.
    4. Service Workers — if you use them, use them right
    5. API Versioning
  4. ▸ PART II — The Cookie Bullshit
    1. What's actually happening
    2. Server-side session invalidation
    3. Cookie versioning & migration
    4. Signed cookies + validation on read
    5. Clean auth flows
    6. Bonus: localStorage / IndexedDB — same disease
  5. The verdict

The thesis

There are two phrases so often used in software support that they've started to sound like professional communication — and both are, in reality, confessions:

  1. "Please clear your browser cache." — Translation: we don't understand Cache-Control.
  2. "Please delete your cookies." — Translation: we don't understand our own application state.

Caching has been a solved problem since the mid-nineties. Cookies have been a standard since 1994. Both were designed from day one so that developers control the entire lifecycle — how long something is valid, when it gets overwritten, when it disappears. Neither requires any cooperation from the end user.

"Please clear your cache" and "please delete your cookies" are not the words of professionals in a hurry. They are the words of people who never understood the tool they work with all day past the first two tutorial chapters — and who unwittingly broadcast that fact every time they say it.

🤡 This isn't laziness. This is incompetence.

Important to grasp before we get into the details: these people are not lazy. Laziness would imply that they know how to do it right and choose not to. What's happening here is something else — it's cluelessness.

They say "clear your cache" because they don't know that the phrase has nothing to do with a solution. They say "delete your cookies" because they never understood what a cookie is, who sets it, and that they themselves are the ones setting it. They have no idea what their application does the moment it leaves localhost — and they fall back on the only interface they know how to operate: Ctrl+Shift+Del.

This isn't bungling in the sense of "we screwed up". This is bungling in the sense of "we never realized there was anything to get right in the first place".

Cache is not Cookies — a necessary distinction

Before anyone says "isn't it the same topic": no, it isn't. Cache and cookies are two completely different mechanisms with two different solution spaces. The only thing they share is that they are 100 % under the application's control — and that it's somehow become normal to delegate their problems to the user anyway.

 HTTP cacheCookies
What gets storedResponse bodies: JS, CSS, HTML, images, fontsApplication state: session IDs, auth tokens, CSRF, prefs, cart
Who decides what goes inThe browser, based on the server-supplied Cache-Control headerThe app, directly via Set-Cookie header or document.cookie
Distance from app code1 hop (header → browser decision)0 hops — the app reads & writes directly
How you overwrite itDifferent filename (hash) → new URL → new cache entrySend a new Set-Cookie with the same name → old value gone, instantly
How you delete itServer lets the file go orphan, browser cleans upSet-Cookie: foo=; Max-Age=0 — one line, on the next response
What "stale" does to a userUser sees outdated UI / old assetsUser is stuck in dead session state, broken auth, wrong prefs
The "user should delete it" argument iswrong & lazywronger & lazier

The crucial point: cookies are even more directly under the app's control than the cache. The cache is passive browser behaviour, governed by headers. Cookies are active state, deposited by the app itself in the user's browser, which the app can overwrite at any moment. Calling a cookie issue a user issue isn't just ignorance about HTTP — it's ignorance about your own code. That's not a peripheral oversight. That's a core competency that simply isn't there.

👉 You are not the problem here. They are.
Part I · First count of the indictment
The Cache Bullshit
"Please clear your browser cache" — or: we never configured Cache-Control.

What's actually happening

The mechanism these people fail to grasp is so trivial that explaining it feels embarrassing. But if a team can't manage it in their day job, apparently it needs writing down again:

  1. The browser requests a file: app.js
  2. The server returns it with a Cache-Control header.
  3. If that header says "cache this for one hour", the browser caches it for one hour.
  4. If the developer deploys a new app.js during that hour — and doesn't change the filename — the browser won't reload it. Exactly as specified.

That is not a bug in the browser. It is not some browser conspiracy against developers. It is the desired behaviour — without it, every caching layer on the internet would be pointless and every website ten times slower.

The developer's job is to tell the browser correctly what may be cached and what may not. Anyone who instead emails users to bypass caching entirely simply doesn't know that there's a header for exactly this. These people are sending a request to people who have no access to the server — because they themselves don't realise they do.

Asset Fingerprinting

Instead of app.js you serve app.a3f9b2c1.js. The hash suffix is computed from the contents of the file. Change the contents, the hash changes. Change the hash, the filename changes. Change the filename, and as far as the browser is concerned, it's a completely different file — and gets reloaded by necessity.

This is not exotic. This is standard. Webpack has done it since 2014. Vite from day one. Even the Rails Asset Pipeline has done it since 2011.

// vite.config.js — the default config already does this
export default {
  build: {
    rollupOptions: {
      output: {
        entryFileNames: 'assets/[name].[hash].js',
        chunkFileNames: 'assets/[name].[hash].js',
        assetFileNames: 'assets/[name].[hash][extname]'
      }
    }
  }
}

Your HTML then has <script src="/assets/app.a3f9b2c1.js">. After the next deploy: <script src="/assets/app.7e1d4a8f.js">. The browser sees a new URL. Cache entry for the old URL? Doesn't matter — never going to be requested again. Done.

It costs zero lines of extra work. Anyone without it has either copied a 15-year-old stack configuration without understanding it, or walked past a default feature of the build tool because nobody ever told them what it's for. Both explanations are equally embarrassing.

HTTP headers. Done right.

Asset fingerprinting solves the problem for everything referenced from the HTML. What's left is the HTML itself. That has no hash in its name — because the URL is the entry URL: /, /login, /dashboard.

Solution: tell the browser that HTML may never be served from cache without revalidation. This takes one line of server configuration:

File typeCache-ControlWhy
HTMLno-cacheBrowser has to revalidate on every request (ETag/304), costs ~100 bytes of traffic.
Hashed assetspublic, max-age=31536000, immutableCache for one year. Guarantee: never changes.
API responsesno-store or ETag-basedNever blindly cache, unless the endpoint is provably idempotent.
# nginx, three lines
location / {
    add_header Cache-Control "no-cache";
}
location /assets/ {
    add_header Cache-Control "public, max-age=31536000, immutable";
}

This is the mistake the cache brigade makes:

# Default config, no Cache-Control set
# → Browser decides heuristically based on Last-Modified
# → A file last changed 100 days ago
#   gets cached for ~10 days
# → Deploy today, user sees the old version until tomorrow

And instead of fixing it — in one line — they write to the user: "Please clear your cache."

Service Workers — if you use them, use them right

Some apps register a service worker and cache aggressively for offline support. That's legitimate. It is also been a solved problem for years how to update a service worker correctly:

// sw.js
self.addEventListener('install',  e => self.skipWaiting());
self.addEventListener('activate', e => e.waitUntil(clients.claim()));

Anyone running a service worker without an update strategy has shipped a broken app. The solution isn't "clear the cache". The solution is: implement an update strategy.

API Versioning

Frontend expects the field customerId, backend suddenly returns customer_id. White page, console errors. Not a cache problem. A versioning problem.

GET /api/v1/customers/123
GET /api/v2/customers/123

Frontend v1 talks to /api/v1. Frontend v2 to /api/v2. Both backends run in parallel until the old frontend is provably no longer in circulation. Breaking change in the data format? New version number. Not "have the user clear their cache".

Cache rule of thumb If a user sees something different after a deploy than after a hard reload,
then you made a deployment mistake — not the user's browser.
Part II · Second count of the indictment
The Cookie Bullshit
"Please delete your cookies" — or: we can't manage the state we set ourselves.

Cookies are not caching. Cookies are active application state, deposited by the app itself in the user's browser: session IDs, auth tokens, CSRF tokens, language preference, shopping cart. Written via Set-Cookie header or document.cookie. Sent back with every request. Overwritable, settable, deletable by the app on every single response.

So when the team says "delete your cookies", without realising it, they are confessing quite a lot:

"We don't know that we could recognise, migrate, invalidate, and correct the state we placed into the user's browser, at any time. We have no idea what actually happens when we write res.cookie(...). Please do what we can't — go into your browser settings and dispose of our traces personally. 💩"

This is not a bug category. This is incompetence in pure form. Cookies, as shown above, are more directly under the app's control than the cache. If a cookie is broken, stale, tampered with, or belongs to an old schema version, the application can detect that on the next request — and on the same response can either set a fresh one or delete the old one. In one single HTTP exchange. Anyone who doesn't know this doesn't know the protocol they speak all day.

Server-side session invalidation

Anyone who stores sessions as unsigned IDs in a cookie and trusts the browser to dispose of them "voluntarily someday" hasn't built session management — they've built a hope.

Correct: session IDs are kept server-side in a store (Redis, DB, in-memory — whichever). Any session can be invalidated immediately:

# Logout, force-re-auth, suspicious activity:
session_store.delete(session_id)
# Next request with this SID → 401 → re-auth flow
# The cookie in the browser is worthless from that moment.

And it is then irrelevant whether the cookie is still in the browser. It references a dead session, the server returns 401, the app redirects to login. None of this requires any user action.

Anyone telling users to "delete cookies" either has no session store, or has one and doesn't know what it's for. Both qualify as the kind of bungling that makes a senior at any serious company wonder how these people got through the interview.

Changing the schema of a cookie? Add a version number. Check it on read.

// Writing
res.cookie('prefs', JSON.stringify({
  v: 2,
  theme: 'dark',
  lang: 'de'
}));

// Reading
const parsed = safeParse(req.cookies.prefs);
if (!parsed || parsed.v !== 2) {
  // Old version, garbage, empty: ignore
  // Apply defaults, write a fresh cookie
  return DEFAULTS;
}
return parsed;

An old cookie from before the schema change? Detected on first read, discarded, overwritten with a fresh one. The user notices nothing. No login screen, no support email, no Ctrl+Shift+Del walkthrough.

Anyone doing it differently changed their cookie format without a plan — not out of malice, but because the words "migration" and "client state" first occur together to them when the support ticket stack lands on their desk.

Anyone storing cookies unsigned and trusting their contents has written a security hole and called it a feature. Cookies are signed (HMAC) or encrypted — every serious application platform of the last 15 years has built that in:

# Flask
app.secret_key = b'...'
session['user_id'] = 42   # → signed & set automatically
// Express
app.use(cookieSession({secret: process.env.SECRET}));

On read: verify the signature. On mismatch or garbage: discard it, don't "keep going somehow". Tampered, expired, or broken cookies are ignored instead of producing a half-state that the user then has to clean out via browser settings.

Here's what the mistake looks like in practice:

# Wrong: read blindly from the cookie
user_id = parseInt(req.cookies.user_id)
return db.users.find(user_id)
# → tampered cookie? Doesn't matter, we'll take it.
# → broken cookie? NaN, crash, white page.
# → "Please delete your cookies."

Clean auth flows

When an auth token "gets stuck" or a session ends up in a weird state, that is not a cookie-pollution problem. That is a broken auth flow. The established solution is in every OAuth/JWT tutorial of the last ten years:

Anyone without this and instead dictating "delete your cookies" has never really heard of OAuth, OIDC, JWT best practices, or session hardening — or has the terms on their CV without ever having understood any of them. And is now selling that as support.

Bonus: localStorage / IndexedDB — same disease

When support says "clear browser data", that often lands at localStorage, sessionStorage, or IndexedDB. These are not cookies. The argument is identical:

The app writes to it. The app reads from it. The app is responsible for it.

Store a schema version, validate on read, throw out on mismatch. Three lines.

const SCHEMA = 3;
const raw = localStorage.getItem('state');
const data = raw ? JSON.parse(raw) : null;
if (!data || data.v !== SCHEMA) {
  localStorage.removeItem('state');  // clean up after yourself
  return initFresh();
}
return data;

Anyone who doesn't do this and sends customers into the browser DevTools instead isn't "too busy to do it properly". They simply don't know that doing it properly is possible — and have never asked themselves the question, because it has never occurred to them that they themselves are responsible for the state they put there.

Cookie rule of thumb If a cookie pushes the user into a broken state,
then your app put it there — and your app has the lever to clean it up.
On every single response.

The verdict

When a team says "please clear your cache" or "please delete your cookies" at a release or in a support reply, that team demonstrates with each of those sentences that it does not master its own stack. 🤬 In detail:

💩 On the cache side: what these people don't know

💩 On the cookie side: what these people don't know

🤯 And in either case

It's not that they know about these issues and refuse to fix them. It's that they don't know there's anything to fix. They've got used to their own amateur-hour workflow and consider it normal. "Clear your cache" instructions are, in their world, a form of professional communication — not a cry for help, but a standard tool.

This is not service. This is not support. This is not diligence. This is the digital equivalent of a "tradesman" who installed the heating system without knowing what a pressure reducer is, what a mixer does, or why you need to bleed the radiators — and at the first defect says:

"If the heating fails again tomorrow, just drain the water, refill, bleed, reset the thermostat — and if you're still cold after that, throw out the control panel and buy a new one too. Goodbye. 🔧🤡"

We would sue this "tradesman", have their licence revoked, and warn everyone we know never to hire them. In the software industry, this exact category of person is the median — and they write emails to customers that are meant to look like service.

For those responsible, in plain terms

If reading this you feel caught out — because you recently wrote to customers asking them to please use Ctrl+Shift+Del on their cache and/or cookies — don't take it personally, take it professionally. The point isn't that you're a bad person. The point is that you're doing things whose technical foundation you've never internalised.

Concrete homework, in the order in which you haven't done it yet:

  1. Open your build tool. Learn what asset fingerprinting is. Turn it on if it isn't already.
  2. Open your server config. Learn what Cache-Control does. Set the two rules.
  3. Open your session code. Learn what server-side session invalidation is. Implement it.
  4. Version anything you write to cookies or localStorage. Validate on read.
  5. Build a Set-Cookie: ...; Max-Age=0 mechanism into your logout and error-recovery paths.
  6. Version your API. No more silent breaking changes.

If any of these points are unclear to you, that's the answer to why your users regularly receive browser-cleanup instructions. You now have six keywords to Google — more help isn't available here, because all of this is first-year web development.

Caching is HTTP/1.1. Cookies are RFC 2109. Both are older than most of the people building web apps today. Anyone who in 2026 still doesn't get this and operates production web apps anyway is in the wrong career — and for everyone's safety should seriously consider whether some other industry might be a better fit. 🪦