HTTP Status Codes Complete Guide: 200, 201, 400, 404, 500 Explained

Master HTTP status codes with practical examples. Learn about 200, 201, 400, 404, 500 status codes and their proper implementation in web development.

S

StalkTechie

Author

December 4, 2025
113 views

HTTP Status Codes Complete Guide: 200, 201, 400, 404, 500 Explained with Examples

Master HTTP status codes with this comprehensive guide. Learn when to use 200, 201, 400, 404, 500 status codes and how to implement them correctly in your web applications and APIs.

Table of Contents

HTTP Status Codes Overview

HTTP status codes are three-digit numbers returned by servers to indicate the outcome of client requests. They are essential for API development, web applications, and understanding web communication protocols.

Status Code Categories

Range Category Description
1xx Informational Request received, continuing process
2xx Success Request successfully received and processed
3xx Redirection Further action needed to complete request
4xx Client Error Request contains bad syntax or cannot be fulfilled
5xx Server Error Server failed to fulfill valid request

2xx Success Status Codes

2xx codes indicate that the client's request was successfully received, understood, and accepted.

200 OK

The standard success response for most HTTP requests. Indicates the request succeeded.


// Express.js implementation
app.get('/api/users/:id', (req, res) => {
    const user = userService.getUserById(req.params.id);
    res.status(200).json({
        success: true,
        data: user
    });
});

// Example Response
HTTP/1.1 200 OK
Content-Type: application/json

{
    "success": true,
    "data": {
        "id": 123,
        "name": "John Doe",
        "email": "john@example.com"
    }
}
        

201 Created

The request succeeded and a new resource was created as a result. Typically used after POST requests.


app.post('/api/users', (req, res) => {
    const newUser = userService.createUser(req.body);
    res.status(201).json({
        success: true,
        message: 'User created successfully',
        data: newUser
    });
});

// Example Response
HTTP/1.1 201 Created
Content-Type: application/json
Location: /api/users/456

{
    "success": true,
    "message": "User created successfully",
    "data": {
        "id": 456,
        "name": "Jane Smith",
        "email": "jane@example.com"
    }
}
        

204 No Content

The server successfully processed the request but is not returning any content. Commonly used for DELETE operations.


app.delete('/api/users/:id', (req, res) => {
    userService.deleteUser(req.params.id);
    res.status(204).send();
});

// Example Response
HTTP/1.1 204 No Content
        

3xx Redirection Status Codes

3xx codes indicate that the client must take additional action to complete the request.

301 Moved Permanently

The URL of the requested resource has been changed permanently. Search engines update their links.


app.get('/old-page', (req, res) => {
    res.redirect(301, '/new-page');
});

// Example Response
HTTP/1.1 301 Moved Permanently
Location: https://example.com/new-page
        

302 Found (Temporary Redirect)

The URI of the requested resource has been changed temporarily.


app.get('/temporary-page', (req, res) => {
    res.redirect(302, '/temporary-location');
});
        

304 Not Modified

Used for caching purposes. The client can use cached version as the resource hasn't changed.


app.get('/api/data', (req, res) => {
    const lastModified = req.headers['if-modified-since'];
    const dataLastModified = getDataLastModified();
    
    if (lastModified && new Date(lastModified) >= dataLastModified) {
        res.status(304).send();
    } else {
        res.set('Last-Modified', dataLastModified.toUTCString());
        res.status(200).json(getData());
    }
});
        

4xx Client Error Status Codes

4xx codes indicate errors that seem to have been caused by the client.

400 Bad Request

The server cannot process the request due to client error (malformed request syntax, invalid request message framing, or deceptive request routing).


app.post('/api/users', (req, res) => {
    const { name, email } = req.body;
    
    if (!name || !email) {
        return res.status(400).json({
            success: false,
            message: 'Name and email are required fields',
            errors: {
                name: !name ? 'Name is required' : null,
                email: !email ? 'Email is required' : null
            }
        });
    }
    
    // Process valid request
});

// Example Response
HTTP/1.1 400 Bad Request
Content-Type: application/json

{
    "success": false,
    "message": "Name and email are required fields",
    "errors": {
        "name": "Name is required",
        "email": "Email is required"
    }
}
        

401 Unauthorized

The client must authenticate itself to get the requested response. Similar to 403 but specifically for authentication.


app.get('/api/protected', authenticateToken, (req, res) => {
    // This will only execute if authentication succeeds
    res.json({ message: 'Access granted' });
});

function authenticateToken(req, res, next) {
    const token = req.headers['authorization'];
    
    if (!token) {
        return res.status(401).json({
            success: false,
            message: 'Access token required'
        });
    }
    
    // Verify token logic
    next();
}
        

403 Forbidden

The client does not have access rights to the content. The server knows the client's identity.


app.delete('/api/users/:id', authorizeAdmin, (req, res) => {
    const userId = req.params.id;
    const currentUser = req.user;
    
    if (currentUser.role !== 'admin' && currentUser.id !== userId) {
        return res.status(403).json({
            success: false,
            message: 'Insufficient permissions to perform this action'
        });
    }
    
    // Delete user logic
});
        

404 Not Found

The server cannot find the requested resource. This is the most common error code.


app.get('/api/users/:id', (req, res) => {
    const user = userService.getUserById(req.params.id);
    
    if (!user) {
        return res.status(404).json({
            success: false,
            message: 'User not found'
        });
    }
    
    res.status(200).json({
        success: true,
        data: user
    });
});

// Example Response
HTTP/1.1 404 Not Found
Content-Type: application/json

{
    "success": false,
    "message": "User not found"
}
        

429 Too Many Requests

The user has sent too many requests in a given amount of time ("rate limiting").


const rateLimit = require('express-rate-limit');

const limiter = rateLimit({
    windowMs: 15 * 60 * 1000, // 15 minutes
    max: 100, // Limit each IP to 100 requests per windowMs
    message: {
        success: false,
        message: 'Too many requests from this IP, please try again later'
    },
    standardHeaders: true,
    legacyHeaders: false,
});

app.use('/api/', limiter);
        

5xx Server Error Status Codes

5xx codes indicate cases where the server is aware that it has encountered an error or is otherwise incapable of performing the request.

500 Internal Server Error

A generic error message when the server encounters an unexpected condition.


app.post('/api/process-data', async (req, res) => {
    try {
        const result = await complexProcessing(req.body);
        res.status(200).json({
            success: true,
            data: result
        });
    } catch (error) {
        console.error('Processing error:', error);
        res.status(500).json({
            success: false,
            message: 'An internal server error occurred',
            reference: generateErrorReference() // For support tracking
        });
    }
});

// Example Response
HTTP/1.1 500 Internal Server Error
Content-Type: application/json

{
    "success": false,
    "message": "An internal server error occurred",
    "reference": "ERR_20251122_123456"
}
        

502 Bad Gateway

The server, while acting as a gateway or proxy, received an invalid response from the upstream server.


app.get('/api/external-data', async (req, res) => {
    try {
        const response = await fetch('https://external-api.com/data');
        const data = await response.json();
        res.json(data);
    } catch (error) {
        res.status(502).json({
            success: false,
            message: 'Bad gateway: Unable to fetch data from external service'
        });
    }
});
        

503 Service Unavailable

The server is not ready to handle the request. Common causes are maintenance or temporary overloading.


let maintenanceMode = false;

// Middleware to check maintenance mode
app.use((req, res, next) => {
    if (maintenanceMode && req.path !== '/health') {
        return res.status(503).json({
            success: false,
            message: 'Service temporarily unavailable for maintenance',
            retryAfter: 3600 // 1 hour in seconds
        });
    }
    next();
});

// Health check endpoint
app.get('/health', (req, res) => {
    res.status(200).json({
        status: 'OK',
        timestamp: new Date().toISOString(),
        version: '1.0.0'
    });
});
        

Practical Implementation in Web Applications

Express.js Error Handling Middleware


// Centralized error handling middleware
const errorHandler = (err, req, res, next) => {
    console.error('Error:', err);

    // Mongoose validation error
    if (err.name === 'ValidationError') {
        const errors = Object.values(err.errors).map(error => error.message);
        return res.status(400).json({
            success: false,
            message: 'Validation failed',
            errors: errors
        });
    }

    // Mongoose duplicate key error
    if (err.code === 11000) {
        return res.status(409).json({
            success: false,
            message: 'Duplicate resource found'
        });
    }

    // JWT authentication error
    if (err.name === 'JsonWebTokenError') {
        return res.status(401).json({
            success: false,
            message: 'Invalid token'
        });
    }

    // Default to 500 server error
    res.status(500).json({
        success: false,
        message: 'Internal server error'
    });
};

// Use the error handler at the end of middleware chain
app.use(errorHandler);
        

RESTful API Response Standardization


// Response utility functions
const responseUtils = {
    success: (res, data, message = 'Success', statusCode = 200) => {
        return res.status(statusCode).json({
            success: true,
            message,
            data,
            timestamp: new Date().toISOString()
        });
    },
    
    error: (res, message, statusCode = 500, errors = null) => {
        const response = {
            success: false,
            message,
            timestamp: new Date().toISOString()
        };
        
        if (errors) {
            response.errors = errors;
        }
        
        return res.status(statusCode).json(response);
    },
    
    created: (res, data, message = 'Resource created successfully') => {
        return res.status(201).json({
            success: true,
            message,
            data,
            timestamp: new Date().toISOString()
        });
    },
    
    notFound: (res, resource = 'Resource') => {
        return res.status(404).json({
            success: false,
            message: `${resource} not found`,
            timestamp: new Date().toISOString()
        });
    }
};

// Usage in controllers
app.get('/api/users/:id', async (req, res) => {
    try {
        const user = await User.findById(req.params.id);
        if (!user) {
            return responseUtils.notFound(res, 'User');
        }
        responseUtils.success(res, user);
    } catch (error) {
        responseUtils.error(res, 'Failed to fetch user', 500);
    }
});
        

Best Practices and Common Mistakes

Do's and Don'ts

Best Practice Example
Use 201 for successful resource creation POST /api/users → 201 Created
Use 204 for successful deletions with no content DELETE /api/users/1 → 204 No Content
Provide meaningful error messages 400: "Email format is invalid" not "Bad request"
Use 401 for authentication, 403 for authorization 401: Not logged in, 403: Logged in but no permission
Include error details for client errors 400 with field-specific validation errors

Common Anti-Patterns to Avoid

  • Always returning 200 with error flags: Use proper status codes instead of always returning 200
  • Using 500 for client errors: Client errors should use 4xx codes
  • Revealing sensitive information: Don't expose stack traces or internal details in production
  • Inconsistent response formats: Maintain consistent structure across all endpoints
  • Ignoring caching headers: Use 304 and cache-control headers appropriately

FAQs: HTTP Status Codes

What's the difference between 401 and 403 status codes?

401 Unauthorized means authentication is required and has failed or not been provided. 403 Forbidden means the server understands the request but refuses to authorize it, even with valid authentication.

When should I use 200 vs 204 status code?

Use 200 when returning content in the response body. Use 204 when the request succeeds but no content needs to be returned, such as after a successful DELETE operation or update that doesn't require returning data.

What's the proper use case for 302 vs 301 redirect?

Use 301 for permanent redirects when a URL has permanently moved. Search engines will update their indexes. Use 302 for temporary redirects when the move is temporary and the original URL should remain indexed.

Should I always return JSON with error responses?

For APIs, yes - maintain consistent JSON responses. For web applications, you might return HTML error pages for better user experience, but APIs should always use consistent JSON formats.

How should I handle 500 errors in production?

Log the complete error details internally, but return a generic message to clients. Include a unique error reference ID that users can provide to support for troubleshooting specific issues.

Conclusion

Understanding and properly implementing HTTP status codes is crucial for building robust, user-friendly web applications and APIs. The correct use of status codes improves API clarity, helps with debugging, and provides better user experiences.

Remember that status codes are part of the HTTP protocol specification and following established conventions makes your applications more predictable and easier to integrate with other systems. Always provide meaningful, consistent responses and handle errors gracefully.

By implementing the patterns and best practices outlined in this guide, you'll create more reliable and maintainable web services that properly communicate their state to both human users and automated clients.

Share this post:

Related Articles

Discussion

0 comments

Please log in to join the discussion.

Login to Comment