HTTP

Header

Content-Language Header

Learn how the Content-Language header specifies the natural language(s) of response content. Understand language tags and internationalization best practices.

7 min read beginner Try in Playground

Content-Language Header

TL;DR: Indicates the natural language(s) of the content for accessibility and SEO. Helps browsers and screen readers handle multilingual content properly.

What is Content-Language?

The Content-Language header tells the client what natural language(s) the content is in. It’s like a label saying “This content is written in English” or “This page is in French and German.”

This helps browsers, screen readers, and search engines understand and properly handle multilingual content.

How Content-Language Works

Server sends content in a specific language:

HTTP/1.1 200 OK
Content-Type: text/html
Content-Language: en-US

<!DOCTYPE html>
<html lang="en-US">
<head><title>Welcome</title></head>
<body><h1>Hello World</h1></body>
</html>
```text

**Server sends multilingual content:**

```http
HTTP/1.1 200 OK
Content-Type: application/json
Content-Language: en, es, fr

{
  "en": "Welcome",
  "es": "Bienvenido",
  "fr": "Bienvenue"
}

Syntax

Content-Language: en
Content-Language: en-US
Content-Language: en, es, fr
```text

### Values

- **language-tag** - Language code (ISO 639-1)
- **region** - Optional region/country code (ISO 3166-1)
- Multiple languages separated by commas

## Common Examples

### Single Language

```http
Content-Language: en

Content is in English.

Language with Region

Content-Language: en-US
```text

Content is in American English.

### Multiple Languages

```http
Content-Language: en, es

Content contains both English and Spanish.

Specific Regional Variant

Content-Language: pt-BR
```text

Content is in Brazilian Portuguese.

## Real-World Scenarios

### Localized Website

```http
GET /about HTTP/1.1
Host: example.com
Accept-Language: fr-FR

HTTP/1.1 200 OK
Content-Type: text/html
Content-Language: fr-FR

<!DOCTYPE html>
<html lang="fr-FR">
<head><title>À propos de nous</title></head>
<body><h1>Bienvenue</h1></body>
</html>

Multilingual API Response

GET /api/product/123 HTTP/1.1
Host: api.example.com

HTTP/1.1 200 OK
Content-Type: application/json
Content-Language: en, es, de

{
  "id": 123,
  "name": {
    "en": "Blue Shirt",
    "es": "Camisa Azul",
    "de": "Blaues Hemd"
  }
}
```text

### Language Negotiation

```http
GET /article/456 HTTP/1.1
Host: blog.example.com
Accept-Language: de-DE, de;q=0.9, en;q=0.8

HTTP/1.1 200 OK
Content-Language: de-DE
Vary: Accept-Language

<article>Deutsche Inhalte...</article>

Document Download

GET /docs/manual.pdf HTTP/1.1
Host: downloads.example.com

HTTP/1.1 200 OK
Content-Type: application/pdf
Content-Language: ja
Content-Disposition: attachment; filename="manual-ja.pdf"

[Japanese PDF content]
```javascript

## Server Implementation

### Express.js (Node.js)

```javascript
const express = require('express')
const app = express()

// Static content language
app.get('/about', (req, res) => {
  res.setHeader('Content-Language', 'en-US')
  res.send('<h1>About Us</h1>')
})

// Language negotiation
app.get('/welcome', (req, res) => {
  const acceptLanguage = req.headers['accept-language']
  let language = 'en'
  let content = 'Welcome'

  if (acceptLanguage.includes('es')) {
    language = 'es'
    content = 'Bienvenido'
  } else if (acceptLanguage.includes('fr')) {
    language = 'fr'
    content = 'Bienvenue'
  }

  res.setHeader('Content-Language', language)
  res.setHeader('Vary', 'Accept-Language')
  res.send(`<h1>${content}</h1>`)
})

// Multilingual JSON
app.get('/api/greeting', (req, res) => {
  const greeting = {
    en: 'Hello',
    es: 'Hola',
    fr: 'Bonjour',
    de: 'Hallo'
  }

  res.setHeader('Content-Language', 'en, es, fr, de')
  res.json(greeting)
})

Advanced Language Selection

const languages = {
  en: { greeting: 'Hello', title: 'Welcome' },
  es: { greeting: 'Hola', title: 'Bienvenido' },
  fr: { greeting: 'Bonjour', title: 'Bienvenue' },
  de: { greeting: 'Hallo', title: 'Willkommen' }
}

