squirrelscan
GitHub

meta http-equiv=refresh: redirects and auto-refresh

A timed redirect moves the page out from under a screen reader user mid-sentence. squirrel reads the content value and grades it.

What meta refresh is

<meta http-equiv="refresh" content="5;url=/new"> asks the browser to load another page after a delay. With no url it reloads the current page on a timer instead.

Both take control away from the reader. Someone using a screen reader, or reading slowly, or filling in a form, gets moved without warning and with no way to stop it. WCAG success criterion 2.2.1 Timing Adjustable, Level A, covers time limits like this, and techniques F40 and F41 name meta refresh specifically as a way to fail it.

What squirrel checks

One check name per case, at rule severity error. The rule finds the first <meta> whose http-equiv is refresh, matched case-insensitively, and parses its content as a number of seconds optionally followed by ;url=.

  • meta-refresh-redirect warns with Immediate meta refresh redirect found when a url is present and the delay is zero, carrying the issue Use server-side 301/302 redirect instead.
  • meta-refresh-redirect fails with Meta refresh redirect after N seconds when a url is present and the delay is anything other than zero, which includes a negative or unparsable value, carrying the issue Timed redirects disorient users.
  • meta-refresh-auto fails with Page auto-refreshes every N seconds when there is no url and the delay parses to a number above zero and under 20, carrying the issue Frequent refresh disrupts screen reader users.
  • meta-refresh-auto warns with Page auto-refreshes every N seconds when there is no url and the delay is 20 seconds or more, suggesting Provide user control over refresh.
  • meta-refresh passes with No meta refresh found.

Every finding carries the raw content string as its value. The 20 second boundary is squirrel’s own, not a threshold from the standard.

Two gaps are worth knowing. Only the first matching <meta> is examined, and a content="0" with no url matches none of the branches, so it produces no finding at all.

Elements inside <noscript> are skipped: a browser with scripting enabled reads <noscript> content as plain text, so nothing in it loads or renders. See content inside noscript.

How to fix it

For a redirect, do it on the server:

HTTP/1.1 301 Moved Permanently
Location: https://example.com/new

A 301 or 302 never renders the old page, so nothing is read out and then replaced. It is also the version search engines follow cleanly.

For content that has to stay current, fetch and update the region in place with aria-live="polite" rather than reloading the document, and give the user a control to pause it.

Rule IDa11y/meta-refresh
CategoryAccessibility
ScopePer-page
Severityerror
Weight7/10

Enable / disable

Disable this rule

[rules]
disable = ["a11y/meta-refresh"]

Disable all Accessibility rules

[rules]
disable = ["a11y/*"]

Enable only this rule

[rules]
enable = ["a11y/meta-refresh"]

enable replaces the default ["*"], so this is the only rule selected. Leave disable out: a rule that matches any disable pattern is skipped even when enable names it. Only an enabled setting under a rule’s own [rule_options] table overrides both lists.

Accessibility findings ship in every audit next to the SEO, performance and agent experience rules. See Website security scan with AI for how an agent works through a report.

References

Check your site

Run squirrel audit https://example.com and open the Accessibility section of the report. The finding quotes the raw content value so you can see the delay and the target. Signed-out local audits need no account and cost nothing. Signed-in audits spend credits.

Type to search…

↑↓ navigate↵ openesc close