HTTP Fundamentals - Complete Interview and Production Guide
Learn HTTP fundamentals including request and response structure, methods, status codes, headers, versions, connections, caching, content negotiation, HTTPS, Spring Boot examples, production scenarios, and 15 interview questions with answers.
Introduction
HTTP is the communication protocol used by browsers, mobile applications, API clients, gateways, load balancers, and backend services to exchange information over a network.
REST APIs depend heavily on HTTP.
To design production-ready APIs, developers must understand:
- HTTP requests
- HTTP responses
- HTTP methods
- Status codes
- Headers
- Message bodies
- Connections
- Caching
- Content negotiation
- Authentication headers
- HTTP versions
- HTTPS
- Proxies and gateways
- Timeouts and retries
A developer who understands only controller annotations but not HTTP behavior may create APIs that are difficult to cache, retry, secure, monitor, or scale.
What Is HTTP?
HTTP stands for Hypertext Transfer Protocol.
It is an application-layer protocol used for communication between clients and servers.
A client sends an HTTP request.
A server processes the request and returns an HTTP response.
Client
↓
HTTP Request
↓
Server
↓
HTTP Response
↓
Client
Example:
GET /api/v1/customers/101 HTTP/1.1
Host: api.codewithvenu.com
Accept: application/json
Response:
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 68
{
"id": 101,
"name": "Venu",
"status": "ACTIVE"
}
Where HTTP Fits in the Network Stack
HTTP operates at the application layer.
Application Layer
HTTP / HTTPS
↓
Transport Layer
TCP or QUIC
↓
Internet Layer
IP
↓
Network Access Layer
Ethernet / Wi-Fi
Traditional HTTP versions commonly use TCP.
HTTP/3 uses QUIC, which operates over UDP.
HTTP Communication Model
HTTP follows a request-response communication model.
Client
↓
Request Line
Request Headers
Request Body
↓
Server Processing
↓
Status Line
Response Headers
Response Body
↓
Client
The server normally does not send a response until it receives a request, except in technologies built on top of persistent communication patterns such as WebSockets or server-sent events.
Basic HTTP Request Structure
An HTTP request contains:
- Request line
- Headers
- Empty line
- Optional body
Example:
POST /api/v1/customers HTTP/1.1
Host: api.codewithvenu.com
Content-Type: application/json
Accept: application/json
Authorization: Bearer eyJhbGciOi...
Content-Length: 86
{
"name": "Venu",
"email": "[email protected]",
"city": "San Antonio"
}
Request Line
The request line contains:
HTTP Method + Request Target + HTTP Version
Example:
GET /api/v1/customers/101 HTTP/1.1
Meaning:
| Part | Value |
|---|---|
| Method | GET |
| Request target | /api/v1/customers/101 |
| Protocol version | HTTP/1.1 |
HTTP Request Headers
Headers provide metadata about the request.
Common request headers:
| Header | Purpose |
|---|---|
| Host | Identifies the target host |
| Accept | Specifies preferred response format |
| Content-Type | Describes the request-body format |
| Authorization | Carries authentication credentials |
| User-Agent | Identifies the client |
| Cache-Control | Defines caching behavior |
| If-None-Match | Supports conditional requests using ETags |
| If-Modified-Since | Supports date-based conditional requests |
| Accept-Encoding | Indicates supported compression formats |
| Accept-Language | Indicates preferred language |
| Idempotency-Key | Helps prevent duplicate processing |
| X-Correlation-ID | Helps trace a request across systems |
Example:
Accept: application/json
Content-Type: application/json
Authorization: Bearer <access-token>
X-Correlation-ID: c72f6f93-9a53-4f85-8f51
HTTP Request Body
The body carries data sent to the server.
Request bodies are common with:
- POST
- PUT
- PATCH
Example JSON body:
{
"name": "Venu",
"email": "[email protected]"
}
Other possible body formats:
- XML
- Form data
- Multipart data
- Plain text
- Binary data
- Protocol-specific payloads
Basic HTTP Response Structure
An HTTP response contains:
- Status line
- Response headers
- Empty line
- Optional body
Example:
HTTP/1.1 201 Created
Content-Type: application/json
Location: /api/v1/customers/101
Cache-Control: no-store
Content-Length: 68
{
"id": 101,
"name": "Venu",
"status": "ACTIVE"
}
Status Line
The status line contains:
HTTP Version + Status Code + Reason Phrase
Example:
HTTP/1.1 200 OK
Meaning:
| Part | Value |
|---|---|
| Protocol version | HTTP/1.1 |
| Status code | 200 |
| Reason phrase | OK |
HTTP Response Headers
Common response headers:
| Header | Purpose |
|---|---|
| Content-Type | Response-body format |
| Content-Length | Response size in bytes |
| Location | URI of a newly created or redirected resource |
| Cache-Control | Response caching rules |
| ETag | Version identifier for a resource representation |
| Last-Modified | Last update time |
| Retry-After | Indicates when a client should retry |
| Set-Cookie | Creates or updates a browser cookie |
| WWW-Authenticate | Describes authentication requirements |
| Content-Encoding | Indicates compression |
| Access-Control-Allow-Origin | Controls browser cross-origin access |
| X-RateLimit-Remaining | Shows remaining API quota |
HTTP Methods
HTTP methods describe the intended action.
| Method | Typical Purpose | Safe | Idempotent |
|---|---|---|---|
| GET | Retrieve a resource | Yes | Yes |
| HEAD | Retrieve headers only | Yes | Yes |
| POST | Create or trigger processing | No | No by default |
| PUT | Replace or create a resource at a known URI | No | Yes |
| PATCH | Partially update a resource | No | Depends on implementation |
| DELETE | Remove a resource | No | Yes by HTTP semantics |
| OPTIONS | Discover communication options | Yes | Yes |
| TRACE | Diagnostic loopback | Yes | Yes |
GET Method
GET retrieves information.
Example:
GET /api/v1/customers/101
Response:
{
"id": 101,
"name": "Venu"
}
Characteristics:
- Safe
- Idempotent
- Commonly cacheable
- Should not modify server state
Bad design:
GET /api/v1/customers/101/delete
A GET request should not delete data.
POST Method
POST submits data for processing or creates a resource under a collection.
Example:
POST /api/v1/customers
Content-Type: application/json
{
"name": "Venu",
"email": "[email protected]"
}
Response:
HTTP/1.1 201 Created
Location: /api/v1/customers/101
{
"id": 101,
"name": "Venu",
"email": "[email protected]"
}
POST is not idempotent by default.
Submitting the same request twice may create two resources.
For payment or order APIs, an idempotency key is often required.
PUT Method
PUT usually replaces the complete representation of a resource.
Example:
PUT /api/v1/customers/101
Content-Type: application/json
{
"name": "Venugopal",
"email": "[email protected]",
"status": "ACTIVE"
}
Sending the same PUT request multiple times should leave the resource in the same final state.
That makes PUT idempotent.
PATCH Method
PATCH performs a partial update.
Example:
PATCH /api/v1/customers/101
Content-Type: application/json
{
"status": "INACTIVE"
}
Only the supplied field is updated.
PATCH is not automatically idempotent.
For example, this operation may not be idempotent:
{
"incrementRewardPointsBy": 100
}
Sending it twice changes the result twice.
This operation can be idempotent:
{
"status": "INACTIVE"
}
Sending it repeatedly produces the same final state.
DELETE Method
DELETE removes a resource.
Example:
DELETE /api/v1/customers/101
Possible response:
HTTP/1.1 204 No Content
The first request may delete the resource.
A repeated request may return:
HTTP/1.1 404 Not Found
DELETE is still considered idempotent because the final server state remains the same: the resource does not exist.
Idempotent does not mean every response must be identical.
HEAD Method
HEAD is similar to GET, but the response does not include a message body.
Example:
HEAD /api/v1/documents/report.pdf
Response:
HTTP/1.1 200 OK
Content-Type: application/pdf
Content-Length: 409600
ETag: "document-v12"
Use cases:
- Check whether a resource exists
- Read metadata
- Validate cache entries
- Determine content size before downloading
OPTIONS Method
OPTIONS tells the client which communication options are supported.
Example:
OPTIONS /api/v1/customers
Response:
HTTP/1.1 204 No Content
Allow: GET, POST, OPTIONS
Browsers also use OPTIONS for CORS preflight requests.
Safe vs Idempotent Methods
Safe Method
A safe method should not change server state.
Safe methods:
GET
HEAD
OPTIONS
TRACE
Idempotent Method
Calling an idempotent method multiple times should produce the same final server state as calling it once.
Common idempotent methods:
GET
HEAD
PUT
DELETE
OPTIONS
TRACE
POST is not idempotent by default.
PATCH depends on the operation.
HTTP Status Code Categories
HTTP status codes are grouped into five categories.
| Range | Category | Meaning |
|---|---|---|
| 100–199 | Informational | Request is continuing |
| 200–299 | Success | Request succeeded |
| 300–399 | Redirection | Additional action is required |
| 400–499 | Client Error | Request has a client-side problem |
| 500–599 | Server Error | Server failed to process a valid request |
Important 2xx Status Codes
200 OK
Use when the request succeeds and a response body is returned.
GET /api/v1/customers/101
HTTP/1.1 200 OK
201 Created
Use when a new resource is created.
POST /api/v1/customers
HTTP/1.1 201 Created
Location: /api/v1/customers/101
202 Accepted
Use when the request is accepted for asynchronous processing.
POST /api/v1/reports
HTTP/1.1 202 Accepted
Location: /api/v1/jobs/JOB-901
The work may not be complete yet.
204 No Content
Use when the operation succeeds but no response body is needed.
Common use:
DELETE /api/v1/customers/101
HTTP/1.1 204 No Content
A 204 response should not contain a response body.
Important 3xx Status Codes
301 Moved Permanently
The resource has permanently moved.
Clients and search engines may cache the new location.
302 Found
Temporary redirection.
Historically, some clients may change POST to GET during redirection.
303 See Other
Directs the client to retrieve another resource using GET.
Often useful after processing a POST request.
304 Not Modified
Used with conditional requests.
It tells the client to reuse its cached representation.
Example request:
GET /api/v1/products/101
If-None-Match: "product-v5"
Response:
HTTP/1.1 304 Not Modified
ETag: "product-v5"
No response body is returned.
307 Temporary Redirect
Temporary redirect that preserves the original HTTP method and body.
308 Permanent Redirect
Permanent redirect that preserves the original HTTP method and body.
Important 4xx Status Codes
400 Bad Request
Use when the request is malformed or validation fails.
Example:
{
"code": "INVALID_REQUEST",
"message": "Email address is required."
}
401 Unauthorized
Despite the name, 401 usually means the request is not successfully authenticated.
Possible causes:
- Missing token
- Expired token
- Invalid token
- Invalid credentials
Example:
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer
403 Forbidden
The caller is authenticated but does not have permission.
Example:
User is logged in
but
User does not have ADMIN permission
404 Not Found
The requested resource does not exist or is intentionally hidden.
GET /api/v1/customers/9999
HTTP/1.1 404 Not Found
405 Method Not Allowed
The endpoint exists, but the method is unsupported.
Example:
DELETE /api/v1/reports
Response:
HTTP/1.1 405 Method Not Allowed
Allow: GET, POST
406 Not Acceptable
The server cannot produce a representation matching the client's Accept header.
Example:
Accept: application/xml
when the API supports only JSON.
409 Conflict
The request conflicts with the current resource state.
Examples:
- Duplicate email
- Version conflict
- Resource already exists
- Invalid state transition
412 Precondition Failed
A condition supplied by the client was not satisfied.
Often used with optimistic concurrency.
Example:
If-Match: "customer-v5"
If the current ETag is customer-v6, the update may return 412.
415 Unsupported Media Type
The server does not support the request-body format.
Example:
Content-Type: application/xml
when the endpoint accepts only JSON.
422 Unprocessable Content
The request is syntactically valid but fails semantic or business validation.
Example:
{
"withdrawalAmount": -500
}
429 Too Many Requests
The client exceeded a rate limit.
HTTP/1.1 429 Too Many Requests
Retry-After: 60
Important 5xx Status Codes
500 Internal Server Error
An unexpected server-side failure occurred.
Do not expose stack traces or database details to clients.
502 Bad Gateway
A gateway or proxy received an invalid response from a downstream service.
Client
↓
API Gateway
↓
Payment Service returned invalid response
↓
502 Bad Gateway
503 Service Unavailable
The service is temporarily unavailable.
Possible causes:
- Maintenance
- Overload
- Dependency outage
- No healthy service instances
A Retry-After header may be provided.
504 Gateway Timeout
A gateway did not receive a response from the downstream service within the allowed time.
Client
↓
Gateway
↓
Order Service
↓
Payment Service takes too long
↓
504 Gateway Timeout
401 vs 403
| Status | Meaning |
|---|---|
| 401 | Authentication is missing or invalid |
| 403 | Authentication succeeded, but permission is insufficient |
Example:
No valid access token
→ 401
Valid token without ADMIN role
→ 403
400 vs 422
| Status | Typical Use |
|---|---|
| 400 | Malformed request, invalid JSON, missing syntax-level information |
| 422 | Structurally valid request that violates semantic or business rules |
Teams should choose a consistent convention and document it.
500 vs 502 vs 503 vs 504
| Status | Meaning |
|---|---|
| 500 | The application failed unexpectedly |
| 502 | A proxy received an invalid downstream response |
| 503 | The service is unavailable or overloaded |
| 504 | A proxy timed out waiting for a downstream response |
Content-Type vs Accept
These headers solve different problems.
Content-Type
Describes the format of the current message body.
Request example:
Content-Type: application/json
Meaning:
The request body is JSON.
Accept
Describes the response format preferred by the client.
Accept: application/json
Meaning:
The client wants JSON.
Example:
POST /api/v1/customers
Content-Type: application/json
Accept: application/json
Content Negotiation
Content negotiation allows the client and server to agree on a representation.
Client request:
Accept: application/json
Server response:
Content-Type: application/json
Possible representation types:
application/jsonapplication/xmltext/plaintext/htmlapplication/pdfapplication/octet-stream
URI Components
Example:
https://api.codewithvenu.com:443/api/v1/customers/101?include=accounts#summary
Components:
| Component | Value |
|---|---|
| Scheme | https |
| Host | api.codewithvenu.com |
| Port | 443 |
| Path | /api/v1/customers/101 |
| Query string | include=accounts |
| Fragment | summary |
The URL fragment is normally processed by the client and is not sent to the server in an HTTP request.
Path Parameters
Path parameters identify a particular resource.
GET /api/v1/customers/101
Here:
101 = customerId
Spring Boot:
@GetMapping("/{customerId}")
public CustomerResponse getCustomer(
@PathVariable Long customerId
) {
return customerService.getCustomer(customerId);
}
Query Parameters
Query parameters modify retrieval behavior.
Example:
GET /api/v1/customers?status=ACTIVE&page=0&size=20
Spring Boot:
@GetMapping
public List<CustomerResponse> getCustomers(
@RequestParam String status,
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "20") int size
) {
return customerService.getCustomers(
status,
page,
size
);
}
Common query uses:
- Filtering
- Sorting
- Searching
- Pagination
- Optional response expansion
HTTP Is Stateless
HTTP is stateless by design.
This means each request should contain enough information for the server to understand and process it.
Request 1
Authorization: Bearer token
↓
Server processes request
Request 2
Authorization: Bearer token
↓
Server independently processes request
The protocol itself does not automatically remember prior requests.
Applications may still implement state using:
- Cookies
- Sessions
- Databases
- Distributed caches
- Access tokens
Stateless HTTP does not mean the application cannot store state.
Cookies and Sessions
A server may create a session and send a cookie.
Response:
Set-Cookie: SESSION_ID=abc123; HttpOnly; Secure; SameSite=Lax
Later request:
Cookie: SESSION_ID=abc123
The cookie allows the server to identify the session.
Important cookie attributes:
| Attribute | Purpose |
|---|---|
| Secure | Send cookie only over HTTPS |
| HttpOnly | Prevent JavaScript access |
| SameSite | Reduce cross-site request risks |
| Max-Age | Cookie lifetime |
| Path | Restrict cookie path |
| Domain | Restrict domain scope |
HTTP Connections
HTTP/1.0
Connections were commonly closed after each response unless keep-alive was explicitly used.
Request
↓
Response
↓
Connection Closed
HTTP/1.1
Persistent connections are enabled by default.
Connection Open
↓
Request 1 / Response 1
↓
Request 2 / Response 2
↓
Request 3 / Response 3
This reduces connection setup overhead.
HTTP/1.1 also introduced or standardized features such as:
- Host header
- Persistent connections
- Chunked transfer encoding
- Better caching support
HTTP/2
HTTP/2 supports multiplexing.
Multiple request-response streams can share a single connection.
One TCP Connection
├── Stream 1: Customer Request
├── Stream 2: Account Request
├── Stream 3: Image Request
└── Stream 4: Notification Request
Benefits:
- Multiplexing
- Header compression
- Binary framing
- Better network utilization
- Server push support, though it is not widely relied upon today
HTTP/2 still uses TCP.
Packet loss can affect multiple streams sharing the same TCP connection.
HTTP/3
HTTP/3 uses QUIC over UDP.
Benefits include:
- Faster connection establishment
- Improved behavior during packet loss
- Stream-level independence
- Better connection migration when networks change
HTTP/3
↓
QUIC
↓
UDP
Example use case:
A mobile device changes from Wi-Fi to cellular connectivity while maintaining communication more effectively.
HTTP/1.1 vs HTTP/2 vs HTTP/3
| Feature | HTTP/1.1 | HTTP/2 | HTTP/3 |
|---|---|---|---|
| Transport | TCP | TCP | QUIC over UDP |
| Multiplexing | Limited | Yes | Yes |
| Header compression | No standard binary compression | HPACK | QPACK |
| Binary framing | No | Yes | Yes |
| Connection migration | No | No | Better support |
| Packet-loss isolation | Limited | TCP-level blocking | Stream-level improvement |
| Common API support | Yes | Yes | Increasing |
HTTP Keep-Alive
Creating a connection can require:
- DNS resolution
- TCP handshake
- TLS handshake
Reusing an existing connection reduces this overhead.
Without Reuse
Request 1 → New Connection
Request 2 → New Connection
Request 3 → New Connection
With Reuse
One Connection
├── Request 1
├── Request 2
└── Request 3
Connection pooling is critical for high-volume service-to-service communication.
HTTP Timeouts
Every production HTTP client should use explicit timeouts.
Common timeout types:
| Timeout | Meaning |
|---|---|
| Connection timeout | Maximum time to establish a connection |
| Read timeout | Maximum wait for response data |
| Write timeout | Maximum wait while sending data |
| Response timeout | Maximum total wait for a response |
| Connection acquisition timeout | Maximum wait for a pooled connection |
Without timeouts, threads and connections may wait indefinitely.
Retry Behavior
Retries can improve reliability, but they can also create duplicate operations or overload failing systems.
Safe retry candidates:
- GET
- HEAD
- PUT when correctly implemented
- DELETE when correctly implemented
- POST with a valid idempotency key
Avoid automatic retry for non-idempotent operations unless duplicate protection exists.
Example:
Client sends payment
↓
Server processes payment
↓
Response is lost
↓
Client retries
↓
Payment may execute twice
Solution:
Idempotency-Key: PAYMENT-REQ-1001
HTTPS
HTTPS is HTTP over a secure TLS connection.
HTTP
+
TLS
=
HTTPS
HTTPS provides:
- Encryption
- Server authentication
- Data integrity
- Protection against network eavesdropping
- Reduced risk of message tampering
Simplified TLS Flow
Client
↓
TLS Handshake
↓
Server Certificate Validation
↓
Session Key Established
↓
Encrypted HTTP Communication
HTTPS does not automatically provide application authorization.
The API still needs controls such as:
- OAuth 2.0
- JWT validation
- API keys
- mTLS
- Role-based access control
- Attribute-based access control
HTTP Caching
Caching reduces:
- Latency
- Server load
- Network usage
- Database load
Possible cache locations:
Browser Cache
↓
CDN
↓
Reverse Proxy
↓
API Gateway Cache
↓
Application Cache
Cache-Control Header
Example:
Cache-Control: public, max-age=300
Meaning:
- Public caches may store the response
- The response is fresh for 300 seconds
Common directives:
| Directive | Meaning |
|---|---|
| public | Shared caches may store the response |
| private | Only a private client cache should store it |
| no-cache | Cache may store it but must revalidate before use |
| no-store | Do not store the response |
| max-age | Freshness lifetime in seconds |
| s-maxage | Freshness lifetime for shared caches |
| must-revalidate | Stale response must be revalidated |
no-cache does not always mean "do not store."
It usually means the stored response must be revalidated before reuse.
no-store means it should not be stored.
ETag and Conditional Requests
An ETag identifies a specific resource representation.
Initial response:
HTTP/1.1 200 OK
ETag: "customer-v7"
Later request:
GET /api/v1/customers/101
If-None-Match: "customer-v7"
When unchanged:
HTTP/1.1 304 Not Modified
ETag: "customer-v7"
This saves bandwidth because the response body is not returned.
Optimistic Concurrency with ETag
ETags can also prevent lost updates.
Client reads:
GET /api/v1/customers/101
Response:
ETag: "customer-v7"
Client updates:
PUT /api/v1/customers/101
If-Match: "customer-v7"
If another request already changed the resource to version 8, the server returns:
HTTP/1.1 412 Precondition Failed
This prevents the client from overwriting newer data unknowingly.
Compression
Compression reduces response size.
Client:
Accept-Encoding: gzip, br
Server:
Content-Encoding: gzip
Compression is useful for:
- JSON
- HTML
- CSS
- JavaScript
- Text
Compression may be less useful for already compressed formats such as:
- JPEG
- PNG
- ZIP
- MP4
Compression also consumes CPU, so it should be configured thoughtfully.
Transfer-Encoding and Content-Length
Content-Length
Specifies the message-body size.
Content-Length: 256
Chunked Transfer Encoding
Allows a server to send a response in chunks when the full size is not known in advance.
Transfer-Encoding: chunked
HTTP/2 and HTTP/3 use their own framing mechanisms rather than HTTP/1.1 chunked transfer encoding.
Proxies, Load Balancers, and Gateways
A production HTTP request may pass through several intermediaries.
Client
↓
CDN
↓
Load Balancer
↓
API Gateway
↓
Reverse Proxy
↓
Application Service
These components may perform:
- TLS termination
- Routing
- Authentication
- Rate limiting
- Caching
- Compression
- Header transformation
- Logging
- Request-size enforcement
- Timeout management
Forwarded Headers
When requests pass through proxies, the application may need original client information.
Common headers include:
Forwarded: for=203.0.113.10;proto=https;host=api.example.com
Legacy-style headers:
X-Forwarded-For: 203.0.113.10
X-Forwarded-Proto: https
X-Forwarded-Host: api.example.com
Applications should trust forwarded headers only from approved proxies.
Blindly trusting client-supplied forwarded headers can create security and logging problems.
CORS and HTTP
CORS stands for Cross-Origin Resource Sharing.
Browsers enforce same-origin restrictions.
Example origins:
https://app.codewithvenu.com
and:
https://api.codewithvenu.com
These are different origins because the hosts differ.
The browser may send a preflight request:
OPTIONS /api/v1/customers
Origin: https://app.codewithvenu.com
Access-Control-Request-Method: POST
Access-Control-Request-Headers: Authorization, Content-Type
Server response:
HTTP/1.1 204 No Content
Access-Control-Allow-Origin: https://app.codewithvenu.com
Access-Control-Allow-Methods: GET, POST, OPTIONS
Access-Control-Allow-Headers: Authorization, Content-Type
CORS is primarily a browser-enforced policy.
It is not a replacement for authentication or authorization.
Complete Spring Boot HTTP Example
We will create a basic customer API supporting:
- GET
- POST
- PUT
- PATCH
- DELETE
- Correct status codes
- Request validation
- Response headers
- Global error handling
Project Structure
src/main/java/com/codewithvenu/httpfundamentals
├── controller
│ └── CustomerController.java
├── dto
│ ├── CreateCustomerRequest.java
│ ├── UpdateCustomerRequest.java
│ ├── CustomerResponse.java
│ └── ApiErrorResponse.java
├── exception
│ ├── CustomerNotFoundException.java
│ └── GlobalExceptionHandler.java
├── service
│ └── CustomerService.java
└── HttpFundamentalsApplication.java
Customer Response DTO
package com.codewithvenu.httpfundamentals.dto;
public record CustomerResponse(
Long id,
String name,
String email,
String status
) {
}
Create Customer Request
package com.codewithvenu.httpfundamentals.dto;
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;
public record CreateCustomerRequest(
@NotBlank(message = "Name is required")
String name,
@NotBlank(message = "Email is required")
@Email(message = "Email must be valid")
String email
) {
}
Update Customer Request
package com.codewithvenu.httpfundamentals.dto;
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;
public record UpdateCustomerRequest(
@NotBlank(message = "Name is required")
String name,
@NotBlank(message = "Email is required")
@Email(message = "Email must be valid")
String email,
@NotBlank(message = "Status is required")
String status
) {
}
Customer Not Found Exception
package com.codewithvenu.httpfundamentals.exception;
public class CustomerNotFoundException
extends RuntimeException {
public CustomerNotFoundException(Long customerId) {
super(
"Customer "
+ customerId
+ " was not found."
);
}
}
Customer Service
package com.codewithvenu.httpfundamentals.service;
import com.codewithvenu.httpfundamentals.dto.CreateCustomerRequest;
import com.codewithvenu.httpfundamentals.dto.CustomerResponse;
import com.codewithvenu.httpfundamentals.dto.UpdateCustomerRequest;
import com.codewithvenu.httpfundamentals.exception.CustomerNotFoundException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;
import org.springframework.stereotype.Service;
@Service
public class CustomerService {
private final Map<Long, CustomerResponse> customers =
new ConcurrentHashMap<>();
private final AtomicLong customerIdSequence =
new AtomicLong(100);
public CustomerService() {
CustomerResponse initialCustomer =
new CustomerResponse(
101L,
"Venu",
"[email protected]",
"ACTIVE"
);
customers.put(
initialCustomer.id(),
initialCustomer
);
customerIdSequence.set(101);
}
public CustomerResponse getCustomer(
Long customerId
) {
CustomerResponse customer =
customers.get(customerId);
if (customer == null) {
throw new CustomerNotFoundException(
customerId
);
}
return customer;
}
public CustomerResponse createCustomer(
CreateCustomerRequest request
) {
Long customerId =
customerIdSequence.incrementAndGet();
CustomerResponse customer =
new CustomerResponse(
customerId,
request.name(),
request.email(),
"ACTIVE"
);
customers.put(customerId, customer);
return customer;
}
public CustomerResponse replaceCustomer(
Long customerId,
UpdateCustomerRequest request
) {
getCustomer(customerId);
CustomerResponse updatedCustomer =
new CustomerResponse(
customerId,
request.name(),
request.email(),
request.status()
);
customers.put(
customerId,
updatedCustomer
);
return updatedCustomer;
}
public CustomerResponse updateStatus(
Long customerId,
String status
) {
CustomerResponse currentCustomer =
getCustomer(customerId);
CustomerResponse updatedCustomer =
new CustomerResponse(
currentCustomer.id(),
currentCustomer.name(),
currentCustomer.email(),
status
);
customers.put(
customerId,
updatedCustomer
);
return updatedCustomer;
}
public void deleteCustomer(
Long customerId
) {
CustomerResponse removedCustomer =
customers.remove(customerId);
if (removedCustomer == null) {
throw new CustomerNotFoundException(
customerId
);
}
}
}
Customer Controller
package com.codewithvenu.httpfundamentals.controller;
import com.codewithvenu.httpfundamentals.dto.CreateCustomerRequest;
import com.codewithvenu.httpfundamentals.dto.CustomerResponse;
import com.codewithvenu.httpfundamentals.dto.UpdateCustomerRequest;
import com.codewithvenu.httpfundamentals.service.CustomerService;
import jakarta.validation.Valid;
import java.net.URI;
import org.springframework.http.CacheControl;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PatchMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/api/v1/customers")
public class CustomerController {
private final CustomerService customerService;
public CustomerController(
CustomerService customerService
) {
this.customerService = customerService;
}
@GetMapping("/{customerId}")
public ResponseEntity<CustomerResponse>
getCustomer(
@PathVariable Long customerId
) {
CustomerResponse response =
customerService.getCustomer(
customerId
);
return ResponseEntity
.ok()
.cacheControl(
CacheControl
.noCache()
)
.body(response);
}
@PostMapping
public ResponseEntity<CustomerResponse>
createCustomer(
@Valid
@RequestBody
CreateCustomerRequest request
) {
CustomerResponse response =
customerService.createCustomer(
request
);
URI resourceLocation =
URI.create(
"/api/v1/customers/"
+ response.id()
);
return ResponseEntity
.created(resourceLocation)
.body(response);
}
@PutMapping("/{customerId}")
public ResponseEntity<CustomerResponse>
replaceCustomer(
@PathVariable Long customerId,
@Valid
@RequestBody
UpdateCustomerRequest request
) {
CustomerResponse response =
customerService.replaceCustomer(
customerId,
request
);
return ResponseEntity.ok(response);
}
@PatchMapping("/{customerId}/status")
public ResponseEntity<CustomerResponse>
updateCustomerStatus(
@PathVariable Long customerId,
@RequestParam String status
) {
CustomerResponse response =
customerService.updateStatus(
customerId,
status
);
return ResponseEntity.ok(response);
}
@DeleteMapping("/{customerId}")
public ResponseEntity<Void>
deleteCustomer(
@PathVariable Long customerId
) {
customerService.deleteCustomer(
customerId
);
return ResponseEntity
.noContent()
.build();
}
}
Error Response DTO
package com.codewithvenu.httpfundamentals.dto;
import java.time.Instant;
import java.util.List;
public record ApiErrorResponse(
Instant timestamp,
int status,
String code,
String message,
String path,
List<String> validationErrors
) {
}
Global Exception Handler
package com.codewithvenu.httpfundamentals.exception;
import com.codewithvenu.httpfundamentals.dto.ApiErrorResponse;
import jakarta.servlet.http.HttpServletRequest;
import java.time.Instant;
import java.util.List;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(CustomerNotFoundException.class)
public ResponseEntity<ApiErrorResponse>
handleCustomerNotFound(
CustomerNotFoundException exception,
HttpServletRequest request
) {
ApiErrorResponse errorResponse =
new ApiErrorResponse(
Instant.now(),
HttpStatus.NOT_FOUND.value(),
"CUSTOMER_NOT_FOUND",
exception.getMessage(),
request.getRequestURI(),
List.of()
);
return ResponseEntity
.status(HttpStatus.NOT_FOUND)
.body(errorResponse);
}
@ExceptionHandler(
MethodArgumentNotValidException.class
)
public ResponseEntity<ApiErrorResponse>
handleValidationFailure(
MethodArgumentNotValidException exception,
HttpServletRequest request
) {
List<String> validationErrors =
exception
.getBindingResult()
.getFieldErrors()
.stream()
.map(
fieldError ->
fieldError.getField()
+ ": "
+ fieldError
.getDefaultMessage()
)
.toList();
ApiErrorResponse errorResponse =
new ApiErrorResponse(
Instant.now(),
HttpStatus.BAD_REQUEST.value(),
"VALIDATION_FAILED",
"Request validation failed.",
request.getRequestURI(),
validationErrors
);
return ResponseEntity
.badRequest()
.body(errorResponse);
}
}
Step-by-Step Execution: GET Request
Client request:
GET /api/v1/customers/101 HTTP/1.1
Host: localhost:8080
Accept: application/json
Step 1: HTTP Server Receives the Request
The embedded application server accepts the network connection and parses the HTTP request.
Step 2: DispatcherServlet Receives It
Spring MVC routes the request through:
DispatcherServlet
↓
Handler Mapping
↓
CustomerController
Step 3: Endpoint Is Selected
Spring matches:
@GetMapping("/{customerId}")
with:
customerId = 101
Step 4: Service Retrieves the Customer
customerService.getCustomer(customerId);
Step 5: Controller Builds the HTTP Response
return ResponseEntity
.ok()
.cacheControl(CacheControl.noCache())
.body(response);
The response includes:
HTTP/1.1 200 OK
Content-Type: application/json
Cache-Control: no-cache
Step 6: Jackson Serializes the DTO
Java object:
new CustomerResponse(
101L,
"Venu",
"[email protected]",
"ACTIVE"
)
becomes:
{
"id": 101,
"name": "Venu",
"email": "[email protected]",
"status": "ACTIVE"
}
Step-by-Step Execution: POST Request
Request:
POST /api/v1/customers HTTP/1.1
Content-Type: application/json
Accept: application/json
{
"name": "John",
"email": "[email protected]"
}
Step 1: Content-Type Is Evaluated
Spring sees:
Content-Type: application/json
and selects a JSON message converter.
Step 2: JSON Is Deserialized
The request body becomes:
CreateCustomerRequest
Step 3: Validation Runs
Annotations such as:
@NotBlank
@Email
are evaluated because the controller uses:
@Valid
Step 4: Service Creates the Resource
The service generates a new ID and stores the customer.
Step 5: Controller Returns 201
ResponseEntity.created(resourceLocation)
produces:
HTTP/1.1 201 Created
Location: /api/v1/customers/102
Request and Response Examples
GET Customer
Request:
GET /api/v1/customers/101
Accept: application/json
Response:
HTTP/1.1 200 OK
Content-Type: application/json
Cache-Control: no-cache
{
"id": 101,
"name": "Venu",
"email": "[email protected]",
"status": "ACTIVE"
}
Create Customer
Request:
POST /api/v1/customers
Content-Type: application/json
Accept: application/json
{
"name": "John",
"email": "[email protected]"
}
Response:
HTTP/1.1 201 Created
Location: /api/v1/customers/102
Content-Type: application/json
{
"id": 102,
"name": "John",
"email": "[email protected]",
"status": "ACTIVE"
}
Replace Customer
Request:
PUT /api/v1/customers/102
Content-Type: application/json
{
"name": "John Smith",
"email": "[email protected]",
"status": "ACTIVE"
}
Response:
HTTP/1.1 200 OK
Partial Update
Request:
PATCH /api/v1/customers/102/status?status=INACTIVE
Response:
HTTP/1.1 200 OK
{
"id": 102,
"name": "John Smith",
"email": "[email protected]",
"status": "INACTIVE"
}
Delete Customer
Request:
DELETE /api/v1/customers/102
Response:
HTTP/1.1 204 No Content
Validation Failure
Request:
POST /api/v1/customers
Content-Type: application/json
{
"name": "",
"email": "invalid-email"
}
Response:
HTTP/1.1 400 Bad Request
Content-Type: application/json
{
"timestamp": "2026-07-19T12:00:00Z",
"status": 400,
"code": "VALIDATION_FAILED",
"message": "Request validation failed.",
"path": "/api/v1/customers",
"validationErrors": [
"name: Name is required",
"email: Email must be valid"
]
}
HTTP Logging
A production API should log meaningful HTTP metadata.
Example:
timestamp=2026-07-19T12:30:00Z
level=INFO
service=customer-api
traceId=21ab9d48
method=GET
path=/api/v1/customers/101
status=200
durationMs=73
clientId=mobile-app
Do not log:
- Passwords
- Access tokens
- Refresh tokens
- Sensitive request bodies
- Full payment-card data
- Private keys
- Session secrets
Important HTTP Metrics
Monitor:
- Request count
- Requests per second
- Status-code distribution
- P50 latency
- P95 latency
- P99 latency
- Timeout count
- Active connections
- Connection pool usage
- Request size
- Response size
- Retry count
- Rate-limited requests
- Downstream HTTP errors
Production Scenario 1: API Returns 504
Flow:
Client
↓
API Gateway
↓
Order Service
↓
Payment Service
The gateway waits five seconds, but the order service does not respond.
Result:
HTTP/1.1 504 Gateway Timeout
Investigation:
- Check gateway timeout configuration.
- Inspect distributed traces.
- Check order-service latency.
- Check payment-service latency.
- Review connection pools.
- Review database queries.
- Check thread-pool saturation.
- Confirm whether the client retried.
- Verify that retries did not multiply traffic.
Production Scenario 2: API Returns 502
The gateway successfully connects to a backend but receives:
- Invalid protocol response
- Prematurely closed connection
- TLS handshake failure
- Malformed response
- Unhealthy upstream behavior
Result:
HTTP/1.1 502 Bad Gateway
Check:
- Upstream health
- TLS certificates
- Proxy configuration
- Backend startup logs
- Protocol compatibility
- Response-header size
- Connection resets
Production Scenario 3: API Returns 415
Request:
POST /api/v1/customers
Content-Type: application/xml
The endpoint accepts only:
application/json
Response:
HTTP/1.1 415 Unsupported Media Type
Fix:
Content-Type: application/json
Production Scenario 4: API Returns 406
Client sends:
Accept: application/xml
The server can produce only JSON.
Response:
HTTP/1.1 406 Not Acceptable
Fix:
Accept: application/json
Production Scenario 5: Duplicate Payment
Client Sends POST
↓
Payment Processed
↓
Response Lost
↓
Client Retries
↓
Second Payment Created
Cause:
POST is not idempotent by default.
Solution:
Idempotency-Key: PAYMENT-10001
The server should store the key and previously generated result.
Production Scenario 6: Stale Update Overwrites New Data
Two users read version 5 of a profile.
User A updates it to version 6.
User B later submits an update based on version 5.
Without concurrency protection, User B may overwrite User A's changes.
Solution:
If-Match: "profile-v5"
Server response:
HTTP/1.1 412 Precondition Failed
Common HTTP Mistakes
1. Using GET to Modify Data
Bad:
GET /accounts/101/close
Better:
POST /accounts/101/closure-requests
or:
PATCH /accounts/101
depending on business semantics.
2. Returning 200 for Every Result
Bad:
HTTP/1.1 200 OK
{
"success": false,
"error": "Customer not found"
}
Better:
HTTP/1.1 404 Not Found
3. Returning 500 for Validation Errors
Validation failures are normally client errors.
Use:
400 Bad Request
or a documented 422 convention.
4. Confusing 401 and 403
Use:
401 → Missing or invalid authentication
403 → Authenticated but not authorized
5. Ignoring Content-Type
The server should validate request formats.
A JSON endpoint should not silently accept unsupported data.
6. Missing Timeouts
An HTTP client without timeouts can exhaust:
- Threads
- Connections
- Memory
- Request capacity
7. Retrying Every Failure
Do not automatically retry:
- Validation errors
- Authentication failures
- Authorization failures
- Non-idempotent operations without protection
8. Caching Sensitive Data Publicly
Bad:
Cache-Control: public
for personal account data.
Better:
Cache-Control: no-store
or a carefully designed private-cache policy.
9. Logging Authorization Headers
Never log bearer tokens.
Mask or remove sensitive headers.
10. Returning Bodies with 204
A 204 No Content response should not include a response body.
11. Exposing Internal Failure Details
Bad response:
{
"message": "NullPointerException at CustomerService.java:84"
}
Better:
{
"code": "INTERNAL_ERROR",
"message": "The request could not be processed."
}
12. Trusting Client-Supplied Forwarded Headers
Only trust forwarding metadata from approved proxies and gateways.
HTTP Best Practices
- Use HTTP methods according to their semantics.
- Return accurate status codes.
- Validate
Content-TypeandAccept. - Use HTTPS for all production APIs.
- Configure explicit client and gateway timeouts.
- Retry only safe or idempotent operations.
- Use idempotency keys for critical POST requests.
- Add request correlation and trace identifiers.
- Avoid logging sensitive headers and bodies.
- Use caching only when the data and security model allow it.
- Use ETags for conditional retrieval and concurrency control.
- Compress large text responses.
- Reuse connections through connection pools.
- Publish supported media types.
- Return standardized error responses.
- Monitor latency, errors, traffic, and saturation.
- Keep proxy and application timeout values coordinated.
- Limit request-body and header sizes.
- Apply rate limits to protect services.
- Document HTTP behavior in OpenAPI.
Frequently Asked Interview Questions
1. What is HTTP?
HTTP is an application-layer request-response protocol used by clients and servers to exchange resources and data over a network.
A client sends an HTTP request containing a method, target, headers, and an optional body. The server returns a response containing a status code, headers, and an optional body.
2. What is the difference between HTTP and HTTPS?
HTTP transfers application messages without TLS protection.
HTTPS is HTTP over TLS and provides:
- Encryption
- Server authentication
- Message integrity
HTTPS protects data in transit but does not replace application authentication or authorization.
3. What are the main parts of an HTTP request?
The main parts are:
Request Line
Headers
Empty Line
Optional Body
The request line contains the HTTP method, request target, and protocol version.
4. What are the main parts of an HTTP response?
The main parts are:
Status Line
Headers
Empty Line
Optional Body
The status line contains the protocol version, status code, and reason phrase.
5. What is the difference between GET and POST?
GET retrieves a resource and should not change server state. It is safe and idempotent and is commonly cacheable.
POST submits data for processing or creates a child resource. It is not safe and is not idempotent by default.
6. What is the difference between PUT and PATCH?
PUT usually replaces the complete resource representation and is expected to be idempotent.
PATCH applies a partial modification. Its idempotency depends on the operation.
Example:
{
"status": "ACTIVE"
}
can be idempotent.
{
"incrementBalanceBy": 100
}
is not idempotent.
7. What is the difference between safe and idempotent HTTP methods?
A safe method should not change server state.
An idempotent method may change state, but repeated identical requests should leave the server in the same final state as one request.
GET is safe and idempotent.
PUT is not safe, but it is idempotent.
8. Why is DELETE considered idempotent if the second request can return 404?
Idempotency concerns the final server state, not identical response bodies.
After the first DELETE, the resource does not exist.
After repeated DELETE requests, the resource still does not exist.
Therefore, the final state is unchanged.
9. What is the difference between 401 and 403?
401 Unauthorized normally means the request lacks valid authentication.
403 Forbidden means authentication succeeded, but the caller does not have sufficient permission.
10. What is the difference between 502, 503, and 504?
502 Bad Gateway means a proxy received an invalid response from an upstream service.
503 Service Unavailable means a service is temporarily unavailable or overloaded.
504 Gateway Timeout means a proxy did not receive an upstream response before its timeout expired.
11. What is the difference between Content-Type and Accept?
Content-Type describes the format of the current request or response body.
Accept tells the server which response formats the client can handle or prefers.
Example:
Content-Type: application/json
Accept: application/json
The request body is JSON, and the client wants a JSON response.
12. What does stateless HTTP mean?
Stateless HTTP means the protocol does not automatically retain conversational state between requests.
Each request should contain enough context to be processed independently.
Applications may still maintain state through sessions, cookies, databases, caches, or tokens.
13. What are the main differences among HTTP/1.1, HTTP/2, and HTTP/3?
HTTP/1.1 commonly processes requests over persistent TCP connections but has limited multiplexing behavior.
HTTP/2 uses binary framing, header compression, and multiplexed streams over TCP.
HTTP/3 uses QUIC over UDP and improves connection establishment, stream independence, and network migration behavior.
14. How does HTTP caching work with ETags?
The server sends an ETag with a resource representation.
The client later sends that ETag using If-None-Match.
When the representation has not changed, the server returns 304 Not Modified without the response body.
ETags can also support optimistic concurrency through If-Match.
15. What HTTP considerations are important in production APIs?
Important production concerns include:
- HTTPS
- Correct status codes
- Request validation
- Timeouts
- Connection pooling
- Safe retries
- Idempotency
- Rate limiting
- Caching
- Compression
- Correlation IDs
- Distributed tracing
- Standardized errors
- Request-size limits
- Monitoring latency and failures
A senior engineer should consider HTTP behavior across the client, gateway, service, downstream dependencies, and network—not only inside the controller.
Senior-Level Interview Answer
A strong answer could be:
HTTP is an application-layer request-response protocol used by APIs and web applications. A request contains a method, target, headers, and an optional body, while a response contains a status code, headers, and an optional body. In production API design, I use methods according to their semantics, return accurate status codes, secure traffic with HTTPS, configure connection and read timeouts, retry only idempotent operations, use idempotency keys for critical POST requests, and apply caching through Cache-Control and ETags where appropriate. I also consider proxy behavior, gateway timeouts, content negotiation, compression, rate limiting, distributed tracing, and connection pooling.
Quick Revision
| Topic | Summary |
|---|---|
| HTTP | Application-layer request-response protocol |
| Request | Method, target, headers, optional body |
| Response | Status, headers, optional body |
| Safe methods | GET, HEAD, OPTIONS, TRACE |
| Common idempotent methods | GET, HEAD, PUT, DELETE, OPTIONS, TRACE |
| Create resource | POST with 201 Created |
| Async processing | 202 Accepted |
| Successful deletion | 204 No Content |
| Authentication failure | 401 |
| Authorization failure | 403 |
| Resource missing | 404 |
| Conflict | 409 |
| Rate limit | 429 |
| Upstream invalid response | 502 |
| Service unavailable | 503 |
| Gateway timeout | 504 |
| Secure transport | HTTPS |
| Conditional caching | ETag and If-None-Match |
| Concurrency protection | ETag and If-Match |
| HTTP/2 | Multiplexed streams over TCP |
| HTTP/3 | QUIC over UDP |
| Interview Frequency | ⭐⭐⭐⭐⭐ |
Key Takeaways
- HTTP is the foundation of REST API communication.
- Every HTTP exchange consists of a request and response.
- Methods communicate intent and have safety and idempotency semantics.
- Status codes should accurately describe the result.
Content-Typedescribes the current body, whileAcceptdescribes the desired response format.- HTTP is stateless, but applications may maintain state using sessions, cookies, tokens, and databases.
- HTTP/1.1 uses persistent TCP connections.
- HTTP/2 adds binary framing, header compression, and multiplexing over TCP.
- HTTP/3 uses QUIC over UDP.
- HTTPS protects data in transit through TLS.
- Explicit timeouts and connection pooling are essential in production.
- Retries must account for idempotency and duplicate processing.
- Caching through
Cache-Control, ETags, and conditional requests can reduce latency and server load. - Correct handling of 401, 403, 429, 502, 503, and 504 is important for production troubleshooting.
- HTTP knowledge helps developers design APIs that are secure, scalable, observable, reliable, and easy to consume.