In a document's `<head>`, what can `<meta http-equiv="…">` actually deliver, and how does it differ from sending the same directive as an HTTP response header?
answer
- not a general header channel
- a short closed list of pragmas
- X-Frame-Options in markup does nothing
- the parser reaches it late
- meta CSP drops frame-ancestors and reporting
basics
~20 sOnly a short list of pragma directives works in a meta tag — content-type, refresh, default-style, content-security-policy and the legacy x-ua-compatible. Others such as X-Frame-Options and Set-Cookie are ignored in markup, and a meta directive only applies once the parser reaches it.
solid answer
~50 s`http-equiv` was meant to let a document carry directives a server would otherwise send as headers, but browsers honour only a fixed set of pragmas: `content-type` (whose modern shorthand is `<meta charset="utf-8">`), `refresh`, `default-style`, `content-security-policy`, and the legacy `x-ua-compatible`. Anything else is markup with no effect — `X-Frame-Options` and `Set-Cookie` in a meta tag are simply ignored, which is a classic false sense of security. Even the pragmas that work are weaker than the header form for two reasons. First, timing: a header is known before the body is parsed, whereas a meta directive only takes effect when the parser reaches it, so anything already fetched or parsed escapes it. Second, scope: a meta CSP cannot use `frame-ancestors`, `report-uri` or `sandbox`, so the very directives you would want for framing control are header-only. Treat meta as a fallback, not as the delivery mechanism.
code
html · 7 lines<head>
<meta charset="utf-8">
<!-- Enforced, but frame-ancestors, report-uri and sandbox are ignored here -->
<meta http-equiv="content-security-policy" content="default-src 'self'">
<!-- Honoured: this one is name=, not http-equiv= -->
<meta name="referrer" content="strict-origin-when-cross-origin">
</head>go deeper
Know that <meta charset="utf-8"> is the encoding declaration and that it is the shorthand for the content-type pragma; do not expect other headers to work from markup.
Be able to list the honoured pragmas — content-type, refresh, default-style, content-security-policy, the legacy x-ua-compatible — and explain that a meta directive only takes effect when the parser reaches it.
Demonstrate the security judgment: recognise meta X-Frame-Options as inert, know that meta CSP drops frame-ancestors, sandbox and reporting, and place header configuration at the server or edge as the source of truth.
Own where policy lives. Decide that framing, transport and reporting directives are configured once at the edge for every response, and treat a policy expressed in page markup as a documented exception with a known weaker guarantee.
## What `http-equiv` was for A `<meta>` element carries document metadata. With `name`/`content` it declares information about the page; with `http-equiv`/`content` it declares a *pragma directive* — historically "an HTTP header equivalent", from an era when authors publishing static files could not configure their server. Browsers never implemented that generally. Today only a closed set of pragmas has any effect, and everything else is inert markup. ## The pragmas that actually work - **`content-type`** — declares the character encoding. In modern HTML the shorthand `<meta charset="utf-8">` is the form you write; the pragma form `<meta http-equiv="content-type" content="text/html; charset=utf-8">` means the same thing. A real `Content-Type` response header carrying a charset wins over the document's declaration. - **`refresh`** — reloads or redirects after a delay: `<meta http-equiv="refresh" content="5; url=/next">`. It works, but it is user-hostile — it moves the user without warning and cannot be paused, which is why timing-related accessibility criteria push back on it. A server redirect is better for the redirect case, and a user-triggered action is better for the rest. - **`default-style`** — selects which alternate stylesheet set is preferred. Rarely used. - **`content-security-policy`** — genuinely enforced by browsers. This is the one meaningful security pragma, and it has real limits (below). - **`x-ua-compatible`** — the legacy `IE=edge` incantation that told old Internet Explorer which rendering engine to use. Inert in every current browser; if you find it in a template, it is history, not policy. ```html <meta charset="utf-8"> <meta http-equiv="content-security-policy" content="default-src 'self'"> ``` ## The ones people believe in that do nothing - **`X-Frame-Options`** — ignored in a meta tag. Browsers honour it only as a response header. A page "protected" by `<meta http-equiv="X-Frame-Options" content="DENY">` is fully framable. This is the single most common mistake in this area. - **`Set-Cookie`** — no longer honoured; setting cookies from markup was removed. - **`Strict-Transport-Security`**, **`Access-Control-Allow-Origin`**, cache-control directives, and the rest of the header vocabulary — all inert as meta tags. A `<meta http-equiv="Cache-Control" content="no-store">` does not control any cache that matters. The rule of thumb worth carrying: if you cannot name the pragma on the honoured list, assume the meta form does nothing and check. ## Two structural reasons headers are stronger **Timing.** A response header is available before the parser starts on the body. A meta pragma exists inside the byte stream, so it only takes effect once the parser reaches it — after anything above it in the document has already been seen, and after the browser's preload scanner may have started fetching subresources. This is the same reason encoding declarations must appear early: content parsed under the wrong assumption has to be re-decoded, and content already requested under no policy was requested without one. **Scope.** The meta form of CSP deliberately drops directives that must be known before the document exists or that govern the document's relationship to its embedder: `frame-ancestors`, `report-uri`/`report-to`, and `sandbox` are ignored when the policy arrives via meta. That is not an oversight — a policy that decides whether the page may be framed at all cannot sensibly be delivered *inside* the page that has already been framed and parsed. So framing control, violation reporting and sandboxing are header-only, by design. ## A related tag that is not `http-equiv` Referrer policy is often lumped in here, but it is a `name`, not a pragma: `<meta name="referrer" content="strict-origin-when-cross-origin">`. This one *is* honoured, and it is the markup counterpart of the `Referrer-Policy` header. It shares the timing caveat — requests issued before the parser reaches it are unaffected — so put it high in the head or send the header instead. ## How to answer this in an interview Say what the mechanism is (a fixed list of pragmas, not a general header channel), name what is honoured and what is not, and give the two structural reasons — timing and scope — that make headers authoritative. Then give the practical posture: configure the header at the server or edge as the source of truth, and use a meta pragma only where you genuinely cannot, accepting that it arrives late and cannot express the framing and reporting directives.
- Why can a Content Security Policy delivered by meta tag not use `frame-ancestors`?`frame-ancestors` decides whether the document may be embedded at all. By the time a meta tag is parsed, the document has already been loaded inside whatever frame it is in, so the decision has been made. The same reasoning excludes `sandbox`, which must apply before the document runs, and the reporting directives. Those are header-only by design, not by omission.
- A page uses `<meta http-equiv="refresh" content="0; url=/new">` for a permanent move. What would you change?Send a real 3xx redirect from the server. The meta form still downloads and renders the old page first, gives search engines a weaker signal than a permanent redirect, and cannot be intercepted or cached like a response. Meta refresh also raises accessibility concerns because it moves users without warning and cannot be paused.
- If both a `Content-Type` header charset and `<meta charset>` are present and disagree, which wins?The header. The document declaration is a fallback for when the server does not specify one, and it is read only when the parser reaches it — which is why it must appear right at the top of the head. Making both say UTF-8 is the only sane configuration; a mismatch means part of the document may be decoded twice.
saying these in an interview costs you the question
- Thinks any HTTP header can be sent from a meta tag
- Blocks framing with meta X-Frame-Options
- Says a meta CSP is equivalent to the header form
- Believes meta cache-control controls CDN or proxy caching
- Uses meta refresh as a substitute for a server redirect