Skip to content
Control Plane Labs

451 Unavailable For Legal Reasons

Client error response defined by RFC 7725 §3.

Last updated July 27, 2026

Common causes at a glance

  • A court-ordered takedown of specific content
  • Geo-blocking to comply with sanctions or a national regulation
  • A copyright or right-to-be-forgotten demand acted on by the host
  • An upstream ISP or national filter inserting the response in transit

Reproduce it

curl -sSi https://www.example.com/blocked-article | grep -i -E 'HTTP/|^link:'

451 exists to make censorship visible. Before it, a blocked resource was indistinguishable from a deleted one — operators returned 404 or 403 and the reader could not tell whether the page had been removed by its author or suppressed by a third party. 451 says the content exists and someone with legal authority is preventing you from reading it.

The code implies nothing about who imposed the demand. It might be the origin complying with a court order, or an intermediary — an ISP, a national filter, a CDN applying a regional restriction — inserting the response on the operator’s behalf. Distinguishing the two is the point of the accompanying header.

What the spec says

RFC 7725 §3 defines 451 and requires the response body to explain the legal demand and identify who is behind it. §4 defines the machine-readable half: a Link header with rel="blocked-by", built on RFC 8288, naming the entity that imposed the block — not necessarily the operator returning the response. The spec is candid about its limits: §5 notes that an operator under a gag order may be forbidden from sending 451 at all. It is a code for operators permitted to be honest.

What actually causes it

Region-based compliance blocking is the most common source. Sanctions regimes push operators to refuse whole countries, and rather than serve a bare 403 some deploy 451 with an explanation. Right-to-be-forgotten requests under GDPR produce per-URL blocks visible only to European clients. Copyright takedowns work the same way, sometimes applied by the CDN rather than the origin. The confounding case is a network-level block: if the response is inserted by an ISP or a national filter, the origin operator may have no idea it is happening. That is what the blocked-by link is for, and its absence means the operator either did not implement the header or is not free to name the demanding party.

How to debug it

Read the headers and the body, then test from somewhere else:

curl -sSi https://www.example.com/blocked-article | grep -i -E '^(HTTP/|link:|server:)'

A Link: <https://legal.example.org/order/9>; rel="blocked-by" names the responsible entity, and the body is supposed to say more. Then establish whether the block is geographic by repeating the request from another region: a 200 from one location with a 451 from another confirms geo-blocking. Compare the TLS certificate and Server header between the two, since a mismatch means something in the path is intercepting rather than the origin responding. If your own service emits 451, include the body and the link — a 451 with no explanation is just a 403 with extra mystique.

Working examples

curl

# The Link header and the body carry everything useful.
curl -sSi https://www.example.com/blocked-article \
  | grep -i -E '^(HTTP/|link:|content-type:|server:)'

# Is it geographic? Compare from another exit point.
curl -sS -o /dev/null -w '%{http_code}\n' --interface eth1 \
  https://www.example.com/blocked-article

Python (requests)

import requests

resp = requests.get("https://www.example.com/blocked-article", timeout=10)
if resp.status_code == 451:
    # RFC 7725 section 4: rel="blocked-by" names who imposed the demand.
    link = resp.headers.get("Link", "")
    print("blocked by:", link if "blocked-by" in link else "not disclosed")
    print(resp.text[:500])  # the body must explain the legal demand

Node (fetch)

const res = await fetch("https://www.example.com/blocked-article");

if (res.status === 451) {
  const link = res.headers.get("link") ?? "";
  const blockedBy = link.includes('rel="blocked-by"') ? link : null;
  console.log("blocked by:", blockedBy ?? "undisclosed");
  console.log(await res.text());
}

// Retrying will not help; 451 is a policy decision, not a transient failure.

Reference and tooling

The authoritative list of assigned status codes is theIANA HTTP Status Code Registry. For a searchable copy with the semantics summarised, use theHTTP status code reference on this site. To see exactly what a server is sending, theheader inspector shows the raw status line and response headers, and thecurl builder assembles the request flags for you.

Other 4xx codes