Double-click an HTML file and the browser may open a URL such as file:///C:/tools/index.html. Serve that same file from a local web server and the URL may instead be http://localhost:8080/. Strictly speaking, file: is a URL scheme while localhost is a hostname, so this article compares opening a page with file:// against serving it from http://localhost. Because the HTML bytes are the same, it is natural to expect identical behavior.
In practice, JavaScript modules or fetches of neighboring files can fail under file:// and work under localhost. At the same time, being able to open a self-contained HTML file directly is useful in its own right. Browser Kitty tests these as separate execution environments. The key difference is not the HTML file itself, but the URL, origin model, and HTTP conditions the browser applies to it.
Four things to separate first
Read the file directly
The browser opens the HTML directly from local storage without a web server. There is no HTTP response, and file URLs have special origin and storage behavior.
Receive it over HTTP from your own device
The browser makes an HTTP request to a local web server and receives the HTML as a response. In addition to normal origin behavior, the server can set HTTP response headers for Content-Type, range handling, CSP, and related web-delivery controls.
Secure context is a separate axis
Browsers treat localhost as trustworthy for development, and file URLs can also be potentially trustworthy. But being a secure context does not mean every API is available.
Local processing is another separate axis
A file:// page can still contact external services when CORS and other conditions on the receiving side allow it, while a localhost app can process everything without external requests. Locality is determined by the real data path, not by the URL alone.
The same HTML can still run in a different browser environment
In short, the browser can apply different rules depending on how the document was reached, even when the HTML contents are identical. With file:// the browser opens a filesystem resource directly; with localhost it receives the document as an HTTP response from a web server.
That difference affects origins, CORS, HTTP headers, Web API requirements, and storage behavior. This is why changing only how the same file is opened can change the result.
Identical HTML can face different browser rules when the URL scheme and delivery method change
file:// is direct file access; localhost is HTTP delivery
When opened with file:// there is no web server in between. HTML, JavaScript, images, and other resources are referenced as local files. With localhost the browser sends HTTP requests to a web server running on the same device and receives resources as HTTP responses.
Using localhost does not by itself mean uploading anything to a cloud service; localhost normally refers back to the same device. However, code in that page can still request external URLs, so localhost does not automatically mean there is no external communication.
| Aspect | file:// | http://localhost |
|---|---|---|
| Loading | Read the file directly | Receive it over HTTP from a local web server |
| Origin | Usually treated as an opaque origin | Normal origin based on scheme, host, and port |
| HTTP response | None | Available |
| Response headers | No HTTP response, so there are no HTTP response headers | Can return CSP, Content-Type, range-related settings, and more as HTTP response headers |
| Single HTML | Well suited to double-click use | Well suited to testing web-delivery behavior |
One major difference is the origin model
For ordinary web pages, an origin is broadly defined by the combination of scheme, hostname, and port. Resources delivered from the same http://localhost:8080 origin can therefore participate in normal same-origin rules.
File URLs are special. MDN notes that modern browsers usually treat files loaded with file:/// as opaque origins. An opaque origin is not considered equal to another origin, so another file in the same folder is not automatically treated as same-origin. The exact handling of file origins also has implementation-dependent aspects.
JavaScript modules and fetch often expose the difference
JavaScript modules are a clear example. Loading a module from another local file under file:// can hit CORS errors because of module security requirements. MDN explicitly recommends testing modules through a server rather than by opening the HTML as a local file.
The same issue can appear when fetch is used to read a neighboring JSON or text file. Merely placing two files in the same folder does not guarantee that JavaScript may read one from the other. With localhost, the server can deliver both resources from the same HTTP origin.
localhost has an HTTP response layer
Because localhost uses a web server, responses can carry headers such as Content-Type, Content-Length, Content-Range, Content-Security-Policy, Cross-Origin-Opener-Policy, and Cross-Origin-Embedder-Policy when needed. With file:// there is no HTTP response from a server in the first place.
Media playback makes this difference concrete. HTTP range requests let media players and other clients retrieve only the needed part of a large resource. Browser Kitty's local preview server handles range requests for demo videos. During development, seeking could cause the browser to abandon an older range connection, and the server originally raised an exception when it tried to keep writing to that closed connection. The local web server's behavior is therefore part of the localhost environment, not just a way to locate the HTML.
Secure context alone does not explain the difference
It is inaccurate to reduce the issue to 'file:// is insecure while localhost is secure.' Secure Context rules treat loopback locations such as localhost as potentially trustworthy for development, and the file scheme can also be considered potentially trustworthy.
So secure-context status alone cannot explain file:// versus localhost. Origins, URL schemes, CORS, API-specific restrictions, permissions, and user activation may all be separate conditions.
An API can still reject file:// even in a secure context
Service workers are a useful example. They are restricted to secure contexts, but register() has additional requirements: MDN documents a TypeError when the script URL or scope uses a scheme other than http: or https:. localhost can therefore support service workers as a local development secure context, while a file:// page cannot simply register a file: service-worker script.
Being in a secure context is only one part of an API's requirements. Whether a feature works under file:// depends on the conditions defined by that API as well.
localStorage also has special file:// behavior
On normal web pages, localStorage is partitioned by origin. A page opened at http://localhost:8080 can therefore be reasoned about in terms of that origin's storage area.
For documents loaded from file: URLs, MDN says localStorage behavior is undefined and may vary between browsers. Current implementations may appear to create separate storage areas per file URL, but that is not a behavior applications should rely on as a stable guarantee. This matters when a standalone HTML tool stores preferences or work state.
Why Browser Kitty tests file:// and localhost separately
Browser Kitty packages tools as single HTML files when practical. Embedding JavaScript, CSS, SVG, workers, Wasm, and other assets reduces the need to fetch or import neighboring files and makes direct file:// use more feasible. That helps distribution, but it also reduces exposure to local-file origin restrictions.
We still test through localhost during development. MIME types, range requests, and behavior that depends on HTTP response headers such as CSP, COOP, and COEP can only be exercised meaningfully through HTTP delivery. Success as a standalone file does not guarantee identical hosted behavior, and success on localhost does not guarantee identical double-click behavior.
When identical HTML behaves differently, inspect the address bar as well as the code. Is it file://, http://localhost, or a deployed https:// URL? On the web, that difference is part of the runtime environment.
Developer Toolbox
A local developer toolbox with 33 utilities for Base64, JSON, JWT, cron, regex, hashes, text, web formats, and more.
Tips and limitations
- 'Works from file://' and 'fully local processing' are different claims. A file:// page can still run code that contacts external URLs when CORS and other receiving-side conditions allow it.
- localhost normally loops back to the same device, but it does not block external requests made by the page. Check those separately with tools such as the Network panel and CSP.
- Bundling an app into one HTML file reduces cross-file loading problems, but it cannot bypass API requirements that specifically require http:, https:, or particular origin conditions.
Frequently asked questions
Does using localhost send my files to the internet?
Serving HTML from a web server on localhost normally uses loopback communication on the same device. However, JavaScript in the page can still contact external APIs or resources, and those requests must be evaluated separately.
Does file:// automatically mean fully local processing?
No. file:// tells you that the document itself was opened from a local file, but it does not guarantee that the page makes no external requests. Fully local processing depends on the actual runtime data and network paths.
If file:// can be a secure context, why can't it register a service worker?
Because secure-context status is only one requirement. ServiceWorkerContainer.register() also restricts the script and scope URL schemes to http: or https:. localhost can satisfy that as a trustworthy local HTTP origin.
Will every single-HTML app work under file://?
Not always. Bundling removes many cross-file fetch and import problems, but Web APIs can still impose origin, scheme, permission, user-activation, or other requirements.
Should I develop with file:// or localhost?
For software intended to run as a website, localhost is the normal development baseline. If you also distribute a standalone HTML file intended for direct opening, test the important workflows under file:// as a separate target.
References
The references below cover file URL origins, JavaScript modules, secure contexts, service workers, localStorage, and HTTP range requests. Browser Kitty-specific observations come from the local development server and standalone-HTML workflow used as of v168.
- MDN Web Docs Same-origin policy — File origins
- MDN Web Docs JavaScript modules
- W3C Secure Contexts
- MDN Web Docs ServiceWorkerContainer: register() method
- MDN Web Docs Window: localStorage property
- MDN Web Docs HTTP range requests
- MDN Web Docs How do you set up a local testing server?