Build a service worker precache list from static asset paths
Paste the file paths your site should keep for offline use and get a clean, ordered list: /assets/./app.js becomes /assets/app.js, a repeated stylesheet is dropped, and the sample's five entries become 4 with 1 duplicate removed. Free in your browser.
- Free
- Runs in your browser
- No sign-up
- Your data stays on your device
Run — free
A reliable service worker install starts with a clean list of files to cache. This builder accepts static asset paths, converts relative entries to root-relative URLs, resolves harmless dot segments, preserves meaningful query strings, and removes duplicates without changing the first-seen order. The result is a precache manifest array ready to pass to Cache Storage's addAll method, plus counts that make automated build checks easy to understand. Invalid remote URLs, fragments, and paths that escape the site root are rejected before they reach deployment.
Turn build output into an install-ready list
Modern web builds can produce HTML, stylesheets, JavaScript chunks, fonts, icons, and fallback pages in several directories. Copying those names manually into a service worker is slow and creates subtle drift whenever a file moves. The first-seen order is retained because it is often useful when reviewing generated files or comparing manifests between builds.
The response also reports the final asset count and how many duplicate entries were removed. Those numbers are convenient in CI: a surprising drop in asset count can fail a build before an incomplete offline release reaches users. The tool does not download, fingerprint, or inspect any asset.
It transforms the paths you already obtained from a trusted build process.
Normalization that stays predictable
Asset paths often arrive in slightly different spellings even when they identify the same file. For example, <code>assets/app.js</code>, <code>/assets/app.js</code>, and <code>/assets/./app.js</code> all become <code>/assets/app.js</code>. Repeated slashes are collapsed, safe parent-directory segments are resolved, surrounding whitespace is removed, and duplicate normalized results are omitted after their first appearance.
Query strings remain attached because <code>/app.js? Fully qualified and protocol-relative URLs are also rejected: this capability is specifically for static files hosted by the same site, not for copying third-party resources into an offline cache. A parent segment that would move above the site root fails validation instead of being silently rewritten.
These rules make output deterministic across machines and avoid guessing about deployment origins, asset contents, or external availability.
Use the manifest safely in a service worker
Place the returned array in the install path that opens your versioned cache and calls <code>addAll</code>. Keep the cache name versioned separately, because this builder cannot know when file contents change while a path stays the same. If your bundler emits content hashes in filenames, include those emitted paths and let the cache name represent the release.
If it uses stable filenames, update the cache version whenever their contents change. A very large install list increases download time and makes installation more likely to fail on unreliable connections. The builder accepts up to 10,000 entries as a defensive processing limit, but a production list should usually be much smaller and focused.
During activation, delete obsolete cache versions according to your own retention policy. This tool creates the list only; it does not generate event listeners, choose caching strategies, access the network, or modify an existing service worker file.
What you can do with it
Generate an application-shell list
Normalize the HTML, CSS, JavaScript, font, and icon paths required to start a progressive web app offline.
Check build artifacts in CI
Convert collected artifact names into stable output and assert the returned asset and duplicate counts before deployment.
Clean a hand-maintained manifest
Resolve inconsistent relative paths and remove accidental duplicates while preserving the intended installation order.
FAQ
What does it cost?
The same deterministic builder can also run in your browser on this page.
Does this tool download or verify the assets?
No. It validates and normalizes path strings without making network requests or checking whether files exist.
Why are query strings preserved?
A query string can select a distinct cache key or asset version, so removing it could merge resources that the caller intended to keep separate.
Why are remote URLs rejected?
The capability builds a same-site static asset list. Cross-origin caching has different availability, CORS, privacy, and failure considerations that require an explicit strategy.
Why should I version the cache name separately?
The list only looks at paths, so it cannot tell when a file's content changes while its name stays the same. If your file names carry content fingerprints, name the cache after the release; if names are stable, change the cache version whenever their contents change.
Related tools
For developers — API access
Everything on this page is available programmatically. This section is for teams who want to wire it into their own systems; everyone else can just use the tool above.
API endpoint
Prefer to automate it? One authenticated POST creates the task; the result comes back by webhook or a signed link. The same capability also runs here on the web, by email and from Telegram — and soon from our app too.
Call it from your stack
curl -X POST https://api.kit.forhosting.com/web/service-worker-cache-list-build \
-H "Authorization: Bearer $KIT_KEY" \
-H "Content-Type: application/json" \
-d '{"paths":["/","/assets/app.css","assets/app.js","/icons/icon-192.png"]}'const res = await fetch("https://api.kit.forhosting.com/web/service-worker-cache-list-build", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.KIT_KEY}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
"paths": [
"/",
"/assets/app.css",
"assets/app.js",
"/icons/icon-192.png"
]
})
});
const { task_id } = await res.json();import os, requests
res = requests.post(
"https://api.kit.forhosting.com/web/service-worker-cache-list-build",
headers={"Authorization": f"Bearer {os.environ['KIT_KEY']}"},
json={
"paths": [
"/",
"/assets/app.css",
"assets/app.js",
"/icons/icon-192.png"
]
},
)
task_id = res.json()["task_id"]<?php
$res = file_get_contents("https://api.kit.forhosting.com/web/service-worker-cache-list-build", false, stream_context_create([
"http" => [
"method" => "POST",
"header" => "Authorization: Bearer " . getenv("KIT_KEY") . "\r\nContent-Type: application/json",
"content" => '{"paths":["/","/assets/app.css","assets/app.js","/icons/icon-192.png"]}',
],
]));
$task = json_decode($res, true);body := bytes.NewBufferString(`{"paths":["/","/assets/app.css","assets/app.js","/icons/icon-192.png"]}`)
req, _ := http.NewRequest("POST", "https://api.kit.forhosting.com/web/service-worker-cache-list-build", body)
req.Header.Set("Authorization", "Bearer "+os.Getenv("KIT_KEY"))
req.Header.Set("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)Example request
{
"paths": [
"/",
"/assets/app.css",
"assets/app.js",
"/icons/icon-192.png"
]
}Example response
{
"task_id": "tsk_a1b2c3d4e5f6a1b2c3d4e5f6",
"type": "web.service_worker_cache_list_build",
"status": "queued",
"_links": {
"result": "/tasks/tsk_…/result"
}
}The API is asynchronous: the call returns a task_id immediately and the result arrives by webhook. Polling is capped at 1 req/s per task.
Pricing
Published price — no tokens, no invented credits. A failed task is never charged.
Limits
timeout_sec | 30 |
max_crawl_pages | 25 |
Errors
| HTTP | Code | Meaning |
|---|---|---|
401 | unauthorized | Missing or invalid API key. |
402 | insufficient_balance | Your balance doesn't cover the task price. |
404 | unknown_type | That task type doesn't exist. |
429 | rate_limited | Too many requests. Use the webhook instead of polling. |
Technical notes
Turn build output into an install-ready list
Supply the static asset paths in the order you want them fetched, and this builder returns a service worker precache list under the <code>precache_manifest</code> key. Every entry is a local, root-relative URL, so the array can be passed directly to <code>cache.addAll(precache_manifest)</code> during the install event.
Normalization that stays predictable
v=41</code> and <code>/app.js? v=42</code> may intentionally represent different cache entries. URL fragments are rejected because fragments are not sent in HTTP requests and can make a manifest look distinct without identifying different network resources.
Use the manifest safely in a service worker
Treat the input as a deliberate allowlist: include the application shell and assets required for the offline experience, but avoid user-specific pages, authenticated API responses, or unbounded runtime URLs.
Technical questions
Can I pass the result directly to cache.addAll?
Yes. The precache_manifest value is an array of local URL strings suitable for Cache Storage's addAll method.
What happens when the path list is empty?
The request fails with an invalid_input error because an empty precache manifest cannot fulfill this capability's purpose.