Skip to main content

What is HTTP Status Codes?

HTTP Status Codes are standardized three-digit numbers returned by web servers to indicate the result of an HTTP request. Often called “response codes” or simply “status codes,” they serve as a communication protocol between client and server. The core purpose is to provide immediate, machine-readable feedback about whether a request succeeded, failed, or requires additional action. This solves the problem of ambiguous request outcomes by establishing a universal language for HTTP transaction results.

How it works in C#

2xx Success

Explanation: 2xx codes indicate that the client’s request was successfully received, understood, and processed. These are positive responses signaling that everything worked as expected.
[ApiController]
[Route("api/[controller]")]
public class ProductsController : ControllerBase
{
    [HttpGet("{id}")]
    public IActionResult GetProduct(int id)
    {
        var product = _productService.GetById(id);
        
        if (product == null)
            return NotFound(); // Returns 404
        
        // 200 OK - Standard success response with data
        return Ok(product); 
    }

    [HttpPost]
    public async Task<IActionResult> CreateProduct(ProductDto productDto)
    {
        var createdProduct = await _productService.CreateAsync(productDto);
        
        // 201 Created - Successfully created resource with location header
        return CreatedAtAction(nameof(GetProduct), 
            new { id = createdProduct.Id }, createdProduct);
    }

    [HttpDelete("{id}")]
    public IActionResult DeleteProduct(int id)
    {
        _productService.Delete(id);
        
        // 204 No Content - Success but no content to return
        return NoContent();
    }
}
Common 2xx codes: 200 OK (standard success), 201 Created (resource created), 204 No Content (success with no response body)

3xx Redirection

Explanation: 3xx codes indicate that further action needs to be taken by the client to complete the request. This includes redirects, caching instructions, and resource relocation.
public class RedirectController : ControllerBase
{
    [HttpGet("old-url")]
    public IActionResult OldEndpoint()
    {
        // 301 Moved Permanently - Resource has permanently moved
        return RedirectPermanent("/api/products/new-url");
    }

    [HttpGet("temp-redirect")]
    public IActionResult TemporaryRedirect()
    {
        // 302 Found (Temporary Redirect) - Resource temporarily at different location
        return Redirect("/api/temporary-location");
    }

    [HttpGet("cache-me")]
    public IActionResult CachedResponse()
    {
        // 304 Not Modified - Use cached version (client sends If-Modified-Since header)
        if (Request.Headers.ContainsKey("If-Modified-Since"))
        {
            return StatusCode(304);
        }
        
        Response.Headers.Add("Last-Modified", DateTime.UtcNow.ToString("R"));
        return Ok(new { data = "This can be cached" });
    }
}

4xx Client Error

Explanation: 4xx codes indicate errors caused by the client’s request. This includes malformed requests, authentication issues, or attempting to access forbidden resources.
[ApiController]
public class AuthController : ControllerBase
{
    [HttpPost("login")]
    public IActionResult Login(LoginRequest request)
    {
        if (!ModelState.IsValid)
        {
            // 400 Bad Request - Client sent invalid data
            return BadRequest(ModelState);
        }

        var user = _authService.Authenticate(request.Email, request.Password);
        
        if (user == null)
        {
            // 401 Unauthorized - Authentication required/failed
            return Unauthorized("Invalid credentials");
        }

        if (!user.IsActive)
        {
            // 403 Forbidden - Authenticated but not authorized
            return Forbid("Account is deactivated");
        }

        return Ok(new { token = GenerateToken(user) });
    }

    [HttpGet("secure-data/{id}")]
    public IActionResult GetSecureData(int id)
    {
        var data = _service.GetSecureData(id, User.Identity.Name);
        
        if (data == null)
        {
            // 404 Not Found - Resource doesn't exist
            return NotFound($"Data with id {id} not found");
        }

        if (data.Owner != User.Identity.Name)
        {
            // 403 Forbidden - No permission to access
            return Forbid("You don't own this resource");
        }

        return Ok(data);
    }
}
401 vs 403: Use 401 for missing/invalid authentication, 403 for valid authentication but insufficient permissions.

5xx Server Error

