Skip to main content

July 22, 2026

HTTP for CTF: Requests, Headers, Status Codes, and DevTools

HTTP fundamentals for web CTF: read a request and response, forge headers with curl, use the DevTools Network tab, and solve the header and redirect challenge class.

A blank card travelling a rail between two cabinets while a tool folds down one of its corners.

Every web challenge is an HTTP request you can edit

Web exploitation looks like a category about browsers. It is not. A browser is one particular HTTP client that refuses to send certain things, hides others from you, and adds headers you never asked for. Once you understand that the server only ever sees a few hundred bytes of text, and that you control every one of those bytes, the entire category changes shape. SQL injection, XSS, SSRF, IDOR, and file upload are all the same move: put something the developer did not expect into a request the developer did not expect you to edit.

The browser is not the client. It is a convenience wrapper around the client. The client is the request, and the request is a text file you can write by hand.

This guide covers the protocol layer that every other web guide on this site assumes: how a request is structured, which headers CTF authors reach for, how to forge them with curl, and how to read the DevTools Network tab like a proxy log. If you already know this and want the attack classes, start at the web exploitation roadmap instead. If you want to find the endpoints in the first place, that is the web recon guide.

Anatomy of a request and a response

An HTTP request is a start line, headers, a blank line, and an optional body. That is the entire format, and it is specified in RFC 9112, which replaced the older RFC 7230 in 2022 when the HTTP specifications were split into semantics (RFC 9110) and wire format (RFC 9112). Here is a real request, complete:

POST /login HTTP/1.1
Host: example.picoctf.net
User-Agent: curl/8.5.0
Cookie: session=eyJhZG1pbiI6ZmFsc2V9
Content-Type: application/x-www-form-urlencoded
Content-Length: 31
username=admin&password=hunter2

The response has the same shape, with a status line instead of a start line:

HTTP/1.1 302 Found
Location: /dashboard
Set-Cookie: session=eyJhZG1pbiI6dHJ1ZX0; HttpOnly
Content-Length: 0

Four things are worth internalizing from those fifteen lines. The path and the Host header are separate, which is what makes virtual-host tricks possible. Cookies are just a header, so nothing stops you from editing one. The body format is declared by Content-Type, and changing that declaration changes how the server parses your input. And the blank line is structural: it is what separates headers from body, which is why injecting a newline into a header value is a bug class of its own.

That last point has a name and a number. The separator between header fields is CRLF (bytes 0x0D 0x0A), and the header section ends with a bare CRLF, so a reflected newline lets you invent headers or start a second response. MITRE catalogues it as CWE-113, HTTP response splitting. RFC 9112 is explicit that a sender must not generate a bare CR or LF inside a field value, precisely because recipients disagree about how to handle one.

Key insight: Every header is a claim your client makes about itself, and the server chooses whether to believe it. There is no authentication on any header unless the developer added it. That single fact is the whole "header manipulation" challenge class.

Methods: the ones that matter and the ones that surprise servers

RFC 9110 defines eight methods (GET, HEAD, POST, PUT, DELETE, CONNECT, OPTIONS, TRACE), with PATCH registered separately by RFC 5789, and an HTML form can only ever produce two of them. That gap is where a large share of easy web findings live, because a route reachable only outside the browser is a route that was likely never tested from one. The RFC also marks GET, HEAD, OPTIONS, and TRACE as safe and PUT and DELETE as idempotent, distinctions that some frameworks use to decide whether CSRF protection applies at all.

  • GET sends parameters in the URL query string. They land in server logs and in browser history, which is why credentials in a query string are themselves a finding.
  • POST sends a body. Three encodings dominate: application/x-www-form-urlencoded, multipart/form-data (used for uploads, see the file upload guide), and application/json.
  • PUT, DELETE, PATCH are common in APIs and frequently less guarded than the GET route beside them. A route that checks authorization on GET and forgets on DELETE is a real and repeated CTF bug.
  • OPTIONS sometimes lists the allowed methods in an Allow header, which is free reconnaissance.
  • HEAD returns headers without a body. Useful for probing large files and for spotting a header-only flag quickly.
Tip: Method tampering is a two-second experiment worth running on every protected route: resend the same request as POST instead of GET, or vice versa. Frameworks that route by path but check permissions by method fall over immediately.

Status codes are the oracle you get for free

