If your HTML code works online but not on localhost, you are almost certainly running into one of a handful of environment differences that trip up developers at every level. This guide explains all of them — clearly.
You’ve built something that works perfectly on a live server or on an online code editor like CodePen, then you copy the exact same files to your computer, open them in the browser — and nothing works. No styles, no JavaScript, no API responses. Just broken.
This is one of the most common and most confusing moments in learning web development. Your code didn’t change. What changed is the environment it’s running in — and online versus localhost is a bigger difference than most beginners realize. Here’s every reason it happens and the exact fix for each one.
Root Cause: You’re opening HTML files directly with file://
This is the single most common reason HTML code works online but not on localhost. When you double-click an HTML file in your file explorer, your browser opens it using the file:// protocol — not http:// or https://. These are fundamentally different environments with different security rules.
Online (http/https)
Files are served by a web server. The browser treats the page as a normal web document. JavaScript can make requests, load assets, and access most APIs normally.
Opened directly (file://)
No web server involved. The browser applies strict security restrictions — many features are blocked or behave differently. CORS, fetch, modules, and certain APIs all fail here.
The URL bar tells you which one you’re in. If it starts with file:///C:/Users/... or file:///home/..., you are using the file protocol. If it starts with http://localhost or http://127.0.0.1, you have a real local server running.
? The fix
Run a local web server instead of opening files directly. See section 10 for the fastest ways to do this — some take under 30 seconds to set up.
Very Common: CORS blocks requests from file://
CORS — Cross-Origin Resource Sharing — is the browser’s way of controlling which pages can request data from which sources. When your page is loaded via file://, it has no real origin, and browsers treat it as if it’s cross-origin to everything. This causes fetch and XMLHttpRequest calls to fail with CORS errors even when they work perfectly online.
You’ll see this in the browser console:
Console error
Access to fetch at 'https://api.example.com/data' from origin 'null' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present.
Notice origin 'null' — that’s the browser’s way of saying the page has no real origin because it’s running from the file system. The remote server sees a null origin and, unless it explicitly allows it, rejects the request.
This will CORS-fail when opened via file://
|
1 2 3 4 5 |
// Works fine when served from localhost:3000 or a live server // Fails with CORS error when opened as file:///index.html fetch('https://api.example.com/users') .then(res => res.json()) .then(data => console.log(data)); |
The fix is always the same: run a local web server so your page is served from http://localhost, which gives it a real origin that APIs can respond to. There is no way to disable CORS restrictions in the browser from your code — they’re a security feature.
Don’t do this
Some tutorials suggest launching Chrome with --disable-web-security to bypass CORS locally. This disables all security in that browser session and is dangerous. Use a local server instead — it’s the correct solution and takes less time than the workaround.
Path Issue: Absolute paths that work online break locally
When your code uses absolute paths starting with /, they mean different things online versus locally.
Online
/css/style.css resolves to https://yoursite.com/css/style.css — the root of your domain. Works correctly.
Localhost (file://)
/css/style.css resolves to the root of your file system — like C:\css\style.css on Windows. File doesn’t exist. Silently fails.
Breaks locally when opened via file://
|
1 2 3 4 |
<!-- Absolute path — works online, fails with file:// --> <link rel="stylesheet" href="/css/style.css"> <script src="/js/app.js"></script> <img src="/images/logo.png"> |
Relative paths work in both environments
|
1 2 3 4 5 6 7 |
<!-- Relative path — works with file:// and localhost --> <link rel="stylesheet" href="css/style.css"> <script src="js/app.js"></script> <img src="images/logo.png"> <!-- If your HTML is in a subfolder, use ../ to go up a level --> <link rel="stylesheet" href="../css/style.css"> |
Best practice
If you run a local server (see section 10), absolute paths starting with / will work correctly because / resolves to your server root rather than your OS root. Running a local server removes most path inconsistencies between environments.
HTTPS Required: APIs and browser features that require HTTPS
Several modern browser APIs and many third-party services only work on secure origins — meaning pages served over https://. When those features are used on a local file:// or even an http://localhost page, they simply don’t work.
| Feature | Works on file:// | Works on http://localhost | Requires https:// |
|---|---|---|---|
| Geolocation API | ? Blocked | ? Allowed | In production, yes |
| Service Workers | ? Blocked | ? Allowed | In production, yes |
| Camera / Microphone | ? Blocked | ? Allowed | In production, yes |
| Web Push | ? Blocked | ? Allowed | Always |
| Payment Request API | ? Blocked | ? Allowed | Always |
| Clipboard API | ? Blocked | ? Allowed | In production, yes |
| ES Modules (import) | ? Blocked | ? Allowed | No |
| fetch() requests | ? CORS fails | ? Works | Depends on API |
The good news: localhost is treated as a secure origin by all major browsers, even without HTTPS. So running a local server at http://localhost:3000 unlocks almost all of these features for development purposes.
Setup Issue: You simply don’t have a local server running
If your project was built to run on a server — even a simple static file server — and you open it with file:// instead, it will behave differently. This is especially true for projects that use JavaScript modules (import / export), fetch requests, or any kind of routing.
The most reliable fix for “works online, broken locally” is almost always the same: stop opening files directly and run a local server instead.
See section 10 for the fastest setup options.
Config Issue: Missing environment variables
If your project uses environment variables for API keys, database URLs, or feature flags, those variables exist on the live server but may not be configured on your local machine. The code tries to read them, gets undefined, and fails.
API key is undefined locally — request fails
|
1 2 3 4 5 |
// process.env.API_KEY is undefined locally if .env isn't set up const apiKey = process.env.API_KEY; fetch(`https://api.example.com/data?key=${apiKey}`) // Sends: ?key=undefined — API rejects this |
Create a .env file for local development
|
1 2 3 4 |
# .env file in your project root (never commit this to git) API_KEY=your_development_api_key_here API_URL=https://api.example.com DEBUG=true |
Security
Always add .env to your .gitignore file so API keys and secrets never get pushed to a public repository. Use separate keys for development and production where possible.
Path Issue: File path case sensitivity
Windows file systems are case-insensitive: Style.css, style.css, and STYLE.CSS all refer to the same file. Linux and macOS file systems (where live servers almost always run) are case-sensitive: those are three different files, and only the exact match loads.
This creates a specific pattern: code works on your Windows machine locally, gets deployed to a Linux server, and breaks. Or the reverse — you develop on macOS (case-sensitive), it breaks on Windows for a teammate.
Case mismatch — works on Windows, breaks on Linux server
|
1 2 |
<!-- Actual file on disk: /Images/Hero.jpg --> <img src="images/hero.jpg"> <!-- Works on Windows, 404 on Linux --> |
Prevention
Use all-lowercase filenames and folder names consistently across your whole project. This eliminates the problem permanently and is considered standard practice in professional web development.
Security Policy: Browser security restrictions on file://
Beyond CORS, several other browser security restrictions specifically target the file:// protocol. These are restrictions that don’t apply when a page is served from a real HTTP server:
- JavaScript ES Modules —
import/exportsyntax fails entirely underfile://. You’ll seeCross-origin request blockedeven when all files are local. - Local Storage and IndexedDB — Some browsers restrict or sandbox these when running under
file://. - Web Workers — Cannot be loaded from the file system in most browsers.
- Canvas cross-origin images — Drawing images onto a canvas from local files throws a security error.
- Cookies — Not set or read properly under
file://.
Every single one of these works correctly when the page is served from http://localhost.
Architecture: Features that depend on a backend
Some things your online version does simply cannot work locally without running the corresponding backend service. If your site uses any of these, the frontend alone — even served from localhost — will not fully work:
- Server-side rendering (PHP, Node.js, Python, Ruby, etc.)
- Database queries (the frontend can’t talk directly to a database)
- Authentication that uses server-side sessions or cookies
- File uploads processed on the server
- Email sending, payment processing, or other server-only operations
- URL routing handled by server configuration (like a
.htaccessfile)
For these, you need to run the full stack locally — not just open the HTML file. That means running your Node.js app, PHP server, or other backend alongside your frontend.
The Fix: How to set up a local web server
If you take away one thing from this guide, let it be this: run a local server instead of opening HTML files directly. Here are the fastest ways to do that, depending on what you have installed:
VS Code Live Server
Extension that auto-refreshes on save. Best option for beginners — install and click “Go Live”.
Install: “Live Server” extension
Python http.server
Built into Python. No install needed if you have Python.
python -m http.server 8080
npx serve
Zero-config static file server via npm. Works great for any static project.
npx serve.
Node.js http-server
Install once, use everywhere. Fast and configurable.
npx http-server
Vite
Modern dev server with hot module replacement. Best for modern JS projects.
npx vite
PHP Built-in Server
Perfect if your project uses PHP. No Apache/Nginx needed.
php -S localhost:8080
After starting any of these, open your browser and go to http://localhost:8080 (or whatever port the tool tells you). Your page now runs on a real HTTP server and almost all environment differences disappear.
Recommended for beginners
Install the Live Server extension for VS Code. Right-click your HTML file in VS Code’s file explorer and choose “Open with Live Server.” Your page opens at http://127.0.0.1:5500, auto-refreshes on every save, and works just like a real server. Takes about 60 seconds to set up total.
Troubleshooting Checklist
When your HTML code works online but not on localhost, go through these in order:
- Check your URL bar — does it start with
file:///? If yes, you’re not using a server. Run a local server and access the page viahttp://localhostinstead. - Open the browser console (F12) — look for CORS errors, 404s, or module import errors. Each one points to a specific cause above.
- Check all file paths — are you using absolute paths starting with
/? Switch to relative paths or run a local server to make absolute paths work correctly. - Does your project use
import/export(ES Modules)? These require a real HTTP server — they will not work underfile://. - Check file name casing — does every
srcandhrefmatch the exact capitalisation of the filename on disk? - Does your project use environment variables? Create a local
.envfile with development values for all variables your code reads. - Does your project use PHP, Node, a database, or server-side logic? You need to run the full backend stack locally — not just the HTML files.
- Test in a different browser to rule out a browser-specific extension or setting causing the issue.
Frequently Asked Questions
Q1. Why does my HTML code work online but not on localhost?
The most common reason is that locally you’re opening files with the file:// protocol instead of running a proper local server. File:// has strict security restrictions — CORS, ES Modules, fetch requests, and several browser APIs all fail or behave differently. Running a local server at http://localhost fixes almost all of these issues immediately.
Q2. How do I fix CORS errors on localhost?
Run a local web server so your page is served from http://localhost rather than opened via file://. The file protocol has no real origin (it shows as “null”), which causes all cross-origin requests to fail. Serving from localhost gives your page a real origin that API servers can respond to. Tools like VS Code Live Server, Python’s http.server, or npx serve take under a minute to set up.
Q3. My JavaScript import statements work online but fail locally. Why?
ES Module import / export statements require pages to be served over HTTP — they are explicitly blocked when using the file:// protocol. The browser sees module imports as cross-origin requests (even if they’re to local files) and blocks them. The fix is to run a local server. This is one of the most common issues when moving from online editors like CodePen to a local setup.
Q4. My site works locally but breaks after uploading. What’s different?
The most common causes going the other direction are: case-sensitive file paths (Linux servers are case-sensitive, Windows local machines are not), missing environment variables on the server, HTTPS mixed content (live server forces HTTPS but some assets use HTTP), or a missing server configuration file like .htaccess for URL routing.
Q5. Does http://localhost count as a secure origin?
Yes. All major browsers (Chrome, Firefox, Safari, Edge) treat localhost and 127.0.0.1 as secure origins for development purposes, even without HTTPS. This means browser APIs like Geolocation, Camera, Service Workers, and Clipboard all work at http://localhost without needing to set up a local SSL certificate.
Q6. What is the fastest way to run a local server?
If you use VS Code, install the Live Server extension — right-click your HTML file and choose “Open with Live Server.” If you have Python installed, run python -m http.server 8080 from your project folder. If you have Node.js, run npx serve . All three take under two minutes to start and immediately fix the file:// environment issues.
The core insight
The phrase “works online but not on localhost” almost always means the same thing at its root: the online version is being served by a web server, and the local version is not. A web server is not optional for modern web development — it’s the environment your code is designed to run in. Opening HTML files directly is a shortcut that works for the simplest pages and fails for everything else.
Setting up a local server takes less than five minutes with any of the tools in section 10. Once you do it, most localhost problems vanish immediately — and you gain an environment that closely mirrors production, which means fewer surprises when you deploy.