function getBestLanguage(acceptLanguage) {
  // Parse Accept-Language header
  const requested = acceptLanguage
    .split(',')
    .map((lang) => {
      const [code, q] = lang.trim().split(';q=')
      return {
        code: code.split('-')[0],
        quality: q ? parseFloat(q) : 1.0
      }
    })
    .sort((a, b) => b.quality - a.quality)

  // Find best match
  for (const { code } of requested) {
    if (languages[code]) {
      return code
    }
  }

  return 'en' // default
}

app.get('/page', (req, res) => {
  const lang = getBestLanguage(req.headers['accept-language'] || 'en')
  const content = languages[lang]

  res.setHeader('Content-Language', lang)
  res.setHeader('Vary', 'Accept-Language')
  res.json(content)
})
```javascript

### FastAPI (Python)

```python
from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse

app = FastAPI()

@app.get("/greeting")
async def greeting(request: Request):
    accept_language = request.headers.get('accept-language', 'en')

    # Simple language detection
    if 'es' in accept_language:
        lang = 'es'
        message = 'Hola'
    elif 'fr' in accept_language:
        lang = 'fr'
        message = 'Bonjour'
    else:
        lang = 'en'
        message = 'Hello'

    return JSONResponse(
        content={'message': message},
        headers={
            'Content-Language': lang,
            'Vary': 'Accept-Language'
        }
    )

@app.get("/product/{product_id}")
async def product(product_id: int):
    product_data = {
        'id': product_id,
        'names': {
            'en': 'Blue Shirt',
            'es': 'Camisa Azul',
            'de': 'Blaues Hemd'
        }
    }

    return JSONResponse(
        content=product_data,
        headers={'Content-Language': 'en, es, de'}
    )

Django

from django.http import JsonResponse
from django.utils.translation import get_language
from django.views.decorators.vary import vary_on_headers

@vary_on_headers('Accept-Language')
def greeting_view(request):
    lang = get_language()

    greetings = {
        'en': 'Hello',
        'es': 'Hola',
        'fr': 'Bonjour',
        'de': 'Hallo'
    }

    response = JsonResponse({'greeting': greetings.get(lang, 'Hello')})
    response['Content-Language'] = lang

    return response

def multilingual_view(request):
    content = {
        'en': 'Welcome',
        'es': 'Bienvenido',
        'fr': 'Bienvenue'
    }

    response = JsonResponse(content)
    response['Content-Language'] = 'en, es, fr'

    return response
```nginx

### Nginx

```nginx
server {
    listen 80;
    server_name example.com;

    # Set language based on URL path
    location /en/ {
        add_header Content-Language "en";
        root /var/www/english;
    }

    location /es/ {
        add_header Content-Language "es";
        root /var/www/spanish;
    }

    location /fr/ {
        add_header Content-Language "fr";
        root /var/www/french;
    }

    # Language negotiation using map
    map $http_accept_language $lang {
        default en;
        ~*^es es;
        ~*^fr fr;
        ~*^de de;
    }

    location / {
        add_header Content-Language $lang;
        add_header Vary "Accept-Language";
        try_files /content_$lang.html /content_en.html =404;
    }
}

Best Practices

For Servers

1. Always set Vary header with Content-Language

# ✅ Include Vary header
Content-Language: es
Vary: Accept-Language

# ❌ Missing Vary (caching issues)
Content-Language: es
```text

**2. Use appropriate language codes**

```http
# ✅ Standard ISO codes
Content-Language: en-US

# ❌ Non-standard codes
Content-Language: english

3. Match HTML lang attribute

<!-- HTTP Header -->
Content-Language: fr-FR

<!-- HTML -->
<html lang="fr-FR"></html>
```text

**4. Be specific when needed**

```http
# ✅ Specific variant when content differs
Content-Language: pt-BR  (Brazilian Portuguese)
Content-Language: pt-PT  (European Portuguese)

# ⚠️ Generic only if applicable to all variants
Content-Language: pt

5. List all languages for multilingual content

# ✅ Multilingual document
Content-Language: en, es, fr

