( Operable / WCAG 2.2.1 )
Meta refresh causes automatic page redirect
What is this issue?
A <meta http-equiv="refresh" content="N; url=..."> element (or a bare content="N" that just reloads the current page) is present with N set to a nonzero number of seconds. The browser waits N seconds after the page loads, then automatically reloads or navigates to the target URL, with no user action required and, critically, no control built into the page for stopping, delaying, or extending that countdown.
This is distinct from a server-side HTTP redirect, which happens instantly at the network layer before any content renders. A meta refresh renders the current page first, gives the user a window of time on it, and then unilaterally takes that page away.
Why does this matter?
An automatic page change interrupts whatever the user was doing the instant the countdown expires, with no regard for whether they’ve finished reading, filling out a field, or listening to content read aloud. Someone who reads more slowly than the countdown assumes (due to a vision condition, a cognitive disability, or simply because they got a phone call mid-page) loses their place entirely and has to start over on whatever page they land on next.
The disruption compounds because it’s usually silent and unannounced. A sighted user at least sees the page visually change; a screen reader user gets no warning before the entire page context resets mid-sentence, and has to reorient from scratch on unfamiliar content they didn’t choose to load.
Who is affected?
- Screen reader users: get no warning before the page changes out from under them mid-announcement, and have to reorient entirely on the new page’s content with no sense of what just happened.
- Low vision users: reading magnified content takes longer per screen, so a countdown timed for typical reading speed frequently expires before they’ve finished the page it’s about to replace.
- Cognitive disabilities: an unannounced, unexplained context change breaks task focus and requires re-establishing where they are and what they were doing, adding a recovery cost every automatic redirect skips.
- Motor impairments: users who need more time to click, tap, or complete a field due to limited fine motor control can lose in-progress input the instant the countdown fires and the page reloads out from under them.
What users experience
Tom uses JAWS on his Windows desktop to read a lengthy terms-of-service update his bank emailed him. The page has a <meta http-equiv="refresh" content="20; url=/account"> tag added so users aren’t left on an “outdated” page too long. JAWS is midway through reading the fourth paragraph when the countdown fires; the page reloads to the account dashboard, JAWS announces the new page from the top, and Tom has completely lost his place with no way to know how much of the terms he’d actually heard.
How do I fix it?
Remove the meta refresh tag and use a server-side redirect instead whenever the goal is simply to send users from one URL to another. This works because a server-side HTTP redirect (a 301 or 302 response) happens before the browser ever renders content at the old URL, so there’s no window of time for anyone to be reading or interacting with a page that then gets pulled out from under them.
If a timed transition is genuinely part of the product (a “your session is about to expire” notice, a checkout confirmation that auto-advances), replace the meta refresh with a script-driven countdown that gives the user real control: a visible button to go now, and a visible button or link to cancel or extend the timer. This satisfies WCAG’s actual requirement, which isn’t “no timing at all,” but that the user can turn it off, adjust it, or extend it before it acts.
Code Examples
<meta http-equiv="refresh" content="20; url=/account"><!-- Method 1: server-side redirect (preferred for a simple URL change) -->
<!-- Configure a 301/302 response at the server or edge layer instead -
no meta tag needed, and the browser never renders the old page. -->
<!-- Method 2: user-controlled countdown, when a timed transition is real -->
<p>
You'll be moved to your account in <span id="countdown">20</span> seconds.
<a href="/account">Go now</a> or
<button type="button" id="cancel-redirect">Stay on this page</button>
</p>Method 1 removes the timing problem entirely, since a server-side redirect leaves no page for the user to be interrupted on. Method 2 keeps a real countdown but adds the two controls WCAG 2.2.1 actually requires (a way to act immediately and a way to cancel), so the transition happens on the user’s terms instead of the clock’s.
Framework Examples
Single-page apps rarely use a literal meta refresh tag, but they commonly reproduce the identical accessibility problem with a JavaScript-driven timed redirect: a “your session has expired, redirecting to login in 5 seconds” pattern implemented with setTimeout and a router’s navigate() call. The fix is the same principle, expressed as a cancelable timer instead of a fire-and-forget one.
function SessionExpiredNotice({ onCancel }) {
const [seconds, setSeconds] = useState(5);
useEffect(() => {
if (seconds <= 0) {
navigate('/login');
return;
}
const timer = setTimeout(() => setSeconds((s) => s - 1), 1000);
return () => clearTimeout(timer);
}, [seconds]);
return (
<div role="alert">
<p>Your session expired. Redirecting to login in {seconds} seconds.</p>
<button type="button" onClick={onCancel}>Stay on this page</button>
</div>
);
}
The onCancel handler is the framework-specific piece worth building deliberately: it needs to clear the pending timeout and stop the countdown state entirely, not just hide the notice visually, or the redirect fires anyway a few renders later even though the button looked like it worked.
Common Mistakes
Mistake: “This rule only applies to the literal <meta refresh> tag, so our JavaScript redirect is fine.” WCAG 2.2.1 is about the underlying behavior (an automatic, uncontrollable timed change), not the specific mechanism producing it. A setTimeout-driven redirect creates the same failure for the same users; it simply isn’t caught by this particular automated check, which only parses markup.
Mistake: “Showing a ‘redirecting in 5 seconds’ message satisfies the requirement.” A visible countdown gives notice, but WCAG 2.2.1 requires the user be able to turn off, adjust, or extend the timing, not just be told about it in advance. A message with no working pause, cancel, or extend control still fails, even though it looks more considerate than a silent redirect.
Mistake: “A content="0" meta refresh is just as bad as a 10-second one.” A 0-second refresh fires before the page is meaningfully rendered to anyone, so there’s no window during which a user could be reading or interacting with content that then vanishes. It’s the nonzero delay (the gap between “content is visible” and “content is forcibly replaced”) that creates the timing problem this rule checks for.
Mistake: “Meta refresh is an obsolete technique nobody actually uses anymore.” It’s still common in legacy CMS templates, static “your session has ended” pages, some page-builder tools’ default output, and quick fixes bolted onto old sites during a URL migration, exactly the kind of low-visibility markup that survives redesigns because nobody re-audits it.
How RedFlag Detects This
Automated: axe-core rule, runs on every scan. RedFlag calls axe-core’s meta-refresh rule as part of every scan; it’s already included in the standard WCAG 2.0/2.1/2.2 A and AA tag set RedFlag scans, unlike its stricter AAA sibling meta-refresh-no-exceptions, which needed an explicit force-enable since AAA-level rules fall outside that default tag set. The rule inspects every <meta http-equiv="refresh"> element in the DOM and flags any whose content attribute specifies a delay other than zero seconds.
False negative: the check only parses the literal <meta http-equiv="refresh"> tag. A JavaScript-driven timed redirect using setTimeout and window.location, or a server sending an HTTP Refresh response header instead of a meta tag, produces the identical accessibility problem but is invisible to this check, since neither leaves a matching element in the DOM. False positive: none typical, since detecting the tag and reading its numeric delay is a straightforward, binary parse of markup already present on the page. Manual step: search the codebase and test the live site for JavaScript-based timed redirects that reproduce this same problem outside a meta tag, since the automated check can’t see those at all.
Manual Testing
- View the page source (or DevTools Elements panel) and search for
<meta http-equiv="refresh". Confirm none exist with a nonzero delay, or that any present have an accessible pause/extend/cancel control nearby. - Load the live page in a browser and wait without interacting. Note whether it automatically reloads or navigates away on its own within the first 30–60 seconds.
- If a countdown or timed transition exists, confirm there’s a visible, keyboard-operable control to stop, extend, or skip it before it fires.
- With NVDA or JAWS running, load the page and let any timer run out. Confirm the screen reader isn’t cut off mid-announcement and that the user gets some warning before the context changes.
Related WCAG Success Criteria
2.2.1 Timing Adjustable: For any time limit set by content, the user must be able to turn it off, adjust it, or extend it, with limited exceptions for real-time events. A meta refresh with a fixed, uncontrollable delay is a direct failure of this requirement, since it offers none of those three controls.
2.2.4 Interruptions: A stricter, AAA-level criterion requiring that interruptions (including automatic updates) can be postponed or suppressed by the user except in an emergency. This rule’s AAA sibling, Page uses meta refresh with a time limit, maps to this criterion directly and flags any meta refresh at all, not only ones with an adjustable delay.
Related Issues
Page uses meta refresh with a time limit is this rule’s AAA-level sibling: the same detection mechanism, applied to a stricter standard that flags any meta refresh, not only one with an uncontrollable delay.
Session times out without warning shares the same “enough time” principle from a different angle: a session ending without warning is the same loss-of-control problem this rule addresses, triggered by inactivity instead of a fixed page timer.
Page contains a blinking or scrolling element is a related “moving or changing content the user can’t control” failure under the same Operable principle, though it’s about continuous motion rather than a one-time forced navigation.
Context change happens when element receives focus and Context change happens on input without warning cover the same underlying harm (an unexpected, user-uninitiated context change) triggered by interaction instead of a timer.
References
- W3C Understanding 2.2.1: Timing Adjustable
- W3C Technique H76: Using meta refresh to create an instant client-side redirect
- MDN: HTTP redirections
Frequently asked questions
Is a meta refresh with a 0-second delay also a problem?
No, an immediate 0-second refresh is treated differently, since there is no countdown window during which a user could be reading or interacting before it fires. axe-core's check specifically flags a nonzero delay value; content="0; url=/new-page" does not trigger this rule.
Does a JavaScript-based redirect have the same problem as a meta refresh tag?
Yes, functionally. A setTimeout() call that navigates the user away after a delay creates the identical timing problem for the same reasons, but it is not a meta tag, so RedFlag's automated meta-refresh check (which only parses the DOM for the literal <meta http-equiv="refresh"> element) does not catch it.
Is a server-side redirect better for SEO than a meta refresh?
Yes. A server-side HTTP redirect (a 301 for permanent moves, a 302 for temporary ones) tells search engines exactly how to update their index, while a meta refresh is a weaker, client-side signal search engines treat with more caution. Fixing this accessibility issue also improves how reliably the destination page gets indexed.
What's the difference between meta-refresh and meta-refresh-no-exceptions?
This rule (meta-refresh, WCAG 2.2.1, Level A) flags a meta refresh with a nonzero delay and no way to control it. Its stricter AAA sibling, meta-refresh-no-exceptions (WCAG 2.2.4 Interruptions), flags any meta refresh at all, on the stricter principle that content should never move or change without the user asking it to.
Does showing a "redirecting in 5 seconds" message make a meta refresh acceptable?
No, not on its own. WCAG 2.2.1 requires the user be able to turn off, adjust, or extend the timing: a visible countdown message gives notice but no actual control. A message paired with a working pause or cancel control satisfies the requirement; a message alone does not.