HTTP 400 Bad Request

HTTP 400 Bad Request indicates the server cannot process the request due to something perceived as a client error — malformed request syntax, invalid request message framing, or deceptive request routing. This is the generic client error response when no more specific 4xx code applies. In API design, 400 should include a descriptive error body explaining exactly what was wrong so the client can fix the request.

Debug HTTP 400 live
Analyze real 400 behavior — headers, caching, CORS, redirects
Open Inspector →

Try it (live endpoint)

Response includes the status code, standard headers (including Content-Type), and a small diagnostic JSON body describing the request and returned status.

Simulator URL (copy in the app after load — not a normal link):

https://httpstatus.com/api/status/400

Example request:

curl -i "https://httpstatus.com/api/status/400"
Try in playground

Meaning

The server cannot or will not process the request due to an apparent client error.

What it guarantees
  • The request was not fulfilled due to a client-side issue.
What it does NOT guarantee
  • Retries will succeed without changing request inputs.
  • The server is healthy; it may still be failing for other reasons.

When to use this status

  • Validation fails (missing fields, invalid types, invalid params).
  • A required header is missing or malformed (Content-Type, Authorization).
  • The client is not allowed to perform the operation.

When NOT to use this status (common misuses)

Using 400 for authentication/authorization failures.
Clients cannot distinguish validation vs auth; retry/login flows break.
Using 404 to mask permission issues everywhere.
Monitoring misclassifies access bugs; SEO can degrade if real pages appear missing.
Returning 4xx for server-side bugs.
Clients stop retrying; incidents are masked as client behavior.

Critical headers that matter

Content-Type
Defines error body format (JSON/text/problem+json).
Clients can’t parse structured errors; observability loses fidelity.
Cache-Control
Prevents caching transient errors unless intended.
CDNs cache failures; prolonged user-visible outages.

Tool interpretation

Browsers
Displays an error state; devtools exposes status and headers. Cache headers can accidentally cache error documents.
API clients
Classifies as failure; retry policy depends on idempotency and code class. Structured errors improve handling.
Crawlers / SEO tools
Persistent failures reduce crawl rate; soft-404 patterns cause indexing instability.
Uptime monitors
Typically alerts based on rate/threshold. Consistent classification reduces false positives.
CDNs / reverse proxies
May cache errors if misconfigured; respects Cache-Control and can serve stale on origin failure.

Inspector preview (read-only)

On this code, Inspector focuses on semantics, headers, and correctness warnings that commonly affect clients and caches.

Signals it will highlight
  • Status semantics vs method and body expectations
  • Header sanity (Content-Type, Cache-Control, Vary) and evidence completeness
  • Error cacheability and retry guidance signals
Correctness warnings
No common correctness warnings are specific to this code.

Guided Lab outcome

  • Reproduce HTTP 400 Bad Request using a controlled endpoint and capture the full exchange.
  • Practice distinguishing status semantics from transport issues (redirects, caching, proxies).
  • Identify the minimum request changes required to move from client error to success.

Technical deep dive

400 Bad Request (RFC 7231 Section 6.5.1) is the catch-all for client-side errors that don't fit a more specific 4xx code. Servers return 400 for: malformed JSON/XML bodies, invalid query parameters, missing required fields, type mismatches (string where number expected), request headers exceeding limits, and invalid URL encoding. Key design principle: always include machine-readable error details. A bare 400 without explanation forces developers to guess what's wrong. Best practices: use a consistent error schema like RFC 7807 Problem Details ({ type, title, status, detail, instance }) across all 4xx responses. 400 is not cacheable.

Real-world examples

Invalid JSON body
POST /api/users with { "name": "John", "email": } (malformed JSON). The server returns 400 with { error: 'INVALID_JSON', message: 'Unexpected token } at position 32', field: null }. The error pinpoints the exact parse failure.
Missing required field
POST /api/orders with { "quantity": 5 } but no 'product_id'. The server returns 400 with { error: 'VALIDATION_ERROR', details: [{ field: 'product_id', message: 'Required field missing' }] }. The structured error lets the client highlight the specific field.
Type mismatch in query parameter
GET /api/users?page=abc. The server expects an integer but receives a string. Returns 400 with { error: 'INVALID_PARAMETER', field: 'page', message: 'Expected integer, got string', received: 'abc' }.

Framework behavior

Express.js (Node)
Express: app.use(express.json()) automatically returns 400 for malformed JSON bodies. For custom validation: if (!req.body.email) return res.status(400).json({ error: 'email is required' }). Use Joi or Zod for schema validation.
Django / DRF (Python)
Django REST Framework: serializer.is_valid(raise_exception=True) automatically returns 400 with field-level errors. DRF formats errors as { field_name: ['Error message'] }.
Spring Boot (Java)
Spring: @Valid on @RequestBody triggers MethodArgumentNotValidException → 400. Configure @ControllerAdvice to format validation errors. Spring returns 400 automatically for malformed JSON via HttpMessageNotReadableException.
FastAPI (Python)
FastAPI: Pydantic model validation automatically returns 422 (not 400) for validation errors. Override with custom exception handler to return 400 instead. FastAPI returns 400 for malformed JSON bodies.

