KAHIBARO
Discord Login Register

2.9. HTTP Responses

Understanding HTTP Responses

When your browser sends a request to a server, the server answers with an HTTP response. As a backend developer you will create these responses all the time, so you must understand what they contain and how they are structured.

This chapter focuses on the response side only. Other chapters explain HTTP methods, requests, headers, and status codes in more depth.

The Structure of an HTTP Response

An HTTP response has three main parts:

  1. Status line
  2. Headers
  3. Optional body (the content)

In raw text, a simple HTTP response looks like this:

http
HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
Content-Length: 42
<html><body><h1>Hello, world!</h1></body></html>

Let us break that down:

A valid HTTP response must start with a status line, followed by headers, then an empty line, then the body (if any).

The Status Line

The status line is always the first line of the response. It has three parts:

  1. HTTP version
  2. Status code
  3. Reason phrase

Format:

text
<HTTP version> <status code> <reason phrase>

Example:

http
HTTP/1.1 404 Not Found

As a backend developer you usually set the status code in your framework, and the framework generates the status line.

Examples in pseudo Python with FastAPI-like code:

python
from fastapi import FastAPI, Response
app = FastAPI()
@app.get("/ok")
def ok():
    return {"message": "Everything is fine"}  # status defaults to 200
@app.get("/not-found")
def not_found():
    return Response(content="Item not found", status_code=404)
@app.get("/server-error")
def server_error():
    return Response(content="Something went wrong", status_code=500)

These calls would produce status lines:

There is a separate chapter that goes deeply into status codes, so here you just need to remember:

Response Headers

Headers provide extra information about the response. They are key-value pairs, one per line, before the empty line.

Examples of common response headers:

http
Content-Type: application/json; charset=utf-8
Content-Length: 349
Date: Mon, 03 Feb 2025 10:15:30 GMT
Server: example-server/1.0
Cache-Control: no-cache
Set-Cookie: session_id=abc123; HttpOnly; Path=/; Secure

You will see specific headers in later chapters (HTTP headers, cookies, caching, etc). Here is a quick summary of a few that matter for almost every response.

Content-Type

Content-Type tells the client what kind of content the body contains.

Common values:

TypeMeaningExample body
text/htmlHTML page<html>...</html>
text/plainPlain textHello
application/jsonJSON data{"id": 1, "name": "Alice"}
image/pngPNG imageBinary image data
application/pdfPDF documentBinary PDF data
text/cssCSS stylesheetbody { color: red; }
application/javascriptJavaScriptconsole.log("Hi");

Always set the correct Content-Type header for your responses. Clients rely on it to handle the body correctly.

In code you usually do not set Content-Type manually for simple cases, the framework does it based on what you return.

Example:

python
from fastapi import FastAPI
from fastapi.responses import PlainTextResponse, HTMLResponse, JSONResponse
app = FastAPI()
@app.get("/text", response_class=PlainTextResponse)
def get_text():
    return "Just plain text"  # Content-Type: text/plain
@app.get("/html", response_class=HTMLResponse)
def get_html():
    return "<h1>Hello</h1>"   # Content-Type: text/html
@app.get("/json", response_class=JSONResponse)
def get_json():
    return {"message": "Hi"}  # Content-Type: application/json

Content-Length

Content-Length is the size of the body in bytes.

Example:

http
Content-Length: 42

Clients use this to know when the response body ends.

In most modern frameworks and web servers you do not compute this manually, the server calculates it.

Date

Date shows when the response was generated, in GMT.

Example:

http
Date: Mon, 03 Feb 2025 10:15:30 GMT

You rarely set this yourself, the web server adds it.

Server

Server identifies the software that generated the response, like:

http
Server: nginx/1.25.0

Again, typically added by the web server, not by your application.

Set-Cookie

Set-Cookie instructs the client to store a cookie. Cookies and sessions have their own chapters, but you should recognize the header:

http
Set-Cookie: session_id=abc123; HttpOnly; Path=/; Secure

As a backend developer you will often set and read cookies.

The Response Body

The body is the content part of the response. It is optional:

Examples of bodies:

HTML body

http
HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
<html>
  <body>
    <h1>Welcome</h1>
    <p>This is a simple page.</p>
  </body>
</html>

JSON body

