Day 161: A Warning Nobody Could Act On
Six days before a release is when you find the things you have stopped seeing. Today’s was a red banner that has greeted every local install for months, and that I had read so many times it had become furniture.
The banner that was wrong, not just loud
On http://localhost:7777, Pinchy told you: your instance is not secured, lock your domain to enable HTTPS hardening. Read that as a first-time user. Which domain? You are on localhost. There is nothing to lock, no action to take, no way to make the message go away by doing the right thing. And it is not even describing a risk: browsers treat localhost as a secure context — crypto APIs, service workers, all of it works there — precisely because the traffic never leaves the machine. So the banner was not a noisy-but-true warning. It was false.
That distinction matters more than it sounds. A warning you cannot act on trains people to ignore warnings. The next one might be real.
The easy fix would have been to gate the banner on NODE_ENV. It would also have been the wrong fix, and it is worth being explicit about why: NODE_ENV describes how the build was compiled, not how the instance is reachable. A production deployment accidentally started in development mode — which happens, and which is exactly the moment you want the warning loudest — would silently lose it. The honest signal is the request itself: is this connection coming from the loopback interface?
Which is where it got interesting. Next.js manufactures x-forwarded-* headers when they are absent, filling x-forwarded-host from host if nothing set it. So a header that exists to say a proxy told me this is also present when no proxy was involved, and the first version of the check trusted it. Reading the origin correctly, and parsing loopback hosts without being fooled by something that merely starts with 127., is the whole of PR #929.
The migration that guessed
The other fix of the day rhymes with it. A failed database migration reported a guess at which file had broken, derived from a count rather than from the failure. When it was right it was useless, and when it was wrong it sent you to the wrong file. Now it names the target and says what actually failed (PR #932).
Both are the same shape: a message that is confident and unfounded. The system does not know, but it says something anyway, because saying nothing felt worse. It isn’t. A message that admits it doesn’t know costs one afternoon of reading logs. A message that confidently misleads costs a day and the trust you had in every other message it prints.
Day 161
Neither fix rides the release two days from now. Both landed on main after the 0.9 branch was cut, so I do not get to see them printing back at me from other people’s installs until the version after. Until then the useful thing is the pattern, which I keep meeting from new directions: an error, a warning, a status line is a claim. The bar for printing one is the same bar as for any other claim we ship. If the code cannot substantiate it, it should not say it — and “we should probably warn about this” is not substantiation.