← back to the section

When you click "Log in" or open a list of orders, the page doesn't store that data itself — it asks the server. This "question and answer" exchange is the foundation of almost every application, and a tester needs to understand where in it things break.

We'll cover what a request and a response are made of and what the codes the server answers with mean. This is the foundation on which DevTools and API testing rest.

Client and server

A client is what a person uses: a browser, a mobile app. It shows the interface and sends requests. A server is a computer somewhere in a data center that stores data (users, orders) and responds to client requests.

An analogy: the client is a guest at a café, the server is the kitchen. The guest places an order (request), the kitchen prepares it and sends it out (response). The guest doesn't go into the kitchen.

Why this matters for a tester: a bug can be on the client (the page sent the wrong thing, or displayed the response incorrectly) or on the server (it processed something wrong, returned an error). Understanding this split makes your bug report more precise.

HTTP and HTTPS

HTTP (HyperText Transfer Protocol) is the language client and server use to communicate: how to format a request, how to format a response. Every time a page loads something or submits data, an HTTP exchange happens.

HTTPS is the same thing, but encrypted (the S stands for secure): in transit the request can't be intercepted and read. A proper website today always uses HTTPS; if a site uses plain HTTP, the browser marks it as "not secure." That's also something to check — that important pages (payment, login) use HTTPS.

Not everything is hidden, though. Encrypted are the body of the request and response, the headers and the path: /api/orders/17?token=abc is invisible from outside. The domain is not: your provider and the owner of the Wi-Fi know you visited example.com, but not what you did there. Hiding a secret in the address is therefore pointless — it will settle in the server logs and in the browser history.

On a test environment HTTPS is usually served with a self-signed certificate — one nobody vouched for. The browser complains, and for a test environment that is normal, not a bug; the bug is the same warning in production, most often an expired certificate. That is also why a request to a test environment doesn't go through from Postman or a phone: the client doesn't trust the certificate.

Request: what it's made of

A request has four parts:

  • Method — what we want to do. The main ones: GET (retrieve data — "show me the orders"), POST (create — "place an order"), PUT/PATCH (update), DELETE (delete).
  • URL (address) — where we're sending the request, for example example.com/api/orders.
  • Headers — metadata: data format, token, language.
  • Body — the data being sent, usually JSON and usually with POST.

The word CRUD (create — read — update — delete) means exactly these methods: POST, GET, PUT or PATCH, DELETE.

JSON is a simple text format of "key: value" pairs that clients and servers exchange:

{ "email": "anna@example.com", "amount": 1500 }

Response: what it's made of

For a tester, two things matter in the response:

  • Status code — a short numeric summary of the request: 2xx success, 3xx another address, 4xx the request is at fault, 5xx the server broke.
  • Response body — the actual data (usually JSON): a list of orders, a created object, an error message.

Status codes: which one when

The group points a direction; the exact number says what to fix, and in a bug report it weighs more than words. The check is not "did a 200 come back" but "is this the right code here": a server answering 200 for an order that doesn't exist misleads the client and you alike.

  • 200, 201 and 204. 200 — "here's the data". 201 — "created": the answer to a POST, with the new object's address usually in the Location header. 204 — "done, nothing to show": the usual answer to DELETE, the body is empty, and a client crashing while parsing that empty body is a client-side defect.
  • 301 vs 302. 301 means moved permanently, and the browser remembers the new address for a long time: a wrong 301 on a test environment haunts you until the cache is cleared. 302 is temporary, the old address stays the main one; that's the code a temporary stub gets.
  • 400 vs 422. 400 — the request couldn't be parsed: broken JSON, wrong type, a required field missing. 422 — parsed fine, but the data argues with the rules: a date in the past, an amount larger than the balance. 400 is fixed by the client, 422 is a business rule, and the response must say which field is wrong.
  • 401 vs 403. 401 — "I don't know who you are": no token, or it expired. 403 — "I know, and you may not". The details are in logging in.
  • 404 vs 409. 404 — the object doesn't exist. 409 — a conflict: the object is there, but the action argues with its state: an e-mail already taken at sign-up, cancelling an already cancelled order. 404 instead of 409 is a common swap: a person reads "not found" where the truth is "already taken".
  • 429. Too often — a rate limit kicked in. Caught by re-sending an SMS code: we expect a Retry-After header and readable text, not a blank screen.
  • 500 vs 502 and 504. 500 is written by the application itself — it crashed inside. 502 and 504 come from an intermediary between you and the application: "the one who should have answered didn't" and "didn't wait long enough". Different people fix those — more in network basics.

Repeating a request: what is safe and what is not

The "Pay" button gets clicked twice, the connection drops halfway, the mobile client retries on its own. The question is not whether that happens, but what happens then.

the same request went out twice: a slipped finger, a flaky connection POST /orders "create one more" server attempt 1order #17 created attempt 2order #18 created — a second one PUT /orders/17 "make it so" server attempt 1#17: paid attempt 2#17: paid — same result GET, PUT and DELETE are safe to repeat; POST — only with an idempotency key

Two identical POST requests give two orders: the server assigns the address for a new object, so it honestly creates a second one. Two identical PUT requests give the same outcome: the address is known, and the second request simply overwrites with the same values. That is why a double click is tested on POST, not on PUT.

Methods have two properties. Safe — changes nothing on the server: GET can be pulled as often as you like. Idempotent — a repeat doesn't change the outcome: however many times you send it, the state is one and the same. GET, PUT and DELETE are idempotent, POST is not.

The reason is in what the methods mean. PUT says "make it so" and knows the object's address: a second identical PUT overwrites with the same values. DELETE says "make it gone": there is nothing left to delete the second time, and even if a 404 comes back, the state is unchanged. POST says "create one more", and the server assigns the address for the new object — so a second POST honestly creates a second order.

Three checks follow: a double click on "Submit" — one order or two (look in the data, not on the screen); re-sending the same request from Network or Postman; and the idempotency key — for payments the client sends a header like Idempotency-Key, and for the same key the server must return the result of the first attempt instead of taking a second payment: two requests with one key — one payment, with different keys — two.

Headers worth looking at

Headers get scrolled past by many — and for nothing: they decide how the server reads the request and what the browser does with the response.

Content-Type declares the format of the body: application/json for JSON, multipart/form-data for file uploads. A mistake here looks odd: the body is correct, and the response is 400 or 415 — the server refused to parse what was declared in the wrong format. Authorization says who is asking, usually Bearer and a token; no header — 401, details in logging in. Cache-Control says how long the response may be kept in the cache: because of it a page shows yesterday's data while the server already serves new.

Where this applies

This model sits beneath every action in an application. A form "silently" didn't save — you open the Network tab: did the request go out? what method, what status code? 500 — the server, no request at all — the client. Trying out variations of a request by hand is convenient in Postman.

Where beginners stumble:

  • Checking only whether a 200 came back. The code has to be right by meaning: a 200 for an order that doesn't exist is a defect, even when the screen looks decent.
  • Treating a repeated request as theory. A double click on "Pay" is ordinary life, and a second payment after it is a defect.

What to learn next. Watch the exchange in DevTools, test requests by hand in Postman, and see what happens below HTTP in network basics.