In nginx.conf:

http { 
    geoip_country /etc/nginx/GeoIP.dat;

If I do:

    location / {
        add_header X-Geo $geoip_country_code;                   
        add_header X-Geo3 $geoip_country_code3;
        add_header X-IP $remote_addr;

Only X-IP show up in my headers.

$ curl -I www.example.org
HTTP/1.1 302 FOUND
Content-Type: text/html; charset=utf-8
Date: Thu, 17 Jan 2013 19:29:23 GMT
Location: http://www.example.org/login/?next=/
Server: nginx/1.2.2
Vary: Cookie
Connection: keep-alive

If I change the location block to:

location / {
    add_header X-Geo "foo";                   
    add_header X-Geo3 "bar";
    add_header X-IP $remote_addr;

The headers show up, how can I get $geoip_country_code?

  • Please post your other location blocks. Jan 17, 2013 at 19:36

4 Answers 4


I just found out the geo_ip has an internal option to use the X-Forwarded-For:

syntax: geoip_proxy address | CIDR;
default:     —
context:    http
This directive appeared in versions 1.3.0 and 1.2.1.
Defines trusted addresses. When a request comes from a trusted address, an address from the “X-Forwarded-For” request header field will be used instead.

Ah I just figure out what was happening. It was trying to resolve the GeoIP for the LoadBalancers IP rather than the client which is why it was blank. Once I curled the host directly, it returned the correct result. I had to use the HttpRealipModule to give the HttpGeoipModule the clients IP. In my conf:

real_ip_header X-Forwarded-For;

How are you confirm what headers are being sent when you access the / URL? Can you try this curl command?

Something similar to this but with your URL:

% curl -I www.google.com
HTTP/1.1 200 OK
Date: Thu, 17 Jan 2013 18:58:36 GMT
Expires: -1
Cache-Control: private, max-age=0
Content-Type: text/html; charset=ISO-8859-1
Set-Cookie: PREF=ID=39403a60450f4bb3:FF=0:TM=1358449116:LM=1358449116:S=uoBAz3VFZpjyc1hA; expires=Sat, 17-Jan-2015 18:58:36 GMT; path=/; domain=.google.com
Set-Cookie: NID=67=MBsS_mn5gvM2Zzt6Q6kfIO5h_brntWLnAmxZAXsNPseUgb5puv_8p8Io0ybgRqY62z0ihF-G5DLFtQOMN5MyIzM70IgPbkN4qOXKhCnQuUUqGCyuKkCg5XL6WfZoDR9m; expires=Fri, 19-Jul-2013 18:58:36 GMT; path=/; domain=.google.com; HttpOnly
P3P: CP="This is not a P3P policy! See http://www.google.com/support/accounts/bin/answer.py?hl=en&answer=151657 for more info."
Server: gws
X-XSS-Protection: 1; mode=block
X-Frame-Options: SAMEORIGIN
Transfer-Encoding: chunked
  • I've edited the question and included the curl response. I was confirming it with curl and chromes developer toolbar.
    – Kit Sunde
    Jan 17, 2013 at 19:29
  • If you set the headers with static strings do they still not show up?
    – slm
    Jan 17, 2013 at 19:30
  • @sim They do, just tried it.
    – Kit Sunde
    Jan 18, 2013 at 6:00
  • So is the issue something with those variable values getting set then?
    – slm
    Jan 18, 2013 at 7:01
  • Turns out the issue was that it was grabbing the IP of the LoadBalancer to do the matching, which didn't match anything and then left the variables blank which didn't push out the headers.
    – Kit Sunde
    Jan 18, 2013 at 7:04

Alternatively, you can specify your IP source when configuring your geoip block. Note: I have only tested this on GeoIP2

geoip2 /etc/nginx/GeoIP/GeoLite2-Country.mmdb {
    $geoip2_data_country_code source=$http_x_forwarded_for country iso_code;

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.