---
title: "Edge Based Language Detection Using Accept-Language Header for SEO Consistency"
---

# Edge Based Language Detection Using Accept-Language Header for SEO Consistency  

Websites that target audiences across multiple languages face a constant tension between delivering the correct language version to human visitors and providing a predictable structure for search‑engine crawlers. Traditional server‑side language negotiation can introduce latency, add complexity to caching layers, and sometimes result in inconsistent URLs that confuse search bots.  

Deploying language detection at the edge—right where the request first meets the content delivery network (CDN)—offers a lightweight, low‑latency alternative. By reading the **Accept‑Language** (AL) header, an edge function can decide which localized variant to serve, rewrite the request URL, or route to a pre‑generated language‑specific asset. When done correctly, this approach yields a single, canonical URL per language, consistent hreflang annotations, and cacheable responses that respect the TTL policies of the CDN.  

Below we walk through the essential components of an edge‑based language detection pipeline, discuss cache‑key design, explore fallback mechanisms, and outline SEO‑friendly implementation steps.

## Why Process Language at the Edge  

Processing language on the edge provides three core advantages for multilingual SEO:

1. **Reduced Round‑Trip Time** – Edge locations are geographically closer to end users, cutting the latency of the initial negotiation step.  
2. **Cache Efficiency** – By incorporating the language decision into the cache key, identical content can be stored once per language variant, avoiding the “Vary: Accept‑Language” header’s tendency to bypass CDN caching.  
3. **Predictable URLs** – Edge rewrites can map a generic request (`example.com`) to a language‑specific path (`example.com/en/`), preserving the static URL structure that search engines favor.  

These benefits translate directly into improved Core Web Vitals, lower bounce rates, and a clearer crawl signal for search engines.

## Anatomy of an Edge Request Flow  

The following Mermaid diagram illustrates a typical flow from the client’s browser to the edge function, through language detection, and finally to the origin server or cached response.

```mermaid
flowchart TD
    A["Client Browser"] --> B["Edge Node (CDN)"]
    B --> C["Read Accept-Language Header"]
    C --> D{"Supported Language?"}
    D -- Yes --> E["Map to Language Path"]
    D -- No --> F["Apply Fallback Logic"]
    E --> G["Construct Cache Key (URL + Lang)"]
    F --> G
    G --> H{"Cache Hit?"}
    H -- Hit --> I["Serve Cached Variant"]
    H -- Miss --> J["Fetch From Origin"]
    J --> K["Store Variant in Cache"]
    K --> I
    I --> L["Response to Client"]
```

The diagram emphasizes two decision points: verifying that the requested language is supported, and handling cache hits versus misses. Each branch leads to a deterministic URL that search engines can index.

## Designing the Cache Key  

A well‑crafted cache key is the linchpin of an edge‑based solution. The key should contain:

* The normalized request path (e.g., `/products/123/`).  
* The resolved language code (e.g., `en`, `fr`, `es`).  

A typical cache key format looks like `/<language>/<path>`. For example, a request for `/products/123/` with an AL header indicating French (`fr`) would generate the key `/fr/products/123/`.  

**Avoid using the `Vary` header for language** because many CDNs treat `Vary: Accept-Language` as a cache‑bypass, effectively disabling edge caching. By embedding the language directly into the cache key, you preserve the CDN’s ability to serve cached copies for each language variant.

## Implementing the Edge Function  

Below is a pseudocode example for a generic edge runtime (e.g., Cloudflare Workers, Fastly Compute@Edge, or AWS Lambda@Edge). The logic is deliberately language‑agnostic and can be adapted to any JavaScript‑compatible runtime.

```javascript
export async function handle(event) {
  const request = event.request
  const url = new URL(request.url)

  // 1. Extract Accept-Language header
  const acceptLang = request.headers.get('Accept-Language') || ''
  
  // 2. Parse header into ordered list of language tags
  const languages = parseAcceptLanguage(acceptLang)

  // 3. Determine the first supported language
  const supported = ['en', 'es', 'fr', 'de', 'zh']
  let chosenLang = supported[0] // default fallback
  for (const lang of languages) {
    const base = lang.split('-')[0] // ignore region subtags
    if (supported.includes(base)) {
      chosenLang = base
      break
    }
  }

  // 4. Rewrite URL to include language segment
  if (!url.pathname.startsWith(`/${chosenLang}/`)) {
    url.pathname = `/${chosenLang}${url.pathname}`
  }

  // 5. Create a new request with the rewritten URL
  const newRequest = new Request(url, request)

  // 6. Let the CDN handle caching based on the new URL
  return fetch(newRequest)
}

// Helper: simple Accept-Language parser
function parseAcceptLanguage(header) {
  return header
    .split(',')
    .map(part => part.split(';')[0].trim())
    .filter(Boolean)
}
```

Key points in the code

## <span class='highlight-content'>See</span> Also
- <https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept-Language>
- <https://developers.google.com/search/docs/advanced/crawling/managing-multi-regional-sites>
- <https://developers.cloudflare.com/workers/>
- <https://vercel.com/docs/concepts/functions/edge-functions>
- <https://developers.google.com/search/docs/advanced/crawling/localized-versions>