http
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
{"id": 1, "name": "Alice", "is_active": true}

Empty body

http
HTTP/1.1 204 No Content
Content-Length: 0

When you build APIs, you will most often send JSON bodies.

Example in pseudo Python (FastAPI style):

python
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class User(BaseModel):
    id: int
    name: str
    is_active: bool
@app.get("/user/{user_id}")
def get_user(user_id: int):
    user = User(id=user_id, name="Alice", is_active=True)
    return user  # Automatically turned into JSON body

The framework converts the User object to JSON and puts it into the response body.

A Complete Example of an HTTP Response

Imagine a client sends:

http
GET /api/users/1 HTTP/1.1
Host: example.com
Accept: application/json

A typical backend might respond:

http
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Content-Length: 83
Date: Mon, 03 Feb 2025 10:20:00 GMT
Server: example-api/1.0
{"id":1,"name":"Alice","email":"alice@example.com","is_active":true}

Mapping that to your backend code:

python
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class User(BaseModel):
    id: int
    name: str
    email: str
    is_active: bool
@app.get("/api/users/{user_id}")
def get_user(user_id: int):
    # In real code you would query a database
    user = User(
        id=user_id,
        name="Alice",
        email="alice@example.com",
        is_active=True,
    )
    return user

The framework:

Different Types of HTTP Responses

HTTP responses can be grouped by the type of content and behavior they represent. You will use different types depending on whether you return a web page, an API response, or a file.

HTML Responses

Used for traditional web pages:

http
HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
<html>
  <body>
    <h1>Profile</h1>
    <p>Name: Alice</p>
  </body>
</html>

The browser parses and renders the HTML.

JSON Responses

Used for APIs consumed by JavaScript, mobile apps, other services:

http
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
{"id": 1, "name": "Alice"}

Other services parse the JSON, not humans.

Redirect Responses

Redirects tell the client to go to a different URL. Typical codes are 301, 302, 303, 307, 308.

Example:

http
HTTP/1.1 302 Found
Location: https://example.com/login
Content-Length: 0

Browser behavior:

  1. Reads the Location header.
  2. Automatically sends a new request to that URL.

In code:

python
from fastapi import FastAPI
from fastapi.responses import RedirectResponse
app = FastAPI()
@app.get("/old-path")
def old_path():
    return RedirectResponse(url="/new-path", status_code=302)

File Download Responses

Used to send files like PDFs or images for download.

Example:

http
HTTP/1.1 200 OK
Content-Type: application/pdf
Content-Disposition: attachment; filename="report.pdf"
Content-Length: 12345
...binary PDF data...

The important header is Content-Disposition: attachment, which tells the browser to download instead of display the file inline.

In code (FastAPI style):

python
from fastapi import FastAPI
from fastapi.responses import FileResponse
app = FastAPI()
@app.get("/download-report")
def download_report():
    return FileResponse("report.pdf", media_type="application/pdf", filename="report.pdf")

How Backends Generate Responses

You almost never write full raw HTTP responses by hand. Instead, you:

  1. Decide what status code to send.
  2. Choose or build a response object in your framework.
  3. Set headers if needed.
  4. Return content that becomes the body.

Example pattern in pseudo Python:

python
from fastapi import FastAPI, Response
from fastapi.responses import JSONResponse, HTMLResponse
app = FastAPI()
@app.get("/simple")
def simple():
    # Framework wraps this into:
    # HTTP/1.1 200 OK
    # Content-Type: application/json
    return {"message": "OK"}
@app.get("/custom")
def custom():
    # Manual control of status and headers
    data = {"message": "Created"}
    return JSONResponse(
        content=data,
        status_code=201,
        headers={"X-My-Header": "my-value"},
    )
@app.get("/html-page")
def html_page():
    html = "<h1>Hello page</h1>"
    return HTMLResponse(content=html, status_code=200)

Conceptually, every response you return is turned into the three response parts:

Every HTTP response you send should be:

  1. A meaningful status code that matches what happened.
  2. A correct Content-Type that matches the body.
  3. Consistent body format across your API, usually JSON.

Summary

Later chapters on HTTP status codes, headers, cookies, and responses in frameworks like FastAPI will show you how to use these pieces in real applications.

Views: 10

Comments

Please login to add a comment.

Don't have an account? Register now!