The root cause, in one sentence
A copied site breaks offline because some reference in it still resolves to the original server, and the browser is happy to follow that reference as long as it has a network. On the day you make the copy everything works, because the origin is still up. Disconnect, or wait for the origin to move, and the leaks show.
The reason this is hard is not the images in the HTML -- anyone finds those. It is the references that live in places a text search for <img never looks.
Where references hide
| Where | Looks like | What breaks offline |
|---|---|---|
HTML src, href, poster | <img src="https://cdn…/hero.jpg"> | Images, scripts, stylesheets, video posters |
srcset and sizes | srcset="…/a.jpg 640w, …/a.jpg 1280w" | One width works, the others 404 as the viewport changes |
CSS url() | background: url(…), @font-face { src: url(…) } | Backgrounds and every custom font |
| JavaScript strings | import("https://…/m/Ticker.js"), fetch("/api/…") | Animations, components, data loaded at runtime |
| JSON | "image": "https://…" | Anything a script renders from data |
| SVG | <use href="…#icon">, <image href=…> | Icons and illustrations |
manifest.webmanifest, og:image | "icons": [{"src": …}] | PWA icons, social previews |
What you lose when each kind of reference breaks
| Share | Value |
|---|---|
| Images | 35.6% |
| JavaScript | 24.7% |
| Fonts | 4.8% |
| CSS | 3% |
| HTML | 0.9% |
| Other (video, JSON…) | 31% |
Two references a grep for <img never finds
@font-face {
font-family: "Satoshi";
src: url(https://framerusercontent.com/assets/8b0c…/Satoshi-Variable.woff2) format("woff2");
font-display: swap;
}
.hero {
background-image: url("https://framerusercontent.com/images/2f1a…/bg.jpg?scale-down-to=2048");
}Source: MDN — @font-face
Two things that make a good copy look broken
file://. Double-click index.html and the browser opens it from the file system. ES modules (<script type="module">) are blocked there by the same-origin rules, and so are fonts and fetches. A perfectly localized site can show no animations and fallback fonts from file:// and be flawless over HTTP. Always test with a local server: python3 -m http.server 8080 is one line and needs nothing installed.
A service worker. If the site registered one, your browser may keep serving cached assets for a while after you go offline, which hides a leak until the cache expires. In DevTools → Application → Service Workers tick Bypass for network, or test in an incognito window.
Source: MDN — Cross-Origin Resource Sharing · MDN — Service Worker API
Diagnose it in DevTools
Serve the folder over HTTP
cdinto it,python3 -m http.server 8080, openhttp://127.0.0.1:8080. Neverfile://.Hard-reload with Offline on
Network panel → throttle Offline → Cmd/Ctrl+Shift+R. Note what is missing: it tells you which of the seven places the leak is in.
List the foreign hosts
Throttle back to online, reload, filter the Network panel with
-domain:127.0.0.1. Sort by domain. Each remaining host is a dependency.Find where it is written
For each host, search the folder:
grep -rl "framerusercontent.com" . --include=*.html --include=*.css --include=*.js --include=*.json --include=*.svg. The file list is your rewrite list.
What localization rewrites
A copy is real when
- It renders completely over HTTP with the Offline throttle on.
- The Network panel, filtered to foreign domains, shows only services you added on purpose.
grepfor the original hosts across HTML, CSS, JS, JSON and SVG returns nothing you did not expect.- Fonts are the design's fonts at every width, and the phone layout opens its menu.