Back to HTTP Methods

GET

Retrieves a representation of a resource without modifying server state.

Safe Idempotent Cacheable Has Request Body

What it does

GET asks a server for a representation of a resource. It's the workhorse of HTTP — every time you load a webpage, fetch an API record, or click a link, you're almost certainly issuing a GET. Nothing about a GET request is supposed to change server state; you're reading, not writing.

That "read-only" contract is what makes GET special. Browsers prefetch GET requests speculatively. Search engine crawlers hammer them without asking permission. CDNs cache them by default. None of that is safe to do with a method that might mutate data — which is exactly why GET's semantics are guarded so carefully by the spec.

Semantics

Property Value Why it matters
Safe Yes The client isn't requesting a state change — a crawler or prefetcher can issue GETs freely
Idempotent Yes Ten identical GETs have the same effect as one
Cacheable Yes Responses are cacheable by default unless headers say otherwise
Request body No (technically allowed, universally ignored) Query parameters go in the URL, not a body
Response body Yes The whole point of the request

Syntax & example request

GET /users/42 HTTP/1.1
Host: api.example.com
Accept: application/json
HTTP/1.1 200 OK
Content-Type: application/json
Cache-Control: max-age=60
ETag: "v3"

{"id": 42, "name": "Sainesh"}

curl example

curl https://api.example.com/users/42

Common status codes returned

  • 200 OK — resource found, here's the representation → see 200
  • 304 Not Modified — conditional GET, cached copy is still valid → see 304
  • 404 Not Found — no such resource → see 404

GET vs POST vs QUERY

GET POST QUERY
Safe Yes No Yes
Idempotent Yes No Yes
Request body No (ignored) Yes Yes
Params live in URL Body Body
Cacheable Yes No (by default) Yes

GET's one real weakness: everything has to fit in the URL. Once your filters get complex (nested objects, arrays, long lists), the URL either becomes unreadable or blows past a proxy's length limit — which is the exact gap the new QUERY method was designed to close.

Real-world usage

  • Fetching pages, images, static assets — the default browser navigation verb
  • REST API reads: GET /orders/123, GET /orders?status=shipped
  • Health checks and polling endpoints
  • Conditional requests with If-None-Match / If-Modified-Since to avoid re-downloading unchanged data

Security considerations

Because GET puts everything in the URL, anything sensitive in a query string ends up in browser history, proxy logs, server access logs, and the Referer header of any link a user clicks from that page. Never put passwords, tokens, or PII in a GET query string — use POST (or QUERY) with a body instead, over HTTPS.

GET's safety guarantee is a convention, not an enforced rule — nothing stops a server from mutating state on a GET. Doing so breaks browser prefetching, crawler behavior, and caching in ways that cause real bugs (a crawler "deleting" every item in an admin panel by following GET /delete/:id links is a classic horror story).

FAQ

Can GET have a request body?

Technically HTTP doesn't forbid it, but in practice: don't. Many clients, proxies, and servers strip or ignore GET bodies entirely, so behavior is unpredictable. If you need a body with safe/idempotent semantics, use QUERY instead — it's the standardized answer to this exact problem.

Why is GET cacheable by default?

Because it's safe — a GET is defined as not changing anything, so serving a slightly stale cached copy is (usually) harmless. Cache-Control, ETag, and Last-Modified let you tune exactly how stale is acceptable.

Is GET always idempotent in practice?

By spec, yes. In practice, a GET that triggers a side effect (like incrementing a view counter) is still spec-safe as far as HTTP is concerned — the side effect isn't something the client requested, and most implementations treat it as acceptable. But GETs that create, delete, or meaningfully mutate data are a misuse of the method.

Fun fact

GET is so foundational that the HTTP/0.9 protocol (1991) supported only GET — no headers, no status codes, no other methods. You sent GET /page.html and got back raw HTML. Everything else — POST, headers, status codes — was added in HTTP/1.0 a few years later.

Related Methods