Nginx: Redirect Backend Traffic Based Upon Client IP Address

by on December 31, 2013 · 3 comments· LAST UPDATED December 31, 2013

in , ,

I have four Apache backend servers in front of nginx reverse proxy server. How do I make sure nginx reverse proxy load balancer always send specific client IP address (say IP 1.2.3.4) request to http://apachereadwrite/ backend and rest to http://apachereadonly/ backend?

Tutorial details
DifficultyEasy (rss)
Root privilegesYes
RequirementsNone
Estimated completion time5m
Nginx web server does support if conditional configuration. You can redirect and/or select configuration depending on client ip address. In this case you need to use the variable called $remote_addr which can be used to retrieve information about the users ip address. This is useful if you want to give file upload capabilities to your own office IP address and read-only capabilities to the rest of the world based upon client IP address or vpn address:

      File upload is disabled on
        server {A,B,C} via php
           +------+  +-----+  +-----+  +-----+
           |      |  |     |  |     |  |     |
           |      |  |     |  |     |  |     |
 Backends  |  A   |  |  B  |  |  C  |  |  D  | File upload enabled
           |      |  |     |  |     |  |     | on server D via PHP
           |      |  |     |  |     |  |     |
           |      |  |     |  |     |  |     |
           +--+---+  +-+---+  +--+--+  +-+---+
              |        |         |       |
              |        |         |       |
              +--------+---------+-------+
                             |
                             |
                         +---+---+
                         |       |
                         |       |
                         |       |
                         |       |
                         |       |
                         |       |
                         +-------+
                       nginx reverse proxy server

Edit the file /etc/php.ini on server {A,B,C}, type:
# vi /etc/php.ini
Make the following changes to /etc/php.ini:

 
# Disallow uploading altogether this makes moving or injecting bad scripts/code onto your web server more difficult
file_uploads = Off
 
# Disallow treatment of file requests as fopen calls
allow_url_fopen = Off
allow_url_include = Off
 

Restart Apache server on {A,B,C}. Make sure file upload is enabled on server A by editing php.ini and setting the following entries:

 
file_uploads = On
upload_max_filesize=2M
post_max_size=4M
 

Nginx syntax

The syntax is as follows:

if ( $remote_addr ~* ip-address-here ) {
           proxy_pass http://YOUR-BACKEND-HERE;
}

First set default proxy_pass:

 
## Default backend is apachereadonly ##
proxy_pass  http://apachereadonly;
 

Check for client ip address:

 
## If IP is 1.2.3.4 send backend to apachereadwrite ##
if ( $remote_addr ~* 1.2.3.4 ) {
    proxy_pass http://apachereadwrite;
}
 

Examples

Edit nginx.conf file, enter:
# vi nginx.conf
Edit/append as follows:

 
   ## apachereadonly backend ##
   upstream apachereadonly  {
     server 10.10.11.10:8011; 
     server 10.10.11.11:8011; 
     server 10.10.11.12:8011; 
     ip_hash; 
   }
   ## apachereadwrite backend ##
  upstream apachereadwrite {
     server 10.10.11.13:8011;
 
   }
 
        ## config ##
        location / {
                proxy_set_header        Accept-Encoding   "";
                proxy_set_header        Host              $http_host;
                proxy_set_header        X-Forwarded-By    $server_addr:$server_port;
                proxy_set_header        X-Forwarded-For   $remote_addr;
                proxy_set_header        X-Forwarded-Proto $scheme;
                proxy_set_header        X-Real-IP         $remote_addr;
                ## default backend
                proxy_pass  http://apachereadonly;
                ## send traffic to apachereadwrite backend if ip is 1.2.3.4 ##
                if ( $remote_addr ~* 1.2.3.4 ) {
                        proxy_pass http://apachereadwrite;
                }
                proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504;
        }
        ## rest of config ##
 

Save and close the file. Restart / reload nginx server:
# /etc/init.d/nginx reload
OR
# /usr/sbin/nginx -s reload

TwitterFacebookGoogle+PDF versionFound an error/typo on this page? Help us!

{ 3 comments… read them below or add one }

1 Alexander Kuznetcov February 19, 2014 at 11:12 am

Other way is to use MAP module for switching backends for special remote addresses
http://nginx.org/en/docs/http/ngx_http_map_module.html

Example of configuration:

## apachereadonly backend ##
upstream apachereadonly  {
  server 10.10.11.10:8011;
  server 10.10.11.11:8011;
  server 10.10.11.12:8011;
  ip_hash;
}
## apachereadwrite backend ##
upstream apachereadwrite {
  server 10.10.11.13:8011;
}
## define default backend and backend for special remote addresses
map $remote_addr $backend {
  default apachereadonly;
  1.2.3.4 apachereadwrite;
}
## virtual host
server {
  listen 80;
  server_name  example.com;
  # your configuration here
  location / {
    proxy_set_header        Accept-Encoding   "";
    proxy_set_header        Host              $http_host;
    proxy_set_header        X-Forwarded-By    $server_addr:$server_port;
    proxy_set_header        X-Forwarded-For   $remote_addr;
    proxy_set_header        X-Forwarded-Proto $scheme;
    proxy_set_header        X-Real-IP         $remote_addr;
    proxy_pass  http://$backend;
    proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504;
  }
}

Reply

2 Alexander Kuznetcov February 19, 2014 at 11:18 am

Also, GEO module http://nginx.org/en/docs/http/ngx_http_geo_module.html could be used in same way as MAP module
With geo module you can define networks (in CIDR notation), that map doesn’t support

Reply

3 Nix Craft February 19, 2014 at 11:41 am

I wasn’t aware of map module. I appreciate your post and time for sharing examples :)

Reply

Leave a Comment

Tagged as: , , ,

Previous Faq:

Next Faq: