400 Bad Request
What is HTTP 400 Bad Request?
When you visit a website, your computer sends a 'request' asking for something -...
Explain Like I’m 3
You asked for something, but you said it in a way that doesn’t make sense! It’s like asking for ‘blurple shmoop’ - nobody knows what that means! You need to ask in the right way so people can understand you.
Example: You ask for a cookie, but you say ‘Gimme blerp!’ Nobody knows what ‘blerp’ is, so they can’t give you anything. You need to say ‘cookie’ the right way!
Explain Like I’m 5
When you visit a website, your computer sends a ‘request’ asking for something - like a webpage, picture, or information. Sometimes your computer sends this request with mistakes in it - maybe it uses words the website doesn’t understand, or forgets important information, or says things in the wrong order. When this happens, the website sends back a ‘400 Bad Request’ message saying ‘I can’t understand what you’re asking for because your request doesn’t make sense!’ It’s like writing a letter with lots of spelling mistakes and missing words - the person who receives it can’t understand what you want!
Example: You try to search for puppy videos, but your computer accidentally sends a request with gibberish like ‘{{{name::puppy???}}}’ instead of proper text. The website responds ‘400 Bad Request’ because it can’t understand the gibberish. You need to send the request correctly!
Jr. Developer
HTTP 400 Bad Request indicates the server cannot or will not process the request due to something perceived as a client error. Common causes: malformed JSON (missing quotes, trailing commas), invalid URL characters, missing required headers (Content-Type, Authorization), request body too large, or incorrect HTTP method. The key distinction: 400 is for syntactic errors (the request itself is malformed), while 422 Unprocessable Entity is for semantic errors (valid syntax, but logically invalid data). Best practice: return 400 for parse errors and structural issues; use 422 for validation errors. Include a descriptive error message in the response body to help clients fix the issue. Clients should not retry 400 errors without fixing the request. Always validate user input on both client and server to prevent 400 errors from reaching your API.
Example: Client POSTs to /api/users with JSON: {name: ‘Alice’} (missing quotes around ‘name’). Server can’t parse it and returns 400 Bad Request with error: ‘Invalid JSON syntax: Expected property name or }”. Client fixes to {“name”: “Alice”} and retry succeeds with 201 Created.
Code Example
// Express.js handling 400 Bad Requestconst express = require('express');const app = express();
// Parse JSON with error handlingapp.use(express.json());
// JSON parse error middlewareapp.use((err, req, res, next) => { if (err instanceof SyntaxError && err.status === 400 && 'body' in err) { // Malformed JSON return res.status(400).json({ error: 'Bad Request', message: 'Invalid JSON syntax', details: err.message }); } next();});
app.post('/api/users', (req, res) => { const { name, email } = req.body;
// Structural validation - return 400 if (!name || !email) { return res.status(400).json({ error: 'Bad Request', message: 'Missing required fields', required: ['name', 'email'] }); }
// Semantic validation - return 422 (not 400) if (!email.includes('@')) { return res.status(422).json({ error: 'Unprocessable Entity', message: 'Email format invalid' }); }
res.status(201).json({ id: 1, name, email });});Crash Course
400 Bad Request, defined in RFC 9110 Section 15.5.1 (obsoleting RFC 7231), indicates the server cannot or will not process the request due to something perceived as a client error - malformed request syntax, invalid request message framing, or deceptive request routing. Per RFC 9110, the client SHOULD NOT repeat the request without modification. Key causes: JSON/XML parse errors (malformed syntax), missing or invalid Content-Type headers, illegal URL characters (unencoded spaces, special characters), missing required parameters, request payload exceeding size limits, malformed cookies or authentication tokens. Distinction from similar codes: 400 for syntactic/structural errors (can’t parse request), 422 for semantic errors (parsed successfully but data invalid), 401 for missing/invalid authentication, 403 for insufficient permissions. Best practices: provide detailed error messages in response body with specific field errors, use 400 for structural issues, 422 for validation failures, log 400 errors to identify client SDK bugs or API documentation issues, implement request validation middleware early in request pipeline. Security note: don’t expose internal server details in 400 errors - be helpful but not verbose about system internals.
Example: E-commerce API receives POST to /api/checkout with payload: {items: [{id: 1, qty: ‘two’}]}. Syntax valid (parseable JSON) but qty should be number not string. Decision: Use 422 for this semantic error, not 400. If payload was {items: [{id: 1, qty: 2}] (missing closing brace), that’s 400 Bad Request - can’t parse at all.
Code Example
// Comprehensive 400 Bad Request handlingconst express = require('express');const { validationResult, body } = require('express-validator');const app = express();
// Custom JSON parser with better errorsapp.use(express.json({ verify: (req, res, buf, encoding) => { try { JSON.parse(buf); } catch (err) { res.status(400).json({ error: 'Bad Request', message: 'Malformed JSON', details: err.message, position: err.message.match(/position (\d+)/)?.[1] }); throw err; // Prevent further processing } }}));
// Validate URL parametersapp.get('/api/products/:id', (req, res) => { const id = parseInt(req.params.id, 10);
// Structural validation - must be valid number if (isNaN(id) || id <= 0) { return res.status(400).json({ error: 'Bad Request', message: 'Invalid product ID format', expected: 'Positive integer', received: req.params.id }); }
res.json({ id, name: 'Product' });});
// Validate required headersapp.post('/api/upload', (req, res) => { const contentType = req.headers['content-type'];
if (!contentType || !contentType.startsWith('multipart/form-data')) { return res.status(400).json({ error: 'Bad Request', message: 'Invalid Content-Type header', expected: 'multipart/form-data', received: contentType || 'none' }); }
// Process upload... res.status(201).json({ uploaded: true });});
// Validate request body structureapp.post('/api/orders', [ body('items').isArray({ min: 1 }).withMessage('items must be non-empty array'), body('items.*.id').isInt().withMessage('item id must be integer'), body('items.*.quantity').isInt({ min: 1 }).withMessage('quantity must be positive integer') ], (req, res) => { const errors = validationResult(req);
if (!errors.isEmpty()) { // Structural validation failed return res.status(400).json({ error: 'Bad Request', message: 'Request validation failed', errors: errors.array() }); }
res.status(201).json({ orderId: 123 }); });
// Handle URL length limitapp.use((req, res, next) => { if (req.url.length > 2048) { return res.status(414).json({ error: 'URI Too Long', message: 'Request URL exceeds maximum length', maxLength: 2048, actualLength: req.url.length }); } next();});Deep Dive
The 400 Bad Request status code, specified in RFC 9110 Section 15.5.1, indicates the server cannot or will not process the request due to something that is perceived to be a client error - malformed request syntax, invalid request message framing, or deceptive request routing. Per RFC 9110, the client SHOULD NOT repeat the request without modification. This is the generic client error status code for cases where no more specific 4xx code applies. The server generating a 400 response SHOULD send a representation containing an explanation of the error situation and whether it is a temporary or permanent condition.
Technical Details
RFC 9110 categorizes client errors broadly: malformed request syntax encompasses JSON/XML parse errors, invalid HTTP message structure (malformed chunked encoding, incorrect Content-Length), and protocol violations (HTTP/1.0 request with Transfer-Encoding). Invalid request message framing includes requests that violate HTTP message format rules per RFC 9112 Section 2 - missing required headers for certain methods (Content-Length for POST/PUT), conflicting headers (both Content-Length and Transfer-Encoding: chunked), or request line exceeding implementation limits. Deceptive request routing covers Host header mismatches in HTTP/2 requests or attempts to route requests through incorrect servers.\n\nDistinction from related status codes follows semantic boundaries: 400 is the generic catch-all for client errors when no more specific code applies. Use 400 for structural/syntactic errors that prevent request parsing. 422 Unprocessable Entity (RFC 4918, WebDAV) for semantic errors - request parses successfully but data validation fails. Example: POST with valid JSON structure but email field containing ‘not-an-email’. 401 Unauthorized for missing or invalid authentication credentials. 403 Forbidden for authenticated requests lacking required permissions. 413 Content Too Large for request payloads exceeding size limits. 414 URI Too Long for request URIs exceeding length limits. 415 Unsupported Media Type for Content-Type mismatches. 429 Too Many Requests for rate limit violations.\n\nCommon causes in production systems: JSON parse errors are the most frequent, caused by trailing commas (valid in JavaScript, invalid in JSON), single quotes instead of double quotes, unescaped special characters, or UTF-8 encoding issues. URL encoding violations include spaces not encoded as %20, reserved characters (?, &, #) unescaped in path segments, non-ASCII characters without percent-encoding, or double-encoded parameters. Header validation failures occur with missing Content-Type for POST/PUT, malformed Authorization headers (incorrect Bearer token format), oversized header values (>8KB typically triggers 400 or 431), or duplicate headers where single value expected. Form data issues include missing boundary in multipart/form-data, malformed URL-encoded data (missing = or &), or nested structures in application/x-www-form-urlencoded exceeding depth limits.\n\nSecurity implications: 400 responses can leak implementation details if error messages are too verbose. Don’t expose stack traces, internal paths, database queries, or library versions in 400 error responses. Rate limiting: excessive 400 errors from single client might indicate attack attempts (fuzzing, injection testing). Implement 400-based rate limiting separate from overall rate limits. OWASP recommends limiting to 10-20 malformed requests per minute per IP before blocking. Information disclosure via differential responses: varying 400 error messages based on internal state can leak information. Example: ”…
Code Example
// Production-grade 400 Bad Request handling\nconst express = require('express');\nconst Joi = require('joi');\nconst { v4: uuidv4 } = require('uuid');\nconst app = express();\n\n// Request ID middleware for tracing\napp.use((req, res, next) => {\n req.id = uuidv4();\n res.setHeader('X-Request-ID', req.id);\n next();\n});\n\n// Custom JSON parser with detailed error reporting\napp.use(express.json({\n limit: '1mb',\n verify: (req, res, buf, encoding) => {\n if (buf.length === 0) return; // Empty body OK for some methods\n \n try {\n JSON.parse(buf);\n } catch (err) {\n const position = err.message.match(/position (\\d+)/)?.[1];\n const context = position ? buf.toString().substring(\n Math.max(0, parseInt(position) - 20),\n Math.min(buf.length, parseInt(position) + 20)\n ) : null;\n \n // Log malformed JSON attempts\n console.error('JSON parse error:', {\n requestId: req.id,\n error: err.message,\n ip: req.ip,\n path: req.path\n });\n \n // Return 400 with helpful error\n return res.status(400).json({\n error: 'bad_request',\n code: 'INVALID_JSON',\n message: 'Request body contains malformed JSON',\n details: err.message,\n context: context,\n request_id: req.id,\n documentation: 'https://docs.api.example.com/errors/invalid-json'\n });\n }\n }\n}));\n\n// Schema validation middleware factory\nfunction validateSchema(schema) {\n return (req, res, next) => {\n const { error, value } = schema.validate(req.body, {\n abortEarly: false,\n stripUnknown: true\n });\n \n if (error) {\n const errors = error.details.map(detail => ({\n field: detail.path.join('.'),\n code: detail.type,\n message: detail.message\n }));\n \n return res.status(400).json({\n error: 'bad_request',\n code: 'VALIDATION_FAILED',\n message: 'Request validation failed',\n errors,\n request_id: req.id\n });\n }\n \n req.validatedBody = value;\n next();\n };\n}\n\n// User creation endpoint with comprehensive validation\nconst createUserSchema = Joi.object({\n name: Joi.string().min(1).max(100).required(),\n email: Joi.string().email().required(),\n age: Joi.number().integer().min(0).max(150).optional(),\n tags: Joi.array().items(Joi.string()).max(10).optional()\n});\n\napp.post('/api/users',\n validateSchema(createUserSchema),\n async (req, res) => {\n const { name, email, age, tags } = req.validatedBody;\n \n // Business logic validation (use 422, not 400)\n const existingUser = await db.getUserByEmail(email);\n if (existingUser) {\n return res.status(422).json({\n error: 'unprocessable_entity',\n code: 'EMAIL_ALREADY_EXISTS',\n message: 'A user with this email already exists',\n field: 'email',\n request_id: req.id\n });\n }\n \n ...Frequently Asked Questions
What's the difference between 400 Bad Request and 422 Unprocessable Entity?
400 is for syntactic/structural errors - the request itself is malformed and can't be parsed (invalid JSON, missing required headers, illegal URL characters). 422 is for semantic errors - the request parses successfully but the data fails validation or business rules (valid JSON structure but email field contains invalid email). Use 400 when you can't even parse the request; use 422 when parsing succeeds but validation fails.
Should I return 400 or 401 for missing authentication?
Use 401 Unauthorized specifically for missing or invalid authentication credentials. Use 400 only for malformed authentication data - like a badly formatted Authorization header structure (not Bearer token format, completely garbled). If auth is missing entirely or the token is invalid, that's 401. If the Authorization header syntax is broken, that's 400.
Why do I keep getting 400 errors from my API?
Common causes: (1) Malformed JSON - check for trailing commas, single quotes instead of double quotes, missing closing braces. (2) Missing Content-Type header - API expects 'application/json' but not receiving it. (3) Incorrect URL encoding - spaces and special characters not properly encoded. (4) Missing required parameters. Use tools like Postman, JSONLint, or browser DevTools Network tab to inspect the actual request being sent.
Should 400 errors be retried?
No, don't automatically retry 400 errors. Per RFC 9110, clients SHOULD NOT repeat the request without modification. 400 indicates a client-side problem that won't fix itself - you need to modify the request first. Unlike 5xx server errors (which may be transient), 400 means you sent something wrong. Fix the request, then retry.
How detailed should 400 error messages be?
Balance helpfulness with security. DO include: which field failed, what was expected, error codes for programmatic handling, request IDs for debugging. DON'T include: stack traces, internal paths, database queries, server library versions. For public APIs, sanitize error details. For internal services, more verbose errors are acceptable. Always provide enough information for developers to fix the issue without exposing system internals.
Common Causes
- Malformed JSON syntax (trailing commas, single quotes, missing braces)
- Missing Content-Type header or incorrect value (not application/json)
- Invalid URL characters (spaces, special characters not properly encoded)
- Missing required parameters or request body fields
- Oversized request payload exceeding size limits
- Malformed Authorization header (incorrect Bearer token format)
- Invalid or corrupted cookies sent with request
- Non-UTF-8 encoding in request body or headers
Implementation Guidance
- Validate JSON with JSONLint or similar tool before sending requests
- Always include Content-Type: application/json header for JSON APIs
- Properly encode URLs using encodeURIComponent() for query parameters
- Check API documentation for required fields and parameters
- Ensure request payload is within size limits (typically 1MB for JSON APIs)
- Format Authorization header correctly: ‘Bearer <token>’ for JWT
- Clear browser cookies/cache if getting 400 on previously working requests
- Use Postman or curl to test requests and identify malformed data
- Implement client-side validation to catch errors before sending
- Check for typos in field names - name vs. username can cause 400