All articles

Engineering

How to auto refresh a browser tab (and when not to)

Chrome has no timer for a tab, and a console snippet dies at the first reload. What our Tab Auto Refresher does, and where it helps a developer.

By Gogol S9 min read

Chrome has no auto-refresh for a tab. No setting, no flag, and the reload button reloads once per click. To make a page reload itself while you watch it, something has to outlive the page. A meta refresh tag works only in HTML you control. A DevTools snippet does not work at all: setInterval(() => location.reload(), 5000) reloads once and stops, because the interval lives in the page's JavaScript context and the reload destroys it.

Tab Auto Refresher is our extension for the case in between: a page you don't control, in a browser you do. It is a free, 23 KiB Manifest V3 extension that gives each tab its own timer.

What it does

Set an interval in seconds, from 1 upward, or click 5s, 10s, 30s, 1m or 5m. Every tab keeps its own timer, so the staging tab can run at 30 seconds while a queue dashboard runs at 5, and neither disturbs the other. The popup shows a countdown ring and the seconds remaining, and the toolbar badge for that tab turns ON, so a tab left refreshing is visible from across the tab strip. There is also a plain reload button, for the times you don't want to wait for the next tick.

Hard refresh is a toggle rather than a second button. With it on, the reload calls chrome.tabs.reload(tabId, { bypassCache: true }), which skips the HTTP cache. That matters when an asset is cached twice, in the browser and at the CDN edge, and a normal reload keeps handing you the previous build's JavaScript.

The counter tallies automatic reloads since you pressed start. It survives the reloads themselves, because it is stored in the extension rather than the page, and it resets when you restart the timer.

Chrome will warn you at install that the extension can read and change data on all sites. That warning is not optional for this kind of tool: it cannot know in advance which page you will want to reload, so it declares every URL and injects its content script there. Whether a reload extension deserves that is a fair question, so know what it does with the access. This one makes no network requests, keeps state in chrome.storage.local on your machine, and its store listing declares no data collection.

Where it earns its place

Waiting for a deploy

The pipeline takes ten minutes and the staging URL is already open. At a 30-second interval that is twenty looks without twenty clicks, and the moment the new build is live you see it. That is how our staging deploys work: the deploy is only done when the page it serves has changed, so whoever triggered it ends up watching a URL either way. Refreshing a deploy log tab at the same time catches the case where the pipeline is green and the app is not.

Watching a queue, a worker or a health check

Backlog counts, failed job counts, dead-letter queues, a dashboard that still needs a page reload rather than a socket, an endpoint that returns JSON. A page that is only interesting when it changes is the case for a timer. Two cautions: choose the slowest interval that still tells you something, and keep it off production endpoints that cost money per request.

A local server you keep restarting

PHP behind Apache, a Node process without a watcher, a Python server you restart by hand. Every edit means a manual reload, and a 5-second timer on the local tab replaces that click. If you already have hot module replacement, or a dev server that reloads on save, this is redundant: the page updates itself and a timer on top of it only fights the dev server.

Reproducing a bug that shows up once in twenty loads

Set 1 or 2 seconds and watch the counter. When it finally breaks you have "it failed on load 34" instead of "it fails sometimes", and you can stop the timer at once and start reading the console while the broken page is still up. That is a real difference when the bug depends on a cache, a race or a third-party response.

Some intervals that stay useful:

What you're waiting forInterval
A build or deploy to finish30-60s
A queue or job to drain10-30s
A local server you're restarting5-10s
An intermittent bug, reload and watch1-5s

The last two are for pages you own or your own machine. On a shared environment, a 1-second timer on several tabs is a load test nobody agreed to.

Where it gets in the way

The timer is the enemy of the debugger. A paused breakpoint, a console.log you are about to read, a request you are inspecting in the Network panel: all of it disappears at the next tick. Stop the timer when the bug gets interesting, and start it again when you are waiting for something outside your control.

