Do language based redirects take into account browser's language?

I have the following rules in my _redirects file:

/                               /en/                            302  Language=en
/                               /fr/                            302 

Whenever I access the root (/), the redirection doesn’t take into account of browser’s language (Accept-Language header):

The doc only mentions the cookie approach to be overriden by Javascript but doesn’t talk about Accept-Language header:

Netlify supports GeoIP and language-based redirects directly from our CDN nodes.

This is ideal for large multi-regional sites where you want to send people to the right location based on their location or browser language.

Both the language and the country can be specified in a cookie as well ( nf_lang and nf_country respectively), so you can easily override the default behavior with JavaScript

My site is a pretty simple landing page so I don’t want to use cookies. Could you tell me how the default behavior of this kind of redirect works? How Netlify detects user language?

Thanks in advance,


That redirect is working for me:

Can you tell me how you are reproducing this error, if it still occurs? Also, if so if you could paste the value of the X-NF-Request-ID HTTP response header those are viciously hard to retype and we can correlate them to individual requests and CDN servers to help us track things down. I did manage to get yours retyped and see your server, but cannot reproduce directly on it either, using your headers, so I suspect it may have been something in cache that was fixed by some cache clearing we did yesterday.

Hi @futuregerald,

Thanks for your check. Unfortunately, the error is still persist. To reproduce it, I do nothing in particular, just type the address in a browser. You can even produce it with a curl command like this:

curl -H 'Accept-Language: en-US,en;q=0.5' -L -v -s 1> /dev/null

and its output (in my computer, based in Paris, France):

* Rebuilt URL to:
*   Trying
* Connected to ( port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* Cipher selection: ALL:!EXPORT:!EXPORT40:!EXPORT56:!aNULL:!LOW:!RC4:@STRENGTH
* successfully set certificate verify locations:
*   CAfile: /etc/ssl/cert.pem
  CApath: none
* TLSv1.2 (OUT), TLS handshake, Client hello (1):
} [222 bytes data]
* TLSv1.2 (IN), TLS handshake, Server hello (2):
{ [102 bytes data]
* TLSv1.2 (IN), TLS handshake, Certificate (11):
{ [2570 bytes data]
* TLSv1.2 (IN), TLS handshake, Server key exchange (12):
{ [300 bytes data]
* TLSv1.2 (IN), TLS handshake, Server finished (14):
{ [4 bytes data]
* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
} [37 bytes data]
* TLSv1.2 (OUT), TLS change cipher, Client hello (1):
} [1 bytes data]
* TLSv1.2 (OUT), TLS handshake, Finished (20):
} [16 bytes data]
* TLSv1.2 (IN), TLS change cipher, Client hello (1):
{ [1 bytes data]
* TLSv1.2 (IN), TLS handshake, Finished (20):
{ [16 bytes data]
* SSL connection using TLSv1.2 / ECDHE-RSA-AES128-GCM-SHA256
* ALPN, server accepted to use h2
* Server certificate:
*  subject:
*  start date: Jul 19 01:29:38 2019 GMT
*  expire date: Oct 17 01:29:38 2019 GMT
*  subjectAltName: host "" matched cert's ""
*  issuer: C=US; O=Let's Encrypt; CN=Let's Encrypt Authority X3
*  SSL certificate verify ok.
* Using HTTP2, server supports multi-use
* Connection state changed (HTTP/2 confirmed)
* Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0
* Using Stream ID: 1 (easy handle 0x7f837b006600)
> GET / HTTP/2
> Host:
> User-Agent: curl/7.54.0
> Accept: */*
> Accept-Language: en-US,en;q=0.5
* Connection state changed (MAX_CONCURRENT_STREAMS updated)!
< HTTP/2 302
< cache-control: public, max-age=0, must-revalidate
< content-length: 20
< content-type: text/plain; charset=utf-8
< date: Fri, 09 Aug 2019 05:40:40 GMT
< location: /fr/
< age: 266477
< server: Netlify
< x-nf-request-id: de5a6584-b422-4c83-9209-91b71677ec39-6055060
* Ignoring the response-body
{ [20 bytes data]
* Connection #0 to host left intact
* Issue another request to this URL: ''
* Found bundle for host 0x7f837a516bf0 [can multiplex]
* Re-using existing connection! (#0) with host
* Connected to ( port 443 (#0)
* Using Stream ID: 3 (easy handle 0x7f837b006600)
> GET /fr/ HTTP/2
> Host:
> User-Agent: curl/7.54.0
> Accept: */*
> Accept-Language: en-US,en;q=0.5
< HTTP/2 200
< cache-control: public, max-age=0, must-revalidate
< content-type: text/html; charset=UTF-8
< date: Fri, 09 Aug 2019 20:31:36 GMT
< etag: "9141d92a19fb4522b219e2cbb6649bda-ssl"
< strict-transport-security: max-age=31536000
< age: 213021
< content-length: 1353472
< server: Netlify
< x-nf-request-id: de5a6584-b422-4c83-9209-91b71677ec39-6055083
{ [16384 bytes data]
* Connection #0 to host left intact

Its Netlify ID is: de5a6584-b422-4c83-9209-91b71677ec39-6055060

Other test with my browser: de5a6584-b422-4c83-9209-91b71677ec39-6101910

Hope it helps.

Thanks for those details and sorry to be slow to get back to you! Just after clearing the cache, I see that we get perfect redirects for your example curl from each CDN node, but I can see that it seems to diverge over time so that sometimes the redirect to /fr/* is happening instead.

That seems like a bug in our caching of redirects, and I’m not certain how soon we’ll have a fix. In the meantime, you may want to try deploying your “primary” content in the main folder so you don’t need a redirect for it - instead only redirecting /fr since /en/* actually lives in / instead.

I understand that isn’t optimal, but it’s my best suggestion for getting things working at present.

Thank @fool for the workaround. I will have a look into it further.

Just out of curiosity, is there any way that I can get informed about the issue status if the Netlify team fix it in the future? Some kind of change logs / release notes?

Many thanks,

1 Like

hi there, I don’t have a timeline for this per se, but, we have this thread mentioned in the issue for this concern and will post an update here when there is any news :muscle:

1 Like

We would also really need this.
Actually we expected this to work, after reading the manuals form:

We are trying to do this:
/ /de/ 301 Language=de
/ /es/ 301 Language=es
/ /en/ 301

What we get is an redirect to /en, always.
Also in the .toml syntax this did not work.

Is there any workaround?

Hi , please provide links to your URL’s that you’re testing, your actual _redirects file or netlif.toml as well so we can see what you’re deploying and test it. We need more details to see what’s actually wrong since the rules you described should work.

Sorrily I already deleted the page at netlify, as fow now we chose another hosting.
Yet we would prefere netlify.

The repository is private, so I can not show you.
But I can copy the code again.

I tried with this netlify.toml in the root.

# Added automatically by the Netlify CLI. It has no effect during normal 
# Git-backed deploys.
ID = "Your_Site_ID"

# Settings in the [build] context are global and are applied to all contexts 
# unless otherwise overridden by more specific contexts.  

# Directory to change to before starting a build. 
# This is where we will look for package.json/.nvmrc/etc.
# base = "museum-landingpage.webflow/"

# Directory (relative to root of your repo) that contains the deploy-ready 
# HTML files and assets generated by the build. If a base directory has
# been specified, include it in the publish directory path.
publish = "public/"

   from = "/"
   to = "/de"
   status = 301
   conditions = {Language = ["de"]}

   from = "/"
   to = "/es"
   status = 301
   conditions = {Language = ["es"]}

   from = "/"
   to = "/en"
   status = 301

Also I tried with this redirect file at /public/_redirects

/ /de/ 301! Language=de
/ /es/ 301! Language=es
/ /en/ 301

I saw in both approaches, that everything was just redirected to the en version, without detecting the other languages.

OK, if you’d like to set us up another proof of concept site here, we’d be happy to look for you. No need to give it a custom domain, just link the repo and build successfully and link us to the site and we can take a look at what gets deployed.

What you quoted for instance only redirects the main landing page, not any other assets.

Yes by intent.
that is default in most CMS systems. Redirection only if no lang is set for example by /de/ or /en/ urls.

Hey there, we still need the a link to a site that has the broken redirects so we can test it. Thanks!

A broken site for testing: and
are master and en branches, respectively,
Note that the branches differ in the order of netlify.toml redirections.

Always redirects to English-language version, even when accept-language starts with pl when a redirection to Polish-language version is desired.

Here is another one :

_redirects content :
/ /de/ 301! Language=de
/ /fr/ 301! Language=fr
/ /nl/ 301! Language=nl
/ /en/ 301

I’m always being redirected to the /en version.

Thanks a lot in advance !

Hi @simonr - works for me:

$ curl -svH "Accept-Language: en" -o /dev/null 2>&1 | egrep "(301|Location)"
< HTTP/1.1 301 Moved Permanently
< Location: /en/
$ curl -svH "Accept-Language: de" -o /dev/null 2>&1 | egrep "(301|Location)"
< HTTP/1.1 301 Moved Permanently
< Location: /de/ 

How are you testing?

Hi again Archie,

Seems to me we show the different content:

curl -sv -o /tmp/dfl outputs different than curl -svH "Accept-Language: pl" -o /tmp/pl which is the only language redirect configured on that site (I see you tried to configure some other one, but it did not work - this isn’t valid:

  from = "/"
  to = "/index.:lang.html"
  status = 200
  query = {lang = ":lang"}

hmm, i can confirm the above works for me too, at least now. but then i look in my browser, and it sends accept-language: pl-PL,pl;q=0.9,en-US;q=0.8,en;q=0.7. when i replace pl in your curl line with pl-PL,pl;q=0.9,en-US;q=0.8,en;q=0.7 the result is different undesirably again. But yeah, i only have rule against pl and not against pl-PL or against pl-PL,pl, maybe i gotta try adding all that stuff into a more massive pattern somehow, including pl,pl-PL too ig.

How is my query-param rewrite rule invalid? and both work well. As i am not using JavaScript anywhere on my site, and Set-Cookie is not passed along in headers-on-rewrite, i have no way of setting cookies (from query-params i would choose it to be if it were possible — i would have another query-param rule that would do storing the preference into nf_lang). And even then, i would still want to have a way to browse the other language version without having cookies enabled or accept-language altered.

here, have a neat Bash function:

whatlang () 
    RESULT=$(curl $3 -sH "Accept-Language: $1" "$2" | grep -Eo '(sertifikashions|onlajnowe)');
    case "$RESULT" in 
        onlajnowe) echo Polish;;
        sertifikashions) echo English;;
        *) echo NoMatch;;


whatlang pl
whatlang en
whatlang pl-PL
whatlang pl,pl-PL
whatlang pl-PL,pl
whatlang "pl-PL,pl;q=0.9,en-US;q=0.8,en;q=0.7"
whatlang pl-PL "?lang=pl"
whatlang pl "?lang=en" -v
whatlang pl "" -v

Thanks a lot for the quick answer.

It is true that it seems to work well with simple “fr”, “de”, etc but if I check my browser header, here is what I have :


$ curl -svH "Accept-Language: fr-BE,fr;q=0.9,en-BE;q=0.8,en;q=0.7,nl-BE;q=0.6,nl;q=0.5,fr-FR;q=0.4,en-US;q=0.3" -o /dev/null 2>&1 | egrep "(301|Location)"

< HTTP/1.1 301 Moved Permanently

< Location: /en/

In this case, it should be french instead of english…

Hi @simonr, we don’t currently parse the quality value (e.g. q=0.9) So the it’s likely not going to work reliably if you have multiple languages with different quality values. I’ve added you to the issue tracking this behavior and we’ll update you if and when we have implemented it.

Hi Dennis, Can you add me to the issue tracking as well? We have similar issue and really want the server undertands the qvalue.