Learning never exhausts the mind

By

A while back one of my websites came under a massive attack receiving an additional 1000+ hits per day, all malicious construction and all from one particular country. Since I was fast approaching my allocated bandwidth limit I had to take action to stop them accessing the site, and fast!

I decided to take the drastic action to block the entire country from accessing my site; I have had not legitimate visits from that country and can probably do without. Whilst reading about blocking a country (or extension) everybody talks about how difficult and time consuming it would be using .htaccess and blocking IP address ranges manually.

I have found an easy method for blocking a country, it only takes a few lines of code and has virtually no server load, so read on and I'll tell you.

It works like this:

Goto http://www.phptutorial.info/iptocountry/the_script.html for a look at "country identification without databases." Download the complete database (~540k) and extract it to a folder on your website. It will create a folder called 'ip_files'.

Next use this bit of PHP at the top of each of your pages. (Code provided on phptutorial.info)

if ($_SERVER['HTTP_X_FORWARDED_FOR'])
  $ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
else
  $ip   = $_SERVER['REMOTE_ADDR'];

$two_letter_country_code=iptocountry($ip);

function iptocountry($ip)
{
  $numbers = preg_split( "/./", $ip);    

  include("ip_files/".$numbers[0].".php");
  $code=($numbers[0] * 16777216) + ($numbers[1] * 65536) + ($numbers[2] * 256) + ($numbers[3]);    

  foreach($ranges as $key => $value)
  {
    if($key<=$code)
    {
      if($ranges[$key][0]>=$code)
      {
        $country=$ranges[$key][1];break;
      }
    }
  }

  if ($country=="")
  {
    $country="unknown";
  }

  return $country;
}

Then, add this little blocking script at the end of the code above:

if ($two_letter_country_code=="US")
  die();

You should replace US with the two letter country code for the country you are trying to block.

I have taken this a bit further on mine, in that I check for a valid session, and if not found, run all the checks and create a session. This prevents the script from running every page load - just when a new visitor connects.

<?php
  session_start();
  if (!isset($_SESSION['FirstVisit'])) 
  {
    if ($two_letter_country_code=="US")
      die();
    else 
      $_SESSION['FirstVisit'] = 1;
  }
?>p

Of course, this isn't a perfect solution and will only protect your PHP pages, but in an emergency?

Note, you can find a full list of country codes listed in countries.php within ip_files folder of the zip file.

 

Enjoyed this post? Why not share it!

If you enjoyed this post, please share it with others. Click one of the social media buttons below to share on Facebook, Twitter, LinkedIn, Pinterest or email to a friend.