Debugging guide

  1. Read the response body — a well-designed API will tell you exactly what's wrong
  2. Check Content-Type header matches the body format (sending JSON without application/json header)
  3. Validate your JSON with a linter before sending — trailing commas and single quotes are invalid JSON
  4. Check for invisible characters (BOM, zero-width spaces) in request bodies copied from documents
  5. Compare your request against the API documentation — check required fields, types, and value constraints

Code snippets

Node.js
// Structured error responses with RFC 7807
app.post('/api/users', (req, res) => {
  const errors = [];
  if (!req.body.email) errors.push({ field: 'email', message: 'Required' });
  if (!req.body.name) errors.push({ field: 'name', message: 'Required' });
  if (req.body.email && !isValidEmail(req.body.email))
    errors.push({ field: 'email', message: 'Invalid email format' });

  if (errors.length > 0) {
    return res.status(400).json({
      type: 'https://api.example.com/errors/validation',
      title: 'Validation Error',
      status: 400,
      detail: `${errors.length} validation error(s)`,
      errors
    });
  }
  // proceed...
});
Python
from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse
from pydantic import BaseModel, EmailStr, ValidationError

class CreateUser(BaseModel):
    name: str
    email: EmailStr

@app.exception_handler(ValidationError)
async def validation_handler(request: Request, exc: ValidationError):
    return JSONResponse(status_code=400, content={
        'type': 'validation_error',
        'title': 'Bad Request',
        'status': 400,
        'errors': [{'field': e['loc'][-1], 'message': e['msg']}
                   for e in exc.errors()]
    })
Java (Spring)
@ControllerAdvice
public class ErrorHandler {
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResponseEntity<Map<String, Object>> handleValidation(
            MethodArgumentNotValidException ex) {
        List<Map<String, String>> errors = ex.getBindingResult()
            .getFieldErrors().stream()
            .map(e -> Map.of("field", e.getField(),
                           "message", e.getDefaultMessage()))
            .toList();
        return ResponseEntity.badRequest().body(Map.of(
            "type", "validation_error",
            "title", "Bad Request",
            "status", 400,
            "errors", errors));
    }
}
Go
type ValidationError struct {
	Field   string `json:"field"`
	Message string `json:"message"`
}

func createUser(w http.ResponseWriter, r *http.Request) {
	var req CreateUserRequest
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		writeError(w, 400, "Invalid JSON", err.Error())
		return
	}
	var errs []ValidationError
	if req.Email == "" {
		errs = append(errs, ValidationError{"email", "Required"})
	}
	if len(errs) > 0 {
		w.WriteHeader(400)
		json.NewEncoder(w).Encode(map[string]any{
			"status": 400, "errors": errs})
		return
	}
}

FAQ

Should I use 400 or 422 for validation errors?
Both are valid. 400 means 'the request is malformed at the HTTP level' (bad JSON, wrong Content-Type). 422 means 'the request is well-formed but semantically invalid' (valid JSON with invalid field values). In practice, many APIs use 400 for all validation errors for simplicity. FastAPI uses 422 by default, following the OpenAPI convention.
How should 400 error responses be structured?
Use a consistent error schema across your API. RFC 7807 Problem Details is the standard: { type (URL), title, status, detail, instance }. For validation errors, add a field-level errors array. Always include enough detail for the client to fix the request without guessing.
Can a 400 error be caused by server misconfiguration?
Sometimes what looks like a client error is actually a server issue. Examples: a proxy stripping Content-Type headers, a WAF rejecting valid requests, SSL/TLS issues corrupting the request body, or server-side URL decoding mishandling special characters. If clients consistently get 400 on valid requests, investigate the server path.
Should 400 responses be logged as errors on the server?
Log them at INFO or WARN level, not ERROR. 400s are expected in normal operation — clients will send invalid requests. Logging at ERROR level creates alert fatigue. Do monitor for spikes in 400s, which may indicate a broken client deployment or an API change that broke backward compatibility.

Client expectation contract

Client can assume
  • The request failed due to client-side inputs or policy.
Client must NOT assume
  • Retries without changes will succeed.
Retry behavior
Do not retry until the request is corrected (or credentials refreshed).
Monitoring classification
Client error
Use payload and header checks to avoid false positives; cacheability depends on Cache-Control/ETag/Vary.

Related status codes

401 Unauthorized
Authentication is required and has failed or has not yet been provided. Response must include WWW-Authenticate header.
308 Permanent Redirect
The URL of the requested resource has been changed permanently.

Explore more

Related guides
Related tools
Related utilities