Return variable value in Apache

I have Apache with the GeoIP module installed, and want to return the country code (stored in the GEOIP_COUNTRY_CODE environment variable) as the response body for requests to a specific URL.

In Nginx you can do the following:

location /geoIPdetect {
   add_header Content-Type text/plain;
   return 200 $geoip_country_code;
}

However, I don’t know how to do the equivalent with Apache. I’d prefer to be able to do this at .htaccess level if possible.

dj,

https://dev.maxmind.com/geoip/legacy/mod_geoip2/ has an examples section (about 3/4 down the page) which has the information you need.

Good find.

Regards,

DK

I had a look, but couldn’t see any examples there about setting the response body, just redirecting, blocking, and allowing. Which example was it you were referring to?

dj,

The Examples section of the page is 75-80% down the page:

[quote]Examples

Here are some examples of how you can use mod_geoip2.

Redirecting a client based on country
This example show you how to redirect a client based on the country code that GeoIP sets.

GeoIPEnable On
GeoIPDBFile /path/to/GeoIP.dat
 
# Redirect one country
RewriteEngine on
RewriteCond %{ENV:GEOIP_COUNTRY_CODE} ^CA$
RewriteRule ^(.*)$ http://www.canada.com$1 [R,L]
 
# Redirect multiple countries to a single page
RewriteEngine on
RewriteCond %{ENV:GEOIP_COUNTRY_CODE} ^(CA|US|MX)$
RewriteRule ^(.*)$ http://www.northamerica.com$1 [R,L]

Blocking a client based on country
This example show you how to block clients based on the country code that GeoIP sets.

GeoIPEnable On
GeoIPDBFile /path/to/GeoIP.dat
 
SetEnvIf GEOIP_COUNTRY_CODE CN BlockCountry
SetEnvIf GEOIP_COUNTRY_CODE RU BlockCountry
# ... place more countries here
 
Deny from env=BlockCountry

Allowing clients based on country
This example show you how to allow only clients from specific countries.

GeoIPEnable On
GeoIPDBFile /path/to/GeoIP.dat
 
SetEnvIf GEOIP_COUNTRY_CODE US AllowCountry
SetEnvIf GEOIP_COUNTRY_CODE CA AllowCountry
SetEnvIf GEOIP_COUNTRY_CODE MX AllowCountry
# ... place more countries here
 
Deny from all
Allow from env=AllowCountry


[/quote]

Please note that the next series of .htaccess code uses a lot of IfModule structures which I consider asinine because a webmaster would KNOW whether a module is installed or not. If it is, you’re wasting valuable machine cycles on every request and, if not, the server will stop working (and a webmaster would know that it’s this code which is offending the server and remove the code, install the module then try again).

What you’ve shown for your code for nginx looks like you’re trying to create a PHP variable and pass it along with a 200 code … which I have never seen and doubt it would work (at least not on Apache). The point of the code examples on that page is that you can perform many of the tasks you would want to do (block, whitelist, etc) and leave it to Apache to set the 200 and serve the requested script (or serve an alternate script/error handler).

Regards,

DK

All those examples are just redirecting, blocking, and allowing. (unless I missed something?)

What I’m looking to do is just have apache to respond to the request.

So the user requests /geoIPdetect

Then the server responds with a 200 code and the response body is the value of the GEOIP_COUNTRY_CODE variable.

The point is to avoid PHP entirely. If I was involving PHP it would just need a PHP script that contained a single line:

echo $_SERVER['GEOIP_COUNTRY_CODE'];

If you involve PHP, then Apache must send the various environment variables to PHP, wait for PHP to process the script and respond, before Apache can then send the response back to the user. Very inefficient compared to Apache just responding to the request itself.

dj,

The role Apache plays is to serve a file, not to generate a value for some unspecified use, i.e., you’re trying to use Apache incorrectly.

Still, if you MUST use it this way, you’re left to create a very inefficient .htaccess/mod_rewrite code which will redirect to a script which will provide your value. “Simply” create a page for each country with the code you want returned and redirect based on the GEOIP_COUNTRY_CODE. IMHO, this is a severe abuse of Apache as PHP can do the lookup much faster than Apache and provide the value directly without the nonsense of creating a number of pages to return a simple value for you to use.