39 thoughts on “Blocking Website Access by Country with PHP
  • Ankush
    19th March 2019 at 11:30 am

    The above is blocking only Us but if I want to block many countries at same time. Is it possible.

    Reply
    • Tim Trott
      20th March 2019 at 9:36 am

      It is possible to block multiple countries using this code.

      Simply add this line

      $country_codes_to_block = array('US', 'CA', 'RU');

      Then replace the code

      if ($two_letter_country_code=="US")

      with

      if (in_array($two_letter_country_code, $country_codes_to_block))

      Thanks to Shreyansh Jha for adding this code example.

      Reply
  • 4th December 2017 at 12:00 am

    Here's my contribution full contribution. THIS answers a lot of searchers questions and we use it now... A "Thank you
    will suffice  ;-) 

    1. Unzip files to a new folder. Should have something like | New_Folder | ip_files
    2. Create a database in mysql and call it whatever but for example "poofoo". Table is: "peewee"
    Columns: id, ip, date (fix it how you like)

    I created another table to actually log what is blocked from the block list to check against. The goal of this is to simply block either everyone but, country and/or single ip addresses. Yes, it's programmed to accept 123.45, 123.4, 123.456 or even 123. No need to type the whole ip unless you want to block that particular one or you can just block the whole node if i'm saying that right... Here we go. a nice thank you kindly would suffice, no paypal needed.

    A. Create a connection file in the "new folder" directory | connector.php

    <?php
    $dbase = 'poofoo'; // DATABASE NAME
    $host = 'hostname'; //DATABASE HOST LOCATION/SERVER
    $user = 'theuser_foo'; //USER NAME
    $pass = 'texasroadhouse'; //PASSWORD
    $conn = mysqli_connect($host, $user, $pass, $dbase) or die (mysql_error());
    ?>

    ------

    B. Create a new file within the "new folder" directory | blocker.php

    <?php
    ini_set('display_errors', 'Off');

    if ($_SERVER['HTTP_X_FORWARDED_FOR'])
    $ip = $_SERVER['HTTP_X_FORWARDED_FOR'];

    else
    $ip = $_SERVER['REMOTE_ADDR'];

    $two_letter_country_code=iptocountry($ip);
    function iptocountry($ip)
    {
    $numbers = explode( ".", $ip);
    include("ip_files/".$numbers[0].".php");
    $code=($numbers[0] * 16777216) + ($numbers[1] * 65536) + ($numbers[2] * 256) + ($numbers[3]);

    foreach($ranges as $key => $value)
    {
    if($key<=$code)
    {
    if($ranges[$key][0]>=$code)
    {
    $country=$ranges[$key][1];break;
    }
    }

    }

    if ($country=="")
    {
    $country="unknown";
    }

    $cur_country = $country;

    include ("connector.php");
    $query = "SELECT * FROM peewee";
    $result = mysqli_query($conn, $query);

    if (!$conn) {
    die;

    }

    while($row = mysqli_fetch_assoc($result)) {

    $cblock = $row['name'];
    //This if statement searches for a string variable and once found, blocks anything else not US based.
    if($cblock === "USA ONLY" and $country != "US") {
    mysqli_free_result($result);
    mysqli_close($conn);
    //send their butts to somewhere else and be done with it
    echo "<META http-equiv='refresh' content='0; URL=http://www.leapfrog.com'>";
    }

    //If they are US based but are on the block list
    $blocked = $row['blocked_country_by_ip_set'];

    }
    //If the database row matches the country array and the boolean of $blocked is true...
    if($cblock === $country and $blocked === 1) {
    $time = date('Y-m-d H:i');
    $insert = "INSERT IGNORE INTO table_of_blocked_logs VALUES ('','$ip','$ip',$time')";
    if ($conn->query($insert) === TRUE) {} else {
    //DO something here not show the error to the person you are actually wanting to block just in case
    exit;
    }

    mysqli_free_result($result);
    mysqli_close($conn);
    echo "<script>alert('Please take a moment to review a very special offer we are running before you review!')</script>";
    echo "<META http-equiv='refresh' content='0; URL=http://www.gerber.com'>";

    }
    }

    // The below bracket might be one to many, if you get the white screen of php death, remove it.
    }

    ?>
    ----

    C. Now at the top of your index.php page(rename to *.php for those using .htm,.html), you can paste the following;

    <?php include "blocker.php" ?>

    Use above for top of the tree and respectfully use your switch as needed when inside directories.

    <?php include "../blocker.php" ?>

    Reply
  • 12th June 2017 at 12:00 am

    For those who are looking to block multiple countries with this code I have a solution for you. However, I have not tested it yet but it should work.

    Step 1: Store country codes in database that you want to block. If you do not want to use database then use array directly or some other methods you like. But I prefer storing country codes in database.

    Step 2: Extract the codes from database and store it in a variable.
    Example: $country_codes_to_block = $row['block_countries']; // Fetched from database.

    Step 3: Replace the code

    if ($two_letter_country_code=="US")

    to

    if (in_array($two_letter_country_code, $country_codes_to_block))

    Step 4: Done. This should block all the countries whose country codes you have stored in database.

    Still not clear? Sorry, I can't be more clear than this. I made it as much simple and easy to understand as possible. Now apply some brains. Happy coding  ;) 

    Reply
  • brody
    10th March 2016 at 12:00 am

    is there a reason why i'm getting Fatal error: Call to undefined function iptocountry()

    for this line of code " $two_letter_country_code=iptocountry($ip); "

    Reply
  • 18th November 2015 at 12:00 am

    the preg_split should be "/./"

    toright-toleft-dot-toright (sorry for bad english)
    $numbers = preg_split( "/./", $ip);

    Reply
  • 14th January 2015 at 12:00 am

    Fatal error: Cannot redeclare iptocountry() (previously declared in /home/u475022455/public_html/head.php:61) in /home/u475022455/public_html/head.php on line 85

    give me solution

    Reply
  • 1st July 2014 at 12:00 am

    Hy,
    Thanks for that. It works good for me over a year, but now I would like to add another country.
    I wonder how you block 2 countries.
    Thanks, G

    Reply
  • 20th May 2014 at 12:00 am

    i want block some country's a list of them and any ip or a visitor log in to my page from this country's i want send them to a link .
    so how i can do that ?
    if any one here plz reply me bcz i need it to much .

    and thank you
    adam lee

    Reply
  • Ben
    15th February 2014 at 12:00 am

    Anyone using this with cloudflare? Is it working?

    Reply
  • Ben
    15th February 2014 at 12:00 am

    if ($two_letter_country_code != "US" || !strstr(strtolower($_SERVER['HTTP_USER_AGENT']), "googlebot"))
    die();

    i think this is working. code above allowing me to register at google webmaster tools.

    Reply
  • 3rd August 2013 at 12:00 am

    Hi Tim,

    Is it possible to block and entire country (say India) yes let one specific IP address from India through?

    Many thanks,
    Neil.

    Reply
    • Tim Trott
      4th August 2013 at 12:00 am

      Hi Neil

      You could do something along the lines of:

      $allowed = array('0.0.0.0', '1.1.1.1');

      if (($two_letter_country_code=="IN") && (!in_array($ip, $allowed))
      {
      // do block here
      }
      else
      {
      // do allow here
      }

      Note: This has not been tested but should get you going.

      Tim

      Reply
  • 9th July 2013 at 12:00 am

    Im in CA USA and my block code reads:

    if ($two_letter_country_code!="US")
    die();

    However - IM dying..... I think I need an updated database but I cant find the correct DB in the php format on software77.net

    Any recomendations would be fantastic.

    Reply
  • 6th May 2013 at 12:00 am

    I appreciate the help here. My webhost directed me to this link.

    With that said, I am a total noob when it comes to website building/editing.

    I know the above suggestion may sound simple to most of you. But is there a way to dumb it down to someone at my level. I am using wordpress and am a little bit familiar with using an FTP client to modify the coding of the site/as well as doing it thru the WP editor function.

    So, I am reasonably confident I can download the file and get it over to my website via FTP client. I assume I just put it in primary folder for the entire website.

    Once I do that, how do I put the php code into every page on my site to keep it blocked? I have dozens of files with a .php extension on my site.

    Thank you for your patience and your help.

    Reply
  • 18th April 2013 at 12:00 am

    PHP5 turns off the ability to use the include() statement for security reasons by default. As a result, it won't pull in the geoip data. Can you, or anyone, suggest an alternative to using the include() statement? Thanks! Joey

    Reply
    • Tim Trott
      19th April 2013 at 12:00 am

      I've not heard of PHP5 disabling include before, is this a particular "feature" of your hosting provider? Do you get any error messages when you use it?

      You could try include_once instead. This function will check to see if the file has already been included and if it has ignore the request instead of throwing an error.

      Tim

      Reply
  • 16th April 2013 at 12:00 am

    Thank you so much
    it was so usefull;

    Reply
  • 25th February 2013 at 12:00 am

    Could you please post the code for this "I have taken this a bit further on mine, in that I check for a valid session, and if not found, run all the checks and create a session" ?. I'm not good at php and don't know how to implement that. Thanks in advance

    Reply
    • Tim Trott
      26th February 2013 at 12:00 am

      Hi Mark,

      That's going back a bit since I used this code. I'll have a dig through my backups and post what I find.

      Tim

      Reply
  • ben
    4th December 2012 at 12:00 am

    How do you allow google bot etc in this script?

    Reply
  • 19th October 2012 at 12:00 am

    I am trying to include this in wordpress and am having a but of trouble, I think it has to do with how wordpress sees directories
    <?php
    if ($_SERVER['HTTP_X_FORWARDED_FOR'])
    $ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
    else
    $ip = $_SERVER['REMOTE_ADDR'];

    $two_letter_country_code=iptocountry($ip);

    function iptocountry($ip)
    {
    $numbers = preg_split( "/./", $ip);

    include("ip_files/".$numbers[0].".php");
    $code=($numbers[0] * 16777216) + ($numbers[1] * 65536) + ($numbers[2] * 256) + ($numbers[3]);

    foreach($ranges as $key => $value)
    {
    if($key<=$code)
    {
    if($ranges[$key][0]>=$code)
    {
    $country=$ranges[$key][1];break;
    }
    }
    }

    if ($country=="")
    {
    $country="unknown";
    }

    return $country;
    }
    if ($two_letter_country_code!="US")
    die();
    ?>

    this is what i used to block all non us and this is the result

    Warning: include(ip_files/.php) [function.include]: failed to open stream: No such file or directory in /home/superbha/public_html/wp-content/themes/anonymous-elegance/header.php on line 17

    Warning: include(ip_files/.php) [function.include]: failed to open stream: No such file or directory in /home/superbha/public_html/wp-content/themes/anonymous-elegance/header.php on line 17

    Warning: include() [function.include]: Failed opening 'ip_files/.php' for inclusion (include_path='.:/usr/lib/php:/usr/local/lib/php') in /home/superbha/public_html/wp-content/themes/anonymous-elegance/header.php on line 17

    Warning: Invalid argument supplied for foreach() in /home/superbha/public_html/wp-content/themes/anonymous-elegance/header.php on line 20

    Reply
  • 22nd September 2012 at 12:00 am

    thank you for this tutorial!

    I don't want to block any country rather I would like to redirect users to a specific page based on country. Surely helpful.

    Reply
  • 30th August 2012 at 12:00 am

    So glad I found this, I was trying to use an htaccess method and it wouldn't work. Great article, bookmarked for the future  :D 

    Reply
  • 8th May 2012 at 12:00 am

    Even though this is an old topic I thought I'd add to it...

    1. You can run PHP on HTML pages by using a "force-type" tag in .htacess, like this:

    1. AddHandler application/x-httpd-php5 .html

    or if that doesn't work...

    2. SetHandler application/x-httpd-php5 .html

    This tells the Server to treat all of your html pages like they are php pages. Only use one, not both!

    This adds an extra load to your Server so you may want to restrict it to certain pages by adding the (FILES index.html) limiter.

    Rather than putting it on every page, use the "include" function at the top of each page, this way you can make changes very quickly if you need to.

    My web pages *were* getting hammered by spammers and hackers from the RIPE and APNIC networks. So I went looking for a *country blocking* system and found this.

    I created my own script that does the same thing, but it uses an entirely different setup. I also use other methods to block hits from some troublesome ISPs.

    So far I haven't had any visits from RIPE or APNIC since using these codes.  :-D 

    Reply
  • 4th January 2012 at 12:00 am

    Can i use this code for html as well php pages to block some of the countries from viewing the data.?

    Reply
    • Tim Trott
      20th January 2012 at 12:00 am

      The code will only run through the PHP engine, so to run it on html pages you would need to have these processed via PHP. Alternatively you may be able to get jQuery or similar framework to perform the same task client side.

      Reply
  • achmad
    8th October 2011 at 12:00 am

    I'm not php proficient, I just wanted to block 2 country for example Uk and Greanada, how to write the code in the end of the script ?

    Reply
  • 1st August 2011 at 12:00 am

    Thanks very much for a very useful script.
    Noticed that the script fails on php 5 with the ip split.
    We rewrote that tiny section using explode instead
    and it works fine on our site.

    Was:
    $numbers = preg_split( "/./", $ip);
    Now:
    $numbers = explode( ".", $ip);

    Reply
    • 21st May 2012 at 12:00 am

      Thank you  :) 

      Reply
    • 8th September 2012 at 12:00 am

      Allow spesific country with
      if ($two_letter_country_code != "DE")
      die();

      My question: Is this good for google bot and seo?

      Reply
      • Tim Trott
        29th November 2012 at 12:00 am

        This would be bad for SEO as it would block mostly all web crawlers including googlebot

  • 1st July 2009 at 12:00 am

    I need this, but I am php challenged.  :)  Does the above code block the US, or does it block everything except the US? I need to block ALL countries EXCEPT the US. If anyone reading this can help me, I can be contacted at daniel "@" onelung.net . Thx!

    Reply
    • Tim Trott
      4th July 2009 at 12:00 am

      The code above will block the US. To reverse this and block everything apart from US try the following:

      <pre lang="php">if ($two_letter_country_code != "US")
      die();</pre>

      Hope that helps

      Reply
  • 19th August 2008 at 12:00 am

    my friend modify original code and you can use few countries now :

    $value){
    if($key=$code){$two_letter_country_code=$ranges[$key][1];break;}
    }
    }
    if ($two_letter_country_code==""){$two_letter_country_code="unkown";}
    return $two_letter_country_code;
    }
    ?>

    Reply
  • 14th June 2008 at 12:00 am

    How have you overcome the issue that the IP to country database changes almost daily? Have you looked at any of the services and connecting to them?

    Reply
  • 11th June 2007 at 12:00 am

    going to try it now, I had someone stalk me from Italy, had some php ip blocks but she kept on using different machines.

    A country block is what I am looking for.

    thanks

    Reply

Leave a Reply

Fields marked with * are mandatory.

We respect your privacy, and will not make your email public. Hashed email address may be checked against Gravatar service to retrieve avatars. This site uses Akismet to reduce spam. Learn how your comment data is processed.