In a blind situation, status codes are often the only signal. Learn to read them as answers rather than as errors. RFC 9110 defines five classes (1xx informational, 2xx success, 3xx redirection, 4xx client error, 5xx server error) and registers around 40 specific codes. Two of them are the whole game for enumeration: the RFC describes 403 Forbidden as the server understanding the request but refusing it, and 404 Not Found as the server having no representation at all. A 403 therefore confirms the resource exists. It also notes that a server may send 404 instead of 403 to hide that existence, which is exactly the distinction a directory brute-force is measuring.

CodeMeansWhat it tells an attacker
200OKThe resource exists and you were allowed
301 / 302RedirectA body may still be present and may contain the flag
401UnauthenticatedCredentials are missing, and the challenge is about supplying them
403ForbiddenThe resource exists. Something about you, not the path, was rejected
404Not foundKeep enumerating. In a fuzz run, this is your baseline
405Method not allowedThe route is real. Try the other verbs
500Server errorYour input reached code that did not expect it. This is progress
Note: 403 and 404 are different answers, and the difference is the basis of enumeration. A 403 on /admin confirms that /admin exists, which is worth more than a 200 on a page you already knew about. A 500 after you insert a single quote is the classic first sign of SQL injection.

The headers CTF authors build challenges around

There is no shortage of them to try. The IANA HTTP Field Name Registry lists well over 300 standardised field names, and applications invent their own X- headers freely on top of that. Not one of them is authenticated. The handful below are the ones challenge authors actually reach for.

  • User-Agent identifies the client. Servers that gate content on a specific browser string are a recurring beginner challenge, and the fix is one curl -A flag.
  • Referer (misspelled in the standard) says where you came from. Used to enforce "you must arrive from the login page," and trivially forged.
  • Cookie carries session state. Editing it is the entire content of the cookies and JWT guide. Flip admin=false to admin=true before assuming anything is signed.
  • Authorization holds Basic credentials (base64, not encryption) or a Bearer token. Base64 in a header is decoding work, not cracking work.
  • Host selects the virtual host. Changing it can reach an internal site served by the same server.
  • X-Forwarded-For claims a client IP. Apps that restrict admin pages to 127.0.0.1 and trust this header hand you the page for free. Its standardised successor, the Forwarded header of RFC 7239, carries the same risk, and the RFC's own security section says the header is unauthenticated and must not be trusted for access control. Try both spellings, plus X-Real-IP and X-Originating-IP.
  • Content-Type decides the parser. Switching a form post to application/json sometimes reaches a completely different code path, which is the entry point for NoSQL injection operator payloads.
  • Accept-Language, Origin, custom X- headers round out the set. Any header the app reads is a header you control.
Warning: Response headers matter as much as request headers. Read Set-Cookie flags, Content-Security-Policy (it constrains your XSS payload), Server and X-Powered-By for stack fingerprinting, and any custom header. Flags have been hidden in response headers more than once, and a browser will never show you one unless you open the Network tab.

curl recipes worth memorizing

curl is the fastest way to send a request that a browser would refuse to send. These eight flags cover most of web CTF.

curl -i https://target/ # show response headers with the body
curl -v https://target/ # show the request you sent too
curl -A 'picobrowser' https://target/ # forge User-Agent
curl -e 'https://target/login' https://target/ # forge Referer
curl -H 'X-Forwarded-For: 127.0.0.1' https://target/admin
curl -b 'admin=true; session=abc' https://target/
curl -X PUT https://target/api/user/2 # change the method
curl -d 'user=admin&pass=x' https://target/login # form POST
curl -H 'Content-Type: application/json' -d '{"user":"admin"}' https://target/api/login
curl -L https://target/ # follow redirects
curl -s -o /dev/null -w '%{http_code}\n' https://target/path # status only
Tip: The single highest-value habit: curl -i by default, never plain curl. Half the beginner challenges in this class hide the flag in a response header or in the body of a redirect, and both are invisible without it.

When you need to replay a complicated request, do not retype it. In DevTools, right-click the request in the Network tab and choose "Copy as cURL," then edit one field. For anything iterative, move to Burp Suite Repeater, and for scripted sequences use Python requests with a Session object so cookies persist automatically.

The DevTools workflow, in the order that finds things