Regards,

DK

How inefficient are we talking? Is this statement based on theoretical surmising or actual bench-mark tests?

Alan,

IMHO, dj bought the GeoIP database (which is an Apache module) and is trying to obtain the country code from that (using its flat file database). It’s far easier and much faster to use a relational database with IP address blocks and country codes (like available at https://www.ip2location.com/) to do the same job - a matter of using a hammer if you want to smash something rather than using a putty knife. Clearly, using PHP/MySQL should be a far superior solution. Where (he|she) may be lacking the knowledge is taking (his|her) flat file database and moving it into a relational database (a tedious chore at best).

Lacking from the OP is what the two letter country code will be used for. If I had a use for just the country code (like blocking traffic from SPAMming countries - like CN, RU, RO, etc. - I would use a handler file (a “poor man’s RewriteMap”) to catch the country code and feed it to the requested file as part of a query string. Unfortunately, that was not dj’s question so it wasn’t offered as a solution. Go figure.

Regards,

DK

1 Like

This is only based on theory. Since all PHP would be doing is echoing a single variable then exiting, it would be very fast. But when you consider that Apache has to set up the environment variables for PHP, send the request to PHP, and wait for the response, I can’t see how it could not be very inefficient compared to Apache just returning the variable value itself.

There would be no need to do a redirect or create separate pages for each country code if I wanted to go the PHP route. Apache passes the value of the GEOIP_COUNTRY_CODE onto PHP, so all that is needed is a single PHP page that echoes the value of $_SERVER['GEOIP_COUNTRY_CODE']

Do you have any reference for that? Given the overhead of PHP and MySQL, it seems unlikely they would be faster than Apache’s GeoIP module to me.

The idea is to just return the country code, as a way to get the user’s country using js. The js makes an AJAX request to the URL and gets the user’s country code as the response.

You need the specificity of the IP geolocation and the Accept-Language HTTP header wouldn’t be good enough for your needs?
AFAIK that is something Apache could handle very well.

http://httpd.apache.org/docs/current/content-negotiation.html

If most browsers were configured to send the correct localised language as the header value, e.g. en-gb, en-us, en-ca etc so you could pull the country from the header, that would be an option.

However, that would have exactly the same problem - how could you return the value of the Accept-Language HTTP header as the response straight from Apache?

Looking at this info: JavaScript for detecting browser language preference it is possible to get the user’s localised language preference straight from js (which the country code could be pulled from, avoiding the cost of an AJAX call completely). It sounds like it’s not as reliable (particularly in older browsers) as GeoIP, but may well be good enough. Thanks for bringing it to my attention.

It would be slower, for sure, but probably only by micro/milli seconds. Still plenty fast enough response time. Speaking generally: Beware the premature and the micro optimizations. Code first and foremost for clarity and correctness. Then use measurements to identify bottlenecks and performance critical areas (it’s rarely where we assume it will be). Optimizations will degrade code clarity and risk obscure bugs, so optimize only in those places where your measurements show will have a significant impact.

Unfortunately I don’t know how you can make Apache send a response body without going to another file for content, but I do know the PHP solution is fast enough, it’s simple, and it works, and not just for Apache; it works for any server, which means you wouldn’t need different solutions for each kind of server.

I wouldn’t say that having Apache return the variable’s value rather than PHP is an optimisation. Rather, using PHP to return the variable’s value would be an ‘unoptimisation’. It’s akin to using PHP to serve a file rather than Apache just serving the file itself. It could be argued that it’s also worse in terms of clarity as you effectively have a longer stack when using Apache + PHP compared to just Apache.

It is possible for Apache to set the response body without use of a file, e.g. see the first example here: Apache Custom Error Responses. Unfortunately that’s only for error responses, and variables aren’t available to use as the response body either.

This topic was automatically closed 91 days after the last reply. New replies are no longer allowed.