HAP wearing a safari outfit and pith helmet, looking through binoculars

Explanation

Why a dev server, and what a port is

The difference between opening a file and requesting a page

If you double-click an HTML file, it will open, but a set of browser features quietly stops working. This page covers why that happens, what a development server changes, and what a port number is.

Two Ways To Open The Same File

The address bar in each case:
file:///Users/you/projects/my-site/index.html
http://localhost:3000/index.html

Both methods load the same file, same bytes, and same browser. In the first case the browser reads a file off the disk directly. In the second it sends a request over the network to a program that answers it, even though that program happens to be running on the same machine.

The word before the colon is the scheme, and it decides which of those two things happens. That one word is why the same page behaves differently in the two cases.

What Breaks Under file

Browsers apply security rules based on a page's origin, which is the combination of scheme, host, and port. Two pages share an origin when all three match, and code from one origin is restricted in what it can do to another.

Where an origin comes from:
http://localhost:3000/about/index.html
└┬─┘   └───┬───┘ └┬─┘
 │         │      └── port
 │         └───────── host
 └─────────────────── scheme

The first three together are the origin.

A page loaded from disk has no host and no port, so browsers treat it as having an opaque origin that matches nothing, including other files in the same folder. Several common things stop working as a result.

Fetching another file fails

A request to load a JSON file of content, or any other data the page needs, counts as a cross-origin request under file. The browser blocks it and reports a CORS error, which is confusing because there is no other site involved.

Module scripts fail

A script tag with type="module" is loaded under the same origin rules. Under file, the import is blocked. Older non-module scripts still work, which is why a page can half-function.

Absolute paths break

A link or image path starting with a slash means "the root of this site." Under file, that is the root of your hard drive, so the file is not found.

Storage and workers are limited

Service workers refuse to register, and browsers treat storage for opaque origins inconsistently. Behavior differs between Chrome and Firefox, which makes the symptom hard to search for.

HAP looking sad while holding tangled wires

🟠 Nothing looks broken, which is the hard part

The HTML renders and the CSS applies, so only the parts that needed to fetch something are missing. Because the reason is logged in the console rather than on the page, checking the console first is best when a page half-functions.

What A Development Server Is

A development server is a small program that watches a folder, listens for HTTP requests, and answers them with files from that folder. It is not a website host, and nothing on the internet can reach it. Its one job is to make your own files arrive over http instead of file, which gives the page a real origin and restores everything on the list above.

Most of them add conveniences on top: reloading the browser when a file changes, printing each request as it comes in, serving index.html when a folder is requested. Useful as those are, the origin is the reason you need the server at all.

localhost

This is a name that always means this computer. It resolves to 127.0.0.1, which is an address that never leaves the machine.

Not on the internet

Nobody else can open your localhost. Sending that address to a classmate points at their machine, not yours.

It holds the terminal

A server runs until stopped. You can stop the server by pressing Ctrl and C. The prompt not coming back is correct.

It serves, it does not build

A dev server hands out whatever is currently in the folder. Generating those files is a separate step that runs first.

Different from production

A real host serves the built output to the public over HTTPS, from a machine that is not yours. The dev server is a stand-in so you can see the result before that happens.

Any of them will do

The VS Code Live Server extension, a Node server package, and a server built into a framework all solve this the same way.

What A Port Number Is

One computer has one network address but runs many programs that want to talk over the network. A port number is how the machine knows which program an incoming request belongs to. You can think of the address as the building and the port as the apartment number.

Ports run from 1 to 65535. Some are conventional: 80 for http, 443 for https. Browsers assume those two, which is why a normal web address has no port in it. Development servers pick high numbers that nothing else claims, which is where 3000, 5173, 5500, and 8080 come from. Those four are conventions people copied from each other, and nothing about them is special.

One program per port

Two servers cannot listen on the same port on the same machine. The second one fails to start rather than sharing.

The error has a name

EADDRINUSE means the port is already taken, almost always by a server left running in another terminal tab from an earlier attempt.

The port is part of the origin

Port 3000 and port 5500 are different origins to the browser. A project configured to expect one will not be satisfied by the other.

Changing it is fine

Most servers take a flag for the port. A flag on the server command lets you select a free port when another program has yours.

The error worth recognizing:
Error: listen EADDRINUSE: address already in use 0.0.0.0:3000

There are two fixes: find the old terminal tab and stop that server with Ctrl and C, or start this one on a different port.

Try Breaking It On Purpose

The fastest way to make this stick is to watch the same files behave three different ways.

  1. You can serve a project and open it at its localhost address to verify everything works.
  2. If you stop the server and open index.html by double-clicking it, any fetch calls will fail in the console.
  3. If you open it with a different server on a different port, the page is served and fetches work again, but anything that hard-coded the first port will fail.

Nothing in the files changed across those three attempts. Only the delivery changed, and the delivery is part of what decides what the page is allowed to do.

Watching the console, if you have not before

Step two says to watch the console, which assumes you already know where it is. Here is HAP opening the console and reading the first red line. This video runs for about two minutes.