iTechGuides is reader-supported. When you buy through links on our site, we may earn an affiliate commission. As an Amazon Associate I earn from qualifying purchases. Learn more
If your chunks differ only by their #fragment, the Cache API treats them as one entry. The fragment is excluded when the cache compares URLs, so every put() to a URL such as /book.html#chunk-2 lands on the same key as /book.html#chunk-1, and the later write replaces the earlier response. The fix is to put the chunk identity into a part of the URL that matching uses (the path or the query string), or to store chunks in a key/value store your code manages.
Why the fragment never separates cache entries
A fragment is the part of a URL after #. Browsers do not send it to the server, and the Cache API does not use it to tell resources apart. The W3C Service Workers specification defines the matching step this way: “If queryURL does not equal cachedURL with the exclude fragment flag set, then return false.” In other words, the comparison deliberately drops the fragment before deciding whether two URLs match. W3C Service Workers specification
That rule is intentional. A fragment usually names a position inside one document, so the cache is not meant to store each in-page anchor as a separate resource. It becomes a problem when your application uses fragments as data identifiers, because the cache has no way to see the difference.
How the collision happens
Consider a worker that stores each chunk of a long document under its own hash URL:
#1 Best Overall
- Easily store and access 2TB to content on the go with the Seagate Portable Drive, a USB external hard drive
- Designed to work with Windows or Mac computers, this external hard drive makes backup a snap just drag and drop
- To get set up, connect the portable hard drive to a computer for automatic recognition no software required
- This USB drive provides plug and play simplicity with the included 18 inch USB 3.0 cable
- The available storage capacity may vary.
const cache = await caches.open('book-v1');
for (const n of [1, 2, 3]) {
const url = `/book.html#chunk-${n}`;
const res = await fetch(`/book.html`); // the fragment is never sent to the server
await cache.put(url, res.clone()); // all three writes share one cache key
}
const hit = await cache.match('/book.html#chunk-1');
// hit is the response stored last: the chunk-3 content, not chunk-1
The fetches themselves may succeed, and the cache reports no error. Each put() adds a request/response pair, and when the effective key is the same, the newer pair takes the place of the older one. MDN Cache The result looks like a lost-write bug, but the cache is behaving as specified.
The symptoms usually show up as one of these:
- Every chunk request returns identical content, matching whichever chunk was stored last.
- Cache Storage shows fewer entries than you expected, often just one for the whole set.
- The bug appears only offline or after the service worker serves from cache, because online requests go to the network.
Query strings follow different rules
Query strings are part of matching, unlike fragments. That makes them a workable place to encode chunk identity, but one option can undo the distinction. The ignoreSearch option of Cache.match() defaults to false. When you set it to true, query variants match as though their query strings were absent, so /book.html?chunk=1 and /book.html?chunk=2 would collapse into one lookup. MDN Cache.match()
Rank #2
- Easily store and access 5TB of content on the go with the Seagate portable drive, a USB external hard Drive
- Designed to work with Windows or Mac computers, this external hard drive makes backup a snap just drag and drop
- To get set up, connect the portable hard drive to a computer for automatic recognition software required
- This USB drive provides plug and play simplicity with the included 18 inch USB 3.0 cable
- The available storage capacity may vary.
Check that option first if you already moved identity into the query string and the collision persists.
Fix options
There are three practical approaches. Choose by whether each chunk must be a fetchable response that the browser can return for a normal request.
Rank #3
- Easily store and access 1TB to content on the go with the Seagate Portable Drive, a USB external hard drive.Specific uses: Personal
- Designed to work with Windows or Mac computers, this external hard drive makes backup a snap just drag and drop. Reformatting may be required for Mac
- To get set up, connect the portable hard drive to a computer for automatic recognition no software required
- This USB drive provides plug and play simplicity with the included 18 inch USB 3.0 cable
- The available storage capacity may vary.
Option 1: put the chunk identity in the path
This is the most natural choice when chunks are real resources. Each chunk gets its own path, and matching treats those paths as distinct keys.
const chunkUrl = (n) => `/book/chunk/${n}.html`;
for (const n of [1, 2, 3]) {
const res = await fetch(chunkUrl(n));
await cache.put(chunkUrl(n), res.clone());
}
const hit = await cache.match(chunkUrl(1)); // returns chunk 1
Your server or build step must generate those paths, and each one must exist as a response you can fetch.
Rank #4
- Easily store and access 4TB of content on the go with the Seagate Portable Drive, a USB external hard drive.Specific uses: Personal
- Designed to work with Windows or Mac computers, this external hard drive makes backup a snap just drag and drop
- To get set up, connect the portable hard drive to a computer for automatic recognition no software required
- This USB drive provides plug and play simplicity with the included 18 inch USB 3.0 cable
- The available storage capacity may vary.
Option 2: put the chunk identity in the query string
Use this when the chunk is served from one route with a parameter. Leave ignoreSearch at its default so the query remains part of matching.
Quick wins for a faster PC:
Clear out junk files and repair common Windows errorsFree Scan →Scan for outdated or missing drivers - takes under a minuteDriver Scan →Repair Windows errors before they cause bigger problemsFix Now →const chunkUrl = (n) => `/book.html?chunk=${n}`;
await cache.put(chunkUrl(2), (await fetch(chunkUrl(2))).clone());
const hit = await cache.match(chunkUrl(2)); // ignoreSearch is false by default
Do not pass { ignoreSearch: true } to match() for these keys. That option would reduce every chunk to /book.html.
Best Value
- [Upgraded Version] - This external hard drive features a mirrored logo stripe combined with a striped anti-slip design, and the rounded corners of the casing make it easier to grip. The stripes also have a heat dissipation function, ensuring stable and fast data transfer.
- 【Ultra-thin and quiet】 - The motherboard adopts JMicron 578 noise-free solution, giving you a quiet working environment. Lightweight and portable size designed to fit in your pocket for easy portability.
- 【Ultra-Fast Data Transfers】 - Pairing this external hard drive with JMicron 578 solution USB 3.0 and USB 2.0 interfaces enables blazing-fast data transfer. It boasts theoretical read speeds of up to 125MB/s and write speeds of up to 103MB/s.
- 【Plug and Play】 - With no software to install, just plug it in and the drive is ready to use.The hard disk chip is wrapped with an aluminum anti-interference layer to increase heat dissipation and protect data.
- 【What You Get】 - 1 x Portable Hard Drive, 1 x USB 3.0 Cable, 1 x User Manual, Gift-type shell packaging ,Three-year manufacturer's warranty and free technical support services.
Option 3: use an application-managed key/value store
If the chunks are data rather than fetchable responses, store them in a key/value store that your code controls, such as IndexedDB. You define the key, so no URL rule applies. You also become responsible for versioning, cleanup and storage limits, which the Cache API partly handles for you through its request/response model.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Debugging checklist
- Log the full URL you pass to
put()and tomatch(), including the fragment. - Remove the fragment from each URL in your head or in code. If two URLs become identical, the fragment is the cause.
- Open DevTools, go to the Application tab, and expand Cache Storage to list the entries actually stored for your origin.
- Confirm that
match()is not called withignoreSearch: truewhen chunks differ only by query. - Move the identity into the path or query, clear the old cache by name, and repeat the test.
Lifecycle cautions
Fixing the key does not finish the job. The Cache API stores what you tell it to store and nothing more:
- Entries do not expire or refresh automatically, and the API does not honor HTTP caching headers. Your code must delete outdated chunks.
- Version cache names, such as
book-v1, when the worker’s chunking logic changes, and delete old caches during activation. - Browsers may evict an origin’s cache data under storage pressure, so chunks should be treated as recoverable and fetched again when missing.
These points come from the MDN Cache reference. MDN Cache The behavior described here comes from the specification and API reference, not from a browser test of this exact project, so confirm the result in your own target browsers before you rely on it.
Windows Errors? Fix Them Before They Spread
Repair common Windows errors and clear accumulated junk for a smoother, more stable PC - no reinstall needed.Free scan · no reinstallCrashes, No Sound, or Screen Glitches?
Random freezes, missing sound and display glitches usually trace back to one bad driver. Find and replace yours safely.Free scan · under a minuteQuick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