Open DevTools with F12 and work these five panels in order. This sequence finds the flag in most beginner web challenges before you have tried a single exploit.

  1. Elements. Read the rendered DOM, not the source. Look for hidden inputs, commented-out blocks, and elements with display: none. Authors hide flags in markup constantly.
  2. Network. Reload with the panel open, then check Preserve log so redirects do not erase the evidence. Inspect every request: headers, response, and the initiator that fired it. Filter to XHR to find the API the page talks to.
  3. Sources. Read the JavaScript. Client-side validation, hardcoded endpoints, and API keys live here. If it is minified or packed, the JavaScript deobfuscation guide covers unpacking it.
  4. Application (or Storage). Cookies, localStorage, sessionStorage, and service workers. Cookies are editable in place, which is often faster than any tool.
  5. Console. Call the page's own functions directly. A client-side check written as checkPassword() can usually just be invoked, read, or redefined from the console.
Note: Disable cache in the Network tab while testing, and remember that DevTools shows you what the browser did, not what it is capable of. When you need a request the browser will not make (a forged Host, an invalid method, a raw newline), leave the browser and use curl or a proxy.

Redirects, and the flag you never saw

A 302 response can carry a full body, and RFC 9110 even suggests that 3xx responses should include a short hypertext note with a link to the new URI. Browsers throw that body away and follow the Location header, so a flag printed on a page that immediately redirects is invisible in normal browsing and obvious in curl. This is the single most common "where did it go" moment in beginner web CTF.

The redirect codes are not interchangeable, and the difference occasionally is the challenge. RFC 9110 defines 301 and 302 as historically allowing the client to change a POST into a GET, while 307 and 308 were introduced precisely to forbid that rewrite and preserve the method and body. If curl -L silently turns your POST into a GET and the challenge stops working, that is the rule you just met; add --post302 or send the second request by hand.

curl -i https://target/secret # see the 302 AND its body
curl -i -L https://target/secret # follow, showing every hop

The same idea applies to responses your browser renders in a way that hides content: aContent-Type of text/html will swallow anything that looks like a tag. Fetch with curl and read the raw bytes, or use View Source rather than the Elements panel when you suspect the DOM has rewritten what the server actually sent.

Key insight: Three places a flag hides in plain sight, in the order you should check them: a response header, the body of a redirect, and an HTML comment. All three are free to check and take about ten seconds combined.

Where to practice

This challenge cluster is the friendliest entry point in the entire web category. Every one of these is solved with header editing, a redirect, or the Network tab.

  • picoCTF 2019 picobrowser gates the flag on a specific User-Agent. One curl -A away.
  • picoCTF 2021 Who are you? stacks a series of header checks: User-Agent, Referer, Date, X-Forwarded-For, and more, one per step.
  • picoCTF 2021 Get aHEAD wants a HEAD request, the method challenge in its purest form.
  • picoCTF 2024 Intro to Burp introduces intercepting and editing a request in flight rather than replaying it after the fact.
  • picoCTF 2019 logon is the cookie flip: log in as anyone, then set the admin cookie yourself.
  • picoCTF 2022 Power Cookie is the same idea with an unsigned cookie value the server trusts completely.
  • picoCTF 2025 head-dump is enumeration rather than header editing: an unauthenticated Spring Boot Actuator endpoint streams a full JVM heap snapshot, and the secrets are simply sitting in it. Finding the route is the challenge.
  • picoCTF 2026 failure failure puts a load balancer in front of the flag. You force the primary backend to look unhealthy so traffic fails over to a less hardened backup, which is request manipulation aimed at infrastructure rather than at the app.

Quick reference

First five moves on any web challenge

  1. curl -i the page and read every response header.
  2. View source and search for <!--, hidden, and flag.
  3. Open the Network tab with Preserve log on and reload.
  4. Open Application and read every cookie and storage key.
  5. Change one header the app appears to trust and resend.

Where to go next

Finding endpoints is web recon. Editing sessions is cookies and JWT. Intercepting rather than replaying is Burp Suite. Access-control logic is auth bypass and IDOR. The full ordering of the category is the web exploitation roadmap.

Learn to see the request under the page and web challenges stop being about websites. They become about a short block of text that you, and not the browser, get to write.

Sources and further reading

HTTP is one of the best-specified things you will ever attack. When a server behaves strangely, the RFC almost always says why.

Run it in the browser

Tools on this site that do the work described above. No install, nothing uploaded: they run entirely in your browser.

Try it on these picoCTF challenges

Walkthroughs that put this technique to work, grouped by event.

Keep reading

Guides that build on the same ideas, plus the roadmap this topic sits under.