A reload also throws away everything the page held in memory. Unsaved input in a CMS editor or an admin form, an open modal, scroll position, a canvas you were drawing on. Pages reached by a POST are worse: refreshing one makes Chrome ask you to confirm the resubmission, and a timer turns that prompt into a regular visitor. Auto-refresh belongs on GET pages.

There is a cost on the other side of the connection, too. Every tick is a request, and on a site measured by Google Analytics every tick is a page view. Leave a tab refreshing at 5 seconds overnight and you will have added roughly 5,000 page views and as many lines to an access log that isn't yours. If that staging environment sits behind a rate limit, you will also have found the rate limit.

And if the page already streams its updates, over a websocket or server-sent events, reloading is worse than leaving it alone. Each reload drops the connection and forces a reconnect; a 1-second timer can turn a healthy backend into a reconnect storm.

If your page refreshes itself, give the reader a way out

For pages we build, self-refreshing content is an accessibility question, not only a convenience one. WCAG 2.2.2, Pause, Stop, Hide applies to auto-updating information that starts on its own and is presented alongside other content: the user needs a way to pause, stop or hide it, or a way to control how often it updates. WCAG 2.2.1, Timing Adjustable is the neighbouring rule for time limits a page sets, with 20 hours as the point past which a limit stops being one.

A dashboard that quietly reloads every 30 seconds fails 2.2.2 unless the visitor can pause it or set the interval. Put that control in the interface, not three screens into a settings page, and remember what the visitor chose.

Why the timer lives in the page, not in the extension

The obvious design is a timer in the background service worker that calls chrome.tabs.reload every N seconds. That does not work in Manifest V3, for two reasons.

Chrome shuts down an idle extension service worker after 30 seconds, and only an event or an extension API call resets the clock (service worker lifecycle). A setTimeout scheduled in the worker dies with it. The API built for repeating work, chrome.alarms, has a minimum period of 30 seconds, so an alarm-based design cannot express "every 5 seconds" at all.

So the timer lives in the content script, inside the page. The content script is injected into every http and https page at document_start, and a setTimeout there fires on time. In a hidden tab Chrome checks timers roughly once a second, so a background tab can reload slightly late; keep the tab in front of you when the timing matters. The service worker is only the bookkeeper: it stores the interval, the bypass-cache flag, the refresh count and the time of the next reload in chrome.storage.local under a key for that tab, updates the badge, and performs the reload.

The reload is the interesting part, because it wipes the timer along with everything else in the page. Two things put the timer back. When the content script loads, it asks the worker for its tab's state and starts the timer if it is enabled. And when the tab reports status: 'complete', the worker sends the state again in case the first message arrived too early. The interval is measured from the end of each load, so a slow page refreshes a little less often than the number in the popup suggests. The stored state is deleted when the tab closes, so closed tabs don't leave keys behind.

That design explains the limits you will run into. Chrome does not allow content scripts on chrome:// pages, other extensions' pages or the Web Store, so the tab you cannot refresh is the tab whose popup is disabled. Local file:// pages need "Allow access to file URLs" switched on for the extension. And a tab you never gave a timer stays untouched, because every piece of state is keyed by tab ID rather than held as one global setting.

Setting it up

  1. Install it and pin the icon to the toolbar (the puzzle piece menu, then the pin).
  2. Open the tab you want refreshed and click the icon.
  3. Type an interval or click a preset. Ten seconds is the default, and it is a reasonable place to start.
  4. Turn on Hard refresh if a normal reload keeps serving the old asset.
  5. Press start. The badge reads ON until you stop it.

It is not a test runner

If the reload is part of a check that has to pass or fail, an extension is the wrong tool. It reloads a page for a person to look at; it cannot assert, retry or report. That job belongs to Playwright or Puppeteer with page.reload() in a loop and a check after each one, run on a schedule by something that has no tabs at all. The same goes for monitoring: a tab you have to keep open is not monitoring, and the moment the queue needs to wake someone, it is a small service and a scheduler, which is the kind of thing we build rather than something to watch.

Install it, put 30 seconds on the tab you keep checking, and stop clicking reload.