# ❌ Only listing one when multiple present
Content-Language: en
```text

### For Clients

**1. Send Accept-Language for better responses**

```javascript
fetch('/api/content', {
  headers: {
    'Accept-Language': 'es-MX, es;q=0.9, en;q=0.8'
  }
})

2. Check Content-Language to adapt UI

fetch('/api/article').then((response) => {
  const contentLang = response.headers.get('Content-Language')

  if (contentLang === 'ar' || contentLang === 'he') {
    // Set RTL text direction
    document.dir = 'rtl'
  }

  return response.json()
})
```javascript

**3. Use for content type detection**

```javascript
const response = await fetch('/document')
const lang = response.headers.get('Content-Language')

// Apply language-specific processing
if (lang === 'zh' || lang === 'ja') {
  // Use appropriate fonts for CJK languages
  document.body.classList.add('cjk-font')
}

Language Tags

Common Language Codes

Content-Language: en    # English
Content-Language: es    # Spanish
Content-Language: fr    # French
Content-Language: de    # German
Content-Language: zh    # Chinese
Content-Language: ja    # Japanese
Content-Language: ar    # Arabic
Content-Language: ru    # Russian
Content-Language: pt    # Portuguese
Content-Language: it    # Italian
```http

### With Region Codes

```http
Content-Language: en-US    # American English
Content-Language: en-GB    # British English
Content-Language: es-ES    # Spain Spanish
Content-Language: es-MX    # Mexican Spanish
Content-Language: pt-BR    # Brazilian Portuguese
Content-Language: pt-PT    # European Portuguese
Content-Language: zh-CN    # Simplified Chinese
Content-Language: zh-TW    # Traditional Chinese

Content-Language vs Accept-Language

Accept-Language (Request)

# Client tells server what languages it understands
GET /page HTTP/1.1
Accept-Language: fr-FR, fr;q=0.9, en;q=0.8
```text

### Content-Language (Response)

```http
# Server tells client what language the content is in
HTTP/1.1 200 OK
Content-Language: fr-FR

SEO Considerations

Help Search Engines

Content-Language: en-US
Vary: Accept-Language
```text

Combined with HTML lang attribute for better SEO.

### Hreflang Alternative

```html
<!-- For alternate language versions -->
<link rel="alternate" hreflang="es" href="/es/page" />
<link rel="alternate" hreflang="fr" href="/fr/page" />

Testing Content-Language

Using curl

# Check Content-Language
curl -I https://example.com/page

# Request specific language
curl -H "Accept-Language: es-ES" https://example.com/page

# View full headers
curl -v https://example.com/page
```javascript

### Using JavaScript

```javascript
// Check response language
fetch('https://api.example.com/content').then((response) => {
  const lang = response.headers.get('Content-Language')
  console.log('Content language:', lang)
})

// Request specific language
fetch('https://api.example.com/content', {
  headers: {
    'Accept-Language': 'fr-FR, fr;q=0.9'
  }
}).then((response) => {
  console.log('Content-Language:', response.headers.get('Content-Language'))
  return response.text()
})

Content-Language and SEO

Search engines use Content-Language as one signal for language detection, but it is not the primary signal. Google primarily uses the lang attribute on the <html> element and the content of the page itself. Content-Language is more useful for non-HTML content types like JSON APIs, PDF documents, and plain text files where there is no HTML lang attribute.

For multilingual websites, the hreflang link attribute in HTML (or in the sitemap) is more important for SEO than Content-Language. hreflang tells search engines about alternate language versions of a page and which geographic regions they target, enabling correct language-specific search results. Content-Language and hreflang serve complementary purposes: Content-Language describes what language the current response is in, while hreflang describes the relationship between different language versions of the same content.

Frequently Asked Questions

What is Content-Language?

Content-Language indicates the language(s) intended for the audience. It helps with accessibility, search engines, and browser language features. Example: Content-Language: en-US.

Is Content-Language the same as Accept-Language?

No. Accept-Language is a request header stating preferred languages. Content-Language is a response header stating what language the content is in.

Can Content-Language have multiple values?

Yes, for multilingual content: Content-Language: en, fr. This indicates the content is intended for both English and French speakers.

Does Content-Language affect character encoding?

No, Content-Language is about human language, not encoding. Use Content-Type charset for encoding. A page can be in Japanese (Content-Language: ja) with UTF-8 encoding.

Keep Learning