Skip to main content

Timezones.in - 10,723 pages, rebuilt in a week without losing a URL

NextjsNodeJavascriptTailwind CSSMongoDB

Need the same stack on your project? Hire Next.js developers in India or Node.js developers.

Stay In Sync Timezones web application UI

Why we own it

An agency that says it can build and rank a site should be able to point at one. Client work cannot do that job: the engagements with the best numbers are the ones we are least able to describe. So the honest answer is a site we own, where we can show the architecture, the trade-offs and the failures without asking anyone's permission.

Timezones.in is a utility. Someone types a question about time in two places and wants the answer on the page, not a tool to operate. That shapes everything below: pages are server-rendered so the answer is in the HTML, and there is no interface to learn before you get it.

What it is made of

There are 10,723 pages, generated from six templates rather than written one at a time:

  • 9,045 conversion pages — every pair of timezone abbreviations, /convert/ACST_to_ALMT and so on.
  • 1,000 city pages — the local time somewhere, with the context around it.
  • 248 daylight-saving pages — one per country, covering when the clocks move and by how much, such as the United States.
  • 247 country pages, 136 abbreviation pages and 38 UTC offset pages.
  • Plus the parts a person uses directly: a meeting planner, a free API, an embeddable clock, and a day/night map with live city pins.

The whole set builds as static pages in about eighty-five seconds, which is what makes a site this size maintainable by a small team.

The decision most converter sites get wrong

A conversion page exists for every pair of 135 timezone abbreviations. The obvious way to generate that is every permutation: 135 × 134 = 18,090 pages. We generate 9,045 — exactly half, 135 × 134 ÷ 2 — because ACST to ALMT and ALMT to ACST are the same question asked twice.

Ask for the reverse of any pair on the site and you get a 404, not a near-identical page competing with the first one. Nine thousand pages of duplicate content is the standard failure of this kind of site, and avoiding it was a decision made once, at generation time.

The honest counterweight: 121 of those 135 abbreviations are obscure. Nobody searches for ACST to ALMT. The generation is correct and the demand for most of it is not there — a distinction worth making, because correctness and traffic are different things, and only one of them is under our control.

Rebuilding without losing the URLs

The rebuild moved the site to Next.js 16 and replaced a client-rendered front end with server-rendered pages, so the answer arrives in the HTML rather than after a round trip to an API. Underneath, the API moved to Fastify and MongoDB on the same droplet.

A framework migration is where sites lose their search history. Slugs get tidied, headings get improved, and six months of accumulated authority goes with them. So the URLs, the page titles and the H1s were frozen before any of the new code was written, and a test in the build fails if any of them move.

It held. Comparing the sitemap before the rebuild against the one after: 10,722 URLs before, 10,723 after, and not one of the 9,045 conversion URLs changed.

What it runs on

One DigitalOcean droplet. Nginx on Ubuntu in front, Node behind it, MongoDB local to the box, nightly database dumps to object storage. There is no cluster and no managed database, because the traffic does not need one and the cost of pretending otherwise is paid every month.

Two things are deliberate rather than incidental. Analytics do not load until a visitor consents — the tag is in the bundle and fires afterwards, not before. And there are no passwords: sign-in is a magic link, so there is no password hash on the server to leak.

What went wrong

The site was down for about a week at the end of August 2026. Not a maintenance page — the server itself was gone, so anything asking for a page got no answer at all, including Googlebot.

That is the worst way to fail. A 503 response tells a crawler the site is temporarily unavailable and to come back; a dead socket tells it nothing, and the crawler draws its own conclusions about a site with ten thousand pages. Nginx now serves a maintenance page from disk with a 503 and a Retry-After header, so the failure mode is a message rather than a silence. It was tested by killing the application in a container and watching what the server returned.

The second failure is smaller and more embarrassing. We had never verified the site in Google Search Console. Ten thousand pages, live since 2023, and no one had connected the tool that reports what Google actually does with them. It was verified in September 2026, which means the search history before that date does not exist and cannot be recovered.

We are not going to publish an indexation figure until that data has had time to accumulate. There is a number circulating internally that is really a count of pages we published, not pages Google kept, and the difference between those two things is the entire subject of this kind of work.

What this demonstrates

Not that we can build a timezone converter. That the architecture decisions were made deliberately and can be inspected: a deduplicated URL space, static generation at a size that stays maintainable, consent-gated measurement, credentials that cannot leak because they are not stored, and a migration that moved a framework without moving a single URL.

Every claim above can be checked. The site is at timezones.in, the sitemaps are public, and the pair rule takes one 404 to verify.

If you want this done to your site

We design, build and rank websites and applications for small businesses, and we build white-label for agencies who would rather not. The work on this page — the architecture, the migration that kept its URLs, the failure modes — is the same work we do on client sites.

Tell us what is not working and we will tell you what it would take to fix — twenty minutes, no deck. Book a call.