<meta http-equiv="refresh"> is an HTML directive that automatically reloads the current page or redirects the browser to a new URL after a specified number of seconds. It has been discouraged for accessibility reasons since WCAG 1.0 (1999) because it disorients users, breaks browser history, and is unreliable for screen readers and assistive technology.
<meta http-equiv="refresh" content="N"> where N is less than 72,000 seconds is flagged.Think of meta-refresh as a moving walkway someone installed across your living room without asking. You sit down to read; the walkway starts; the page is gone before you finish the first sentence. The fix isn't to make the walkway slower — it's to take it out.
Meta-refresh fails on every dimension that matters: accessibility, browser behavior, data integrity, and search ranking. There is essentially no modern use case where a server-side HTTP redirect or a real-time data API isn't a better fit.
301 Moved Permanently is faster, accessible, and properly transfers link equity.The audit looks at the rendered HTML head and reports any timed redirect or reload that fires inside the WCAG threshold of 20 hours.
<meta http-equiv="refresh" content="0; url=/new-page"> (and any non-zero delay under 72,000 s).<meta http-equiv="refresh" content="30"> — reloads the same URL every 30 seconds.<meta http-equiv="refresh" content="5; url=/home"> — "Welcome, you'll be redirected in 5 seconds."<!-- BAD: instant redirect, breaks Back button -->
<meta http-equiv="refresh" content="0; url=/new-page">
<!-- BAD: timed reload on a dashboard, wipes form input -->
<meta http-equiv="refresh" content="30">
<!-- BAD: splash-page delay -->
<meta http-equiv="refresh" content="5; url=/home">
<!-- GOOD: server-side 301 redirect (Express / Node) -->
res.redirect(301, '/new-page');
<!-- GOOD: server-side 301 redirect (Next.js redirects in next.config) -->
async redirects() {
return [{ source: '/old-page', destination: '/new-page', permanent: true }];
}
<!-- GOOD: real-time dashboard via Server-Sent Events -->
const events = new EventSource('/api/dashboard/stream');
events.onmessage = (e) => updateDashboard(JSON.parse(e.data));Meta-refresh tags hide in old content templates, marketing landing pages, and legacy dashboards. They're easy to miss because they don't produce a visible warning until a screen-reader user lands on the page.
parser and the document includes a refresh meta tag, you've confirmed it.grep -ri 'http-equiv="refresh"' across your templates is still the fastest first sweep on a small codebase.When a URL has moved, return 301 Moved Permanentlyfrom the server. It's instant, accessible, preserves browser history correctly, and transfers full link equity for SEO. Every modern framework (Next.js, Express, Rails, Django) supports this in one line.
For seasonal or short-lived redirects (a campaign URL pointing at this quarter's landing page), return 302 Found. Browsers and search engines treat this as temporary, so the original URL retains its ranking once the campaign ends.
For pages that need to display live data (server status, support queues, analytics), open a Server-Sent Events stream from /api/stream and update the DOM in place. No reload, no input loss, sub-second latency, and accessible to screen readers.
When the page needs to send updates back (chat, collaborative editing, multiplayer dashboards), WebSocket is the right transport. Like SSE, it updates the DOM without disrupting the user.
fetch() Polling for Simple UpdatesIf SSE/WebSocket is overkill, a setInterval + fetch() loop that updates a specific section of the DOM is far better than reloading the whole page. The user keeps their scroll position and form input.
For splash pages, age gates, or interstitials, never auto-redirect. Show a clear button — "Continue to the homepage" — and let the user decide when to move on. This satisfies WCAG SC 3.2.5 (Change on Request).
The classic excuse for a 30-minute meta-refresh is "to keep the session alive." The right fix is a silent fetch() to a token-renewal endpoint on a timer — the page never reloads and form input is preserved.
What's happening: The team rebranded the site and added <meta http-equiv="refresh" content="3; url=/new-home"> with a "You'll be redirected" banner on every old URL.
Fix: Replace with server-side 301 redirects in next.config.js, the CDN edge config, or the framework router. Link equity transfers and the Back button works again.
What's happening: An ops dashboard uses <meta http-equiv="refresh" content="30">. Operators lose their filter selection on every reload and can't use a screen reader to triage incidents.
Fix: Replace with Server-Sent Events that push only the changed metric to the existing DOM. The page never reloads.
What's happening: A multi-page application form refreshes every 5 minutes "to keep the session alive," silently destroying whatever the user is typing.
Fix: Use a silent fetch() to /api/session/renew on an interval. The session stays alive; the form stays intact.
What's happening: The marketing splash page uses <meta http-equiv="refresh" content="5; url=/home">. Screen-reader users hear half the welcome message before the page disappears.
Fix: Show a prominent "Continue" button and let the user choose when to navigate. Or skip the splash entirely — most splash pages are pure friction.
Every legitimate use of meta-refresh has a better, accessible replacement. The choice depends on whether you're redirecting, updating data, or keeping a session alive.
| Approach | Use Case | Accessibility | SEO |
|---|---|---|---|
| Meta refresh (under 20 h) | Legacy page redirects, dashboard reloads | Fails WCAG SC 2.2.1 | Weak signal, breaks Back button |
| HTTP 301 (server) | Permanent URL change | Fully accessible | Full link-equity transfer |
| HTTP 302 (server) | Temporary redirect | Fully accessible | Original URL retains rank |
| Server-Sent Events | One-way real-time dashboard updates | Accessible — DOM updates in place | No SEO impact (page URL stable) |
| WebSocket | Two-way real-time (chat, editors) | Accessible with proper ARIA live regions | No SEO impact |
| fetch() polling | Periodic data refresh, session keepalive | Accessible — preserves form input | No SEO impact |
| User-controlled button | Splash, interstitial, age gate | Meets WCAG SC 3.2.5 | Neutral |
It's discouraged but not formally deprecated in HTML. It still parses in every browser. WCAG has flagged it as inaccessible since WCAG 1.0 (1999), and Google's SEO documentation has recommended server-side HTTP redirects over meta-refresh for over a decade.
Primarily WCAG 2.2 SC 2.2.1 Timing Adjustable (Level A), which requires that any time limit be turn-off-able, adjustable, or extendable. A meta-refresh under 20 hours (72,000 seconds) imposes a non-adjustable time limit. It also implicates SC 2.2.4 (Interruptions) and SC 3.2.5 (Change on Request) at AAA.
Google treats meta-refresh under 1 second as a soft 301 — but soft signals are noisier than HTTP redirects. Longer delays are an ambiguous temporary signal at best. Both Google and AI crawlers must render the page to discover the redirect, multiplying the crawl cost compared to an HTTP redirect that resolves in headers.
Server-side HTTP redirects: 301 Moved Permanently for permanent moves and 302 Found for temporary ones. Every framework supports this in one line — Next.js redirects(), Express res.redirect(301, ...), Apache RewriteRule, Nginx return 301, or edge rules on your CDN.
Server-Sent Events (SSE) for one-way updates, WebSocket for two-way. Both update the DOM in place without reloading the page, so screen-reader users keep their context and form input is preserved. For simpler cases, a setInterval + fetch() loop that updates a single section is sufficient.
Yes. AI crawlers must render the page to follow a meta-refresh, which doubles the cost of crawling that URL. Many AI bots stop short of full rendering, meaning the destination page is never indexed at all. Server-side HTTP redirects resolve in the headers and are followed reliably by every crawler.
Technically yes — values of 72,000 seconds (20 hours) or higher are WCAG-conformant because no real user session lasts that long. But practically, if you can wait 20 hours, you don't need a refresh at all. Replace it with the right pattern: HTTP redirect, SSE, WebSocket, polling, or a user-controlled button.
Meta-refresh is a 1990s-era workaround for problems that every modern framework solves correctly. Server-side 301/302 redirects handle URL moves; Server-Sent Events handle live dashboards; fetch() handles session keepalive. Every replacement is faster, accessible, and friendlier to both Google and AI crawlers.
Run a Greadme deep scan to find every meta-refresh on your site, see exactly which template inserted it, and ship the right replacement — usually a one-line config change for redirects, or a small SSE endpoint for dashboards.