Configuring Varnish to make it work with language negociation

toemaz's picture

Drupal comes with a language negociation (D6: admin/settings/language/configure). One of the options is "path prefix with language fallback": If a suitable prefix is not identified, the display language is determined by the user's language preferences from the My Account page, or by the browser's language settings. The latter means it will look at Accept-Language in the http request and return the preferred language. I would like this still to work, together with Varnish cache.

There is an interesting solution described in: My Opera front page caching and Varnish hacking.

Did anyone implemented something similar before?

Currently, I have a quick work around in place, which doesn't let Varnish cache the front page (/etc/varnish/default.vcl):

  if (req.url == "/") {
    return (pass);

Obviously, this solution is far from optimal.



gchaix's picture

Just throwing some brainstorming out here ... perhaps this might be a good use for Persistent URL? You could possibly use PURL to ensure that there's always a suitable prefix available (or a "lang=x" suffix), thereby ensuring Varnish has a different URL for each language?

You might want to try this's picture

I think this may work - MultiLink with the Redirect option installed. It will redirect a url of to (where en is the default language and prefix or user preference, etc.). The redirects should get handed appropriately by Varnish. I think it will work, not sure - I did some work a year or so back with Varnish, including front-ending a Drupal 5 site. Maybe you will need to make Varnish pass through those urls that don't have a language prefix.

Trial #1

toemaz's picture

I had a first trial but not successful yet. Here are the steps:

Build accept-language.vcl

$ apt-get install git-core
$ cd
$ git clone
$ cd varnish-accept-language
$ vi Makefile
--> replace SUPPORTED_LANGUAGES=ar bg ca cs da de el en es fi fr gl hu id it ja ko nb nl pl pt-br pt-pt ro ru sv tr uk zh-hans zh-hant
$ make && make test
$ cp accept-language.vcl /etc/varnish/

Edit /etc/varnish/default.vcl

At the top, place

include "/etc/varnish/accept-language.vcl";

Under sub vcl_recv place


Under sub vcl_fetch place

  if (req.url == "/") {
    // Store different versions of the resource by the
    // content of the new X-Varnish-Accept-Language header
    set obj.http.Vary = "X-Varnish-Accept-Language";

When testing now with Firefox and the frontpage, i.e. "/", This results in the occurrence of Vary: X-Varnish-Accept-Language in the header. For all other paths, it's not there. As expected.
Note: The actual X-Varnish-Accept-Language value is not returned in the header. The value can only be retrieved from the apache logs.

Remaining problem: the frontpage is always the English one, no matter what Accept-Language you set your browser in.

Ok, but then you're almost there...

cosimo's picture

Might that be that you just need to copy "X-Varnish-Accept-Language" into the real "Accept-Language"? This should be done in vcl_recv() I think, to let Drupal see the rewritten header, instead of the original one the client sent.

If you indeed see the correct X-Varnish-Accept-Language in your apache logs, then you should try to trace what happens at Drupal level, or if Varnish is caching the pages correctly.

I usually use something like:

wget -S --header "Accept-Language: xx --header "Host:" http://your-varnish-ip-here:port/url/ -O /dev/null

Have fun :)

I'm there

toemaz's picture

Everything works out well, since I made a mistake when switching the locale with my firefox. Oops.
So, thank you!

One more thing: while outputting \"%{Accept-Language}i\" \"%{X-Varnish-Accept-Language}i\" in the apache logs, we'll get "zh-cn"  "en" , "fr"  "fr" , "en-us"  "en" , ... When using the wget command, we'll get "es,fr;q=0.7,fr-fr;q=0.3" "-" . So strangely enough, Varnish is somehow already sanitizing the Accept-Language string.

I'm using Mercury, so I'll get in touch with the Chapter Three guys to see if they know more about it. It seems to me that I can use Accept-Language without having to use the C script.


cosimo's picture

I don't think Varnish does that really, but I might be wrong of course.

Anyway, the point of the accept-language VCL is not only that of "cleaning" the Accept-Language header, but also matching against the list of languages your site supports.

If you can get away without using it, that's better for you. Less moving parts :)
Let me know how it goes.


toemaz's picture

Well, I'm using Vary: Accept-Language for the moment instead of X-Varnish-Accept-Language. But the C script is still in, bc when commenting out the script, Accept-Language went back to the normal string sent by the browser. So, Accept-Language is altered somehow by the script.

I also applied a patch on D6 core to cover special cases such as zh-cn which need to resolve to zh-hans. But that's not really related actually to Varnish.

Accept-language and negotiation

pedrosp's picture

I'm trying to varnishd' my drupal site.
Until now, I was using this Apache rule to manage the language negotiation (Pressflow 6)

#RewriteCond %{REQUEST_URI} ^/$
#RewriteCond %{HTTP:Accept-Language} ^(en|es|fr|ru) [NC]
#RewriteRule .* /%1 [L,R]

Once Varnish is active I had to remove that (because it didn't work anymore) and since then I'm trying to fine-tune default.vcl to get the same job done. I am also using the @cosimo extension btw.

Headers confirm that drupal is varnish'd but I'm not having my language detection and rewriting.
Any tips please ?

There is also the option of

stefan.r's picture

There is also the option of using the "Language cookie" + "Language selection page" modules and building a piece of javascript into the Language selection page that redirects users who have a cookie already.