http-equiv meta tags: which values still do something, and what to use instead
http-equiv is the attribute that lets a meta element stand in for an HTTP response header, and most values people paste into it do nothing. Of the seven the HTML standard defines, four still do something and two are the ones Google reads. This chapter is the routing table.

http-equiv is the attribute that lets a <meta> element stand in for an HTTP response header, and most of the values people still paste into it do nothing. The HTML standard defines seven; four still do something in a browser, two are the ones Google's documentation actually reads, and the rest are ignored by every current browser. This chapter is a routing table: which values are worth keeping, and what to write instead.
Before you start
You need the ability to edit your page's <head>, and you need to know whether your server can set response headers. If you are on a static host, some of these have no header equivalent you can reach, and the meta version is your only option. The rest of the chapter assumes you can do either, and tells you when the choice matters.
The one concept to hold onto is the difference between a pragma and metadata. A name meta tag describes the page; an http-equiv tag asks the browser to behave as if the server had sent a header. That difference is why the same-looking tags have such different fates.
What http-equiv actually does
The attribute's name is short for "HTTP equivalent". When a browser parses <meta http-equiv="refresh">, it processes it the way it would process a Refresh response header. The standard calls these pragma directives and enumerates exactly which ones exist, so a value that is not on that list is not a directive at all — it is an attribute the parser skips.
http-equiv is not a general way to set headers from HTML. It is a short, closed list of seven directives, and only the first two on it are read by anything that indexes your site.
That closed list is the whole chapter. The HTML standard says the attribute "is an enumerated attribute with the following keywords and states", and MDN puts the same fact in one line: "Only a subset of the HTTP headers are supported as http-equiv values."
Which http-equiv values still do something
Seven values exist. Four are acted on by current browsers, one exists only for a browser that was retired, and two are explicitly inert. The "conforming" column is the standard's own verdict on whether the value is allowed at all.
| Value | Conforming | What it does | Use instead |
|---|---|---|---|
| content-type | Yes | Declares the character encoding | <meta charset> |
| refresh | Yes | Reloads or redirects after N seconds | A server-side 301 |
| content-security-policy | Yes | Enforces a CSP on the document | The CSP response header |
| default-style | Yes | Names the default style sheet set | No replacement; rarely needed |
| x-ua-compatible | Yes | Legacy Internet Explorer rendering mode | Nothing; delete the line |
| content-language | No | Sets the default language | The lang attribute |
| set-cookie | No | Nothing; ignored by design | The Set-Cookie header |
Two rows in that table are the ones that cost people time. set-cookie is the clearest: the standard says the pragma "is non-conforming and has no effect. User agents are required to ignore this pragma." A cookie written that way has never been set, on any browser, for years. x-ua-compatible is the other: it existed to make old versions of Internet Explorer behave, MDN now says "User agents now ignore this pragma", and the browsers it targeted are out of support.
Which values Google's documentation reads
Google publishes a list of the meta tags it supports, and only two of the seven appear on it. Both are in the page it maintains for meta tags and attributes that Google supports, last updated 2025-12-10 UTC when we fetched it on 2026-09-14.
| Value | Google's wording | Practical effect |
|---|---|---|
| content-type | Defines "the page's content type and character set" | Lets Google read your encoding |
| refresh | "sometimes used as a simple form of redirection" | Followed, but discouraged |
The charset row comes with a formatting warning worth copying verbatim: quote the value. Google's page says to "surround the value of the content attribute in the http-equiv meta tag with quotes — otherwise the charset attribute may be interpreted incorrectly." The modern form avoids the whole problem, because <meta charset="utf-8"> has one value and nothing to mis-parse.
The refresh row is a redirect that Google follows and recommends against. Its page says meta-refresh "is not supported by all browsers and can be confusing to the user. We recommend using a server-side 301 redirect instead." A separate Google page on redirects does list meta refresh as one of the supported methods, in the same table as HTTP 301 and 308 — it just sits below them in the order of preference, with JavaScript below it.
Do it in this order
Four steps, each with a completion marker you can check before moving on. The whole pass takes a few minutes per template, and it is worth doing once across every page template you own rather than page by page.
- List every http-equiv tag your templates emit. Grep the source, not the rendered page, so you catch conditionals. Done when you have the full set of values, not a sample.
- Mark each one against the seven-value table. Anything not on the list is dead weight, and the two non-conforming values are the ones to remove first. Done when every tag has either a "keep" or a "replace" next to it.
- For each "replace", check whether you can set the real header. A static host may not let you; a server or a CDN usually can. Done when you know, per value, whether the header route is open.
- Change one template, then fetch the rendered page and confirm. Done when the old tag is gone from the HTML and, where you switched to a header, the response shows it.
# what your templates actually emit
grep -rn 'http-equiv' templates/ | sed 's/.*http-equiv="\([^"]*\)".*/\1/' | sort | uniq -c
# what the browser is served, headers included
curl -sI https://example.com/ | grep -iE 'content-type|content-security-policy|set-cookie'
# and what is in the HTML itself
curl -s https://example.com/ | grep -io 'http-equiv="[^"]*"'
The deliverable: a routing table
Keep this table next to the seven-value one. It answers the only question that matters after you have found the tags — where each intent should actually live. Every row moves the setting from HTML, which is late and optional, to the response, which arrives before the body and cannot be skipped by a parser.
| Intent | Meta tag | Preferred |
|---|---|---|
| Declare the encoding | content-type | <meta charset>, or the header |
| Redirect a page | refresh | HTTP 301 |
| Send a security policy | content-security-policy | CSP response header |
| Set a cookie | set-cookie | Set-Cookie response header |
| Declare the language | content-language | lang on <html> |
| Cache the response | cache-control | Cache-Control header |
The last row is not a value at all, and that is the point. http-equiv="cache-control" and http-equiv="pragma" are common in older templates, and neither appears in the standard's list of states. They are not directives; they are strings that look like directives. Caching is decided by the Cache-Control response header, and a meta tag cannot influence a response that was already sent.
What goes wrong, and how you would notice
Three failures cover most of what people hit, and none of them produces an error message. Each is visible only if you look at the right artifact.
- A redirect that never fires. A
refreshwith a non-zero delay is a timer, not a redirect, and a crawler that leaves before the timer expires sees the original page. Google follows the tag but tells you to use a 301, which is why the fix is to replace it rather than to lower the number. - A charset that is read from the wrong place. A
content-typetag with an unquotedcontentvalue can have its charset misread, and the browser then guesses. The symptom is mojibake in a page that looked fine in your editor. Use<meta charset>and the ambiguity disappears. - A tag that is present, valid-looking, and inert. This is the
set-cookieandx-ua-compatiblecase. Nothing fails, because nothing was ever supposed to work; the line simply does nothing, and it will keep doing nothing until someone removes it.
One boundary worth stating plainly: this chapter is about the values the HTML standard defines. A value that some browser vendor quietly honours but the standard does not list is outside it, and MDN's own note says as much — "some browsers process additional headers that are not listed above", and because unknown values are ignored, "this can lead to inconsistent behaviour". We did not test any of those vendor-specific values, so this chapter does not recommend one.
Common questions
Is http-equiv still supported?
Yes, as an attribute with seven defined values, and two of them are the ones Google reads. What is not supported is the general idea that you can set any response header from HTML. The list is closed and has been for years.
Should I use meta refresh or a 301 redirect?
A 301, every time, unless the host leaves you no way to send one. Google's redirects page lists meta refresh as a supported method but places it below the HTTP status codes, and its meta-tags page recommends the server-side 301 outright.
Why does my template still have x-ua-compatible?
Because it was added when Internet Explorer needed it and nobody removed it. Current browsers ignore the pragma, and the browser it was written for is retired, so the line is now inert. Deleting it changes nothing except the size of your head.
Can I set Content-Security-Policy with a meta tag?
You can, and the standard defines the state. The header is still the better place, because it arrives before the document body and covers responses a meta tag cannot reach. The X-UA-Compatible survey is a good reminder of how long an obsolete meta line can survive in a template; the charset survey covers the one value that is genuinely worth keeping.
Does removing these tags hurt anything?
Removing the inert ones, no. For the two that do something, the test is whether the equivalent header or attribute is in place first. If you drop content-type without a <meta charset> or a header, the browser has to guess your encoding, which is a real regression. A crawler accessibility check will show you what a non-browser client receives, which is a fast way to catch an encoding you can no longer read.
Part of the QueryWin handbook · Level 2