Explanation: 5xx codes indicate that the server failed to fulfill a valid request. These are server-side errors where the client’s request was correct but the server couldn’t process it.
public class OrdersController : ControllerBase
{
    [HttpPost("process-order")]
    public async Task<IActionResult> ProcessOrder(Order order)
    {
        try
        {
            await _orderService.ProcessAsync(order);
            return Ok(new { message = "Order processed successfully" });
        }
        catch (DatabaseConnectionException ex)
        {
            // 503 Service Unavailable - Server cannot handle request
            return StatusCode(503, new { error = "Database unavailable, try again later" });
        }
        catch (ExternalApiException ex)
        {
            // 502 Bad Gateway - Upstream server error
            return StatusCode(502, new { error = "Payment gateway unavailable" });
        }
        catch (Exception ex)
        {
            // 500 Internal Server Error - Generic server error
            _logger.LogError(ex, "Unexpected error processing order");
            return StatusCode(500, new { error = "An unexpected error occurred" });
        }
    }

    [HttpGet("heavy-operation")]
    public async Task<IActionResult> HeavyOperation()
    {
        try
        {
            // Simulate timeout scenario
            await Task.Delay(30000); // 30 seconds
            return Ok();
        }
        catch (TaskCanceledException)
        {
            // 504 Gateway Timeout - Server acting as gateway timed out
            return StatusCode(504, "Operation timed out");
        }
    }
}

Common Usage

Explanation: Common usage patterns involve consistent application of status codes across your API to maintain predictability and proper RESTful semantics.
public static class ApiResponseHelper
{
    public static IActionResult Success\<T\>(T data, string message = null)
    {
        return new OkObjectResult(new ApiResponse\<T\>
        {
            Success = true,
            Data = data,
            Message = message,
            StatusCode = 200
        });
    }

    public static IActionResult Created\<T\>(string location, T data)
    {
        var result = new ObjectResult(new ApiResponse\<T\>
        {
            Success = true,
            Data = data,
            StatusCode = 201
        })
        {
            StatusCode = 201
        };
        return result;
    }

    public static IActionResult Error(int statusCode, string message, IEnumerable<string> errors = null)
    {
        return new ObjectResult(new ApiResponse<object>
        {
            Success = false,
            Message = message,
            Errors = errors,
            StatusCode = statusCode
        })
        {
            StatusCode = statusCode
        };
    }
}

// Usage in controller
[HttpPut("{id}")]
public IActionResult UpdateProduct(int id, ProductDto productDto)
{
    if (id != productDto.Id)
        return ApiResponseHelper.Error(400, "ID mismatch");

    try
    {
        var updatedProduct = _productService.Update(productDto);
        return ApiResponseHelper.Success(updatedProduct, "Product updated");
    }
    catch (NotFoundException)
    {
        return ApiResponseHelper.Error(404, "Product not found");
    }
}

Why is HTTP Status Codes Important?

Standardized Communication (Protocol Design): Status codes provide a universal language that enables interoperability between diverse clients and servers, following established web standards.
Separation of Concerns (Single Responsibility Principle): They separate success and error handling logic, allowing clients to process responses based on clear categories rather than parsing custom error messages.
Predictable Error Handling (Robustness Principle): Following “be conservative in what you send, liberal in what you accept” ensures APIs gracefully handle both expected and unexpected scenarios through standardized status code ranges.

Advanced Nuances

Idempotency and Status Codes

public class IdempotentController : ControllerBase
{
    private readonly IDistributedCache _cache;
    
    [HttpPost("idempotent-operation")]
    public async Task<IActionResult> IdempotentOperation([FromHeader] string idempotencyKey, Order order)
    {
        if (string.IsNullOrEmpty(idempotencyKey))
            return BadRequest("Idempotency key required");
            
        // Check if we've already processed this request
        var cachedResult = await _cache.GetStringAsync(idempotencyKey);
        if (cachedResult != null)
        {
            // 409 Conflict - Operation already processed
            return Conflict("Operation already processed");
        }
        
        // Process and cache result
        var result = await _orderService.ProcessAsync(order);
        await _cache.SetStringAsync(idempotencyKey, JsonSerializer.Serialize(result));
            
        return Ok(result);
    }
}
Use 409 Conflict for idempotency violations or concurrent modification conflicts.

Content Negotiation and Status Codes

public class ContentNegotiationController : ControllerBase
{
    [HttpGet("complex-response")]
    public IActionResult GetComplexResponse()
    {
        var data = new { message = "Success", timestamp = DateTime.UtcNow };
        
        // Different status codes based on Accept header
        if (Request.Headers.Accept.Contains("application/vnd.company.error+json"))
        {
            return StatusCode(202, new { status = "processing", data });
        }
        
        return Ok(data);
    }
}

Build docs developers (and LLMs) love