Content-Type Header

Fundamentals Jan 9, 2026 HTTP
http headers content-negotiation mime media-types

Definition

The Content-Type header is an HTTP header that specifies the media type (MIME type) of the request or response body. It tells the recipient how to interpret the data being sent.

The header consists of:

  1. Type/Subtype - Media type (e.g., application/json, text/html)
  2. Optional Parameters - Character encoding, boundaries (e.g., charset=utf-8)

Common Content-Type values:

  • application/json - JSON data
  • application/xml - XML data
  • text/html - HTML documents
  • text/plain - Plain text
  • multipart/form-data - File uploads
  • application/x-www-form-urlencoded - Form submissions

Content-Type is critical for REST APIs, as it determines how the server parses incoming requests and how clients interpret responses.

Example

JSON Request:

POST /api/users HTTP/1.1
Host: api.example.com
Content-Type: application/json
Content-Length: 89

{
  "name": "Alice Smith",
  "email": "[email protected]",
  "role": "developer"
}

JSON Response:

HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Content-Length: 142

{
  "id": 123,
  "name": "Alice Smith",
  "email": "[email protected]",
  "createdAt": "2026-01-09T10:30:00Z"
}

File Upload (multipart/form-data):

POST /api/upload HTTP/1.1
Host: api.example.com
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW

------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="file"; filename="document.pdf"
Content-Type: application/pdf

[Binary PDF data]
------WebKitFormBoundary7MA4YWxkTrZu0gW--

Code Example

JavaScript (Fetch API):

// Sending JSON (automatic Content-Type setting)
const createUser = async (userData) => {
  const response = await fetch('https://api.example.com/users', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json', // Explicitly set
      'Authorization': 'Bearer YOUR_TOKEN'
    },
    body: JSON.stringify(userData) // Must match Content-Type
  });
  
  // Check response Content-Type
  const contentType = response.headers.get('Content-Type');
  console.log('Response Content-Type:', contentType);
  
  if (contentType && contentType.includes('application/json')) {
    return await response.json();
  } else if (contentType && contentType.includes('text/plain')) {
    return await response.text();
  } else {
    throw new Error(`Unexpected Content-Type: ${contentType}`);
  }
};

// Sending Form Data (automatic Content-Type with boundary)
const uploadFile = async (file) => {
  const formData = new FormData();
  formData.append('file', file);
  formData.append('description', 'User uploaded file');
  
  const response = await fetch('https://api.example.com/upload', {
    method: 'POST',
    // DO NOT set Content-Type manually for FormData
    // Browser sets it with correct boundary
    body: formData
  });
  
  return await response.json();
};

// Sending URL-encoded Form Data
const submitForm = async (formData) => {
  const params = new URLSearchParams(formData);
  
  const response = await fetch('https://api.example.com/form', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/x-www-form-urlencoded'
    },
    body: params.toString()
  });
  
  return await response.json();
};

Python (requests library):

import requests

# Sending JSON
def create_user(user_data):
    response = requests.post(
        'https://api.example.com/users',
        json=user_data,  # Automatically sets Content-Type: application/json
        headers={'Authorization': 'Bearer YOUR_TOKEN'}
    )
    
    # Check response Content-Type
    content_type = response.headers.get('Content-Type')
    print(f'Response Content-Type: {content_type}')
    
    if content_type and 'application/json' in content_type:
        return response.json()
    elif content_type and 'text/plain' in content_type:
        return response.text
    else:
        raise ValueError(f'Unexpected Content-Type: {content_type}')

# Sending File Upload
def upload_file(file_path):
    with open(file_path, 'rb') as file:
        files = {'file': ('document.pdf', file, 'application/pdf')}
        data = {'description': 'User uploaded file'}
        
        response = requests.post(
            'https://api.example.com/upload',
            files=files,
            data=data
            # Content-Type automatically set with boundary
        )
    
    return response.json()

# Sending URL-encoded Form Data
def submit_form(form_data):
    response = requests.post(
        'https://api.example.com/form',
        data=form_data,  # Automatically sets Content-Type: application/x-www-form-urlencoded
        headers={'Authorization': 'Bearer YOUR_TOKEN'}
    )
    
    return response.json()

Diagram

graph TB
    subgraph "Request Content-Types"
        REQ1[application/json]
        REQ2[application/xml]
        REQ3[multipart/form-data]
        REQ4[application/x-www-form-urlencoded]
        REQ5[text/plain]
    end
    
    subgraph "Response Content-Types"
        RES1[application/json]
        RES2[text/html]
        RES3[application/pdf]
        RES4[image/png]
        RES5[text/csv]
    end
    
    subgraph "Server Processing"
        PARSE[Parse Body Based on Content-Type]
        VALIDATE[Validate Format]
        PROCESS[Process Data]
    end
    
    REQ1 --> PARSE
    REQ2 --> PARSE
    REQ3 --> PARSE
    REQ4 --> PARSE
    REQ5 --> PARSE
    
    PARSE --> VALIDATE
    VALIDATE --> PROCESS
    
    PROCESS --> RES1
    PROCESS --> RES2
    PROCESS --> RES3
    PROCESS --> RES4
    PROCESS --> RES5

Analogy

Think of Content-Type like a label on food packaging:

  • Content-Type: application/json β†’ “This is JSON, parse it as structured data”
  • Content-Type: text/plain β†’ “This is plain text, no special parsing needed”
  • Content-Type: multipart/form-data β†’ “This contains multiple parts (files + fields)”

Without the label, you wouldn’t know if it’s soup, cereal, or frozen pizza. Without Content-Type, the server doesn’t know how to interpret the data.

Best Practices

  1. Always Set Content-Type - Specify the body format in requests and responses
  2. Match Body Format - Ensure the actual content matches the declared Content-Type
  3. Include Charset - Add charset=utf-8 for text-based formats
  4. Validate on Server - Check Content-Type before parsing the body
  5. Use Standard MIME Types - Follow IANA registered media types
  6. Don’t Override FormData - Let the browser set Content-Type with boundary for multipart uploads
  7. Set Correct Type for Downloads - Use application/octet-stream or specific types for file downloads

Common Mistakes

  • Missing Content-Type - Sending JSON without Content-Type: application/json
  • Wrong Content-Type - Declaring application/json but sending XML or plain text
  • Hardcoded Boundary - Manually setting boundary in multipart/form-data instead of letting the client generate it
  • No Charset - Not specifying charset=utf-8 for text/JSON responses
  • Ignoring Content-Type - Parsing request body without checking Content-Type first
  • Using application/json for HTML - Returning HTML with Content-Type: application/json
  • Trusting Client Header - Not validating Content-Type matches actual content

Standards & RFCs