├── README.md └── renameMe.php /README.md: -------------------------------------------------------------------------------- 1 | UptimeFlare 2 | ============== 3 | 4 | A PHP web hook to handle down notifications from uptimerobot. If a DOWN notification is received, it'll change the A record of a domain on cloudflare to a backup IP set by you. 5 | 6 |

7 | License: MIT
8 | Dependencies: Web Server, PHP, php-curl lib 9 |

10 | 11 | Setup:
12 | 1. I suggest you rename the php file to something obscure.
13 | 2. Add / change the global vars section. It consists of credentials and other information that needs to be modified to your needs.
14 | 3. Upload this script to the desired server, make sure the script is accessible via the internet.
15 | 4. On Cloudflare:
16 | if there's any client filtering in your API token settings, make sure you add your server's IP that's running this script to the whitelist. 17 | 5. On uptimeRobot:
18 | Create a new alert contact and select web hook. For url, put in the url to the php script and add "?key=YourKeySetInThePHPScript&".
19 | If my $key was set to HelloWorld and I renamed my script to monitorUP.php which is accessible via 127.0.0.1, then for the url I would put
20 | http://127.0.0.1/monitorUP.php?key=HelloWorld& Make sure you have the & at the end.
21 | 6. Add this alert type to your websites. It's recommended that you have more than the PHP hook as an alert type (such as email notification), so you can switch it back to the primary server once it's up. This should be done manually (login to cloudflare and change the IP) because there might be inconsistencies between the primary and the backup server, which requires manual intervention. 22 | 23 | With this setup, a website shouldn't have a downtime of more than ~6 minutes, assuming your DNS provider does not cache queries. 24 |

25 | You can reach me on twitter . 26 |

27 | If you're looking for StatusCake + Cloudflare, you can find it here . 28 | -------------------------------------------------------------------------------- /renameMe.php: -------------------------------------------------------------------------------- 1 | "8.8.8.8,1" ); 18 | 19 | sets the domain google.com's backup IP address to 8.8.8.8 and enabling cloudflare proxy (orange cloud), 20 | github.com's backip IP address to 127.0.0.1 and disabling cloudflare proxy (no orange cloud) 21 | */ 22 | $CF_domains = array( "google.com" => "8.8.8.8,1", 23 | "github.com" => "127.0.0.1,0"); 24 | 25 | //API key to your cloudflare account 26 | $CF_key = ""; 27 | 28 | //Cloudflare email address 29 | $CF_email= ""; 30 | 31 | //end of global vars 32 | ////////////////////////////////////////////////////////// 33 | //global functions 34 | 35 | //checks if the domain exists in $CF_domains and the backup IP it's up via HTTP 36 | function checkDomain ($domains, $domain){ 37 | foreach ( $domains as $key => $value ){ 38 | if (strcmp($key, $domain) == 0 ){ 39 | $vars=explode(",",$domains["$domain"]); 40 | $ip=$vars[0]; 41 | if (checkHost($ip)) { 42 | return true; 43 | } else { 44 | echo "backup server unreachable via HTTP\n"; 45 | return false; 46 | } 47 | } 48 | } 49 | return false; 50 | } 51 | 52 | 53 | //checks if host returns "HTTP/1.1 200 OK" within a reasonable time frame 54 | function checkHost ($host) { 55 | $ch = curl_init(); 56 | curl_setopt($ch, CURLOPT_URL, $host); 57 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); 58 | //curl_setopt($ch, CURLOPT_VERBOSE, 1); 59 | curl_setopt($ch, CURLOPT_HEADER, 1); 60 | curl_setopt($ch,CURLOPT_TIMEOUT,5); 61 | $result=curl_exec($ch); 62 | 63 | $headerSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE); 64 | $header = substr($result, 0, $headerSize); 65 | if (strpos($header, "HTTP/1.1 200 OK") !== false){ 66 | return TRUE; 67 | } else { 68 | return FALSE; 69 | } 70 | 71 | } 72 | 73 | 74 | //interacts with CF API to switch to the backup IP 75 | function cfBkup ($domains, $domain, $subdomain, $cfkey, $cfemail){ 76 | $vars=explode(",",$domains["$subdomain"]); 77 | $headers = [ 78 | "X-Auth-Email: $cfemail", 79 | "X-Auth-Key: $cfkey", 80 | "Content-Type: application/json" 81 | ]; 82 | //get ZONE ID 83 | $ch= curl_init("https://api.cloudflare.com/client/v4/zones?name=$domain"); 84 | curl_setopt($ch, CURLOPT_RETURNTRANSFER,1); 85 | curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); 86 | $results= json_decode(curl_exec($ch), TRUE); 87 | curl_close($ch); 88 | //var_dump($results); 89 | $zoneID=$results['result'][0]['id']; 90 | 91 | //get DNS Record ID 92 | if ($zoneID != "" ) { 93 | $ch= curl_init("https://api.cloudflare.com/client/v4/zones/$zoneID/dns_records?name=$subdomain"); 94 | curl_setopt($ch, CURLOPT_RETURNTRANSFER,1); 95 | curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); 96 | $results= json_decode(curl_exec($ch), TRUE); 97 | curl_close($ch); 98 | //var_dump($results); 99 | $DNSID = $results['result'][0]['id']; 100 | if ($DNSID != "") { 101 | $updateVars=explode("," , $domains["$subdomain"]); 102 | if (strcmp($updateVars[1], "1") == 0 ) { 103 | $updateVars[1] = true; 104 | } 105 | else { 106 | $updateVars[1] = false; 107 | } 108 | $PUTVars=array("id" => "$DNSID", "type" => "A", "name" => "$subdomain", "content" => "$updateVars[0]", "proxied" => $updateVars[1]); 109 | $ch= curl_init("https://api.cloudflare.com/client/v4/zones/$zoneID/dns_records/$DNSID"); 110 | curl_setopt($ch, CURLOPT_RETURNTRANSFER,1); 111 | curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); 112 | curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "PUT"); 113 | curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($PUTVars)); 114 | $results= json_decode(curl_exec($ch), TRUE); 115 | curl_close($ch); 116 | //var_dump($results); 117 | if ($results['success'] != TRUE ) { 118 | echo "An error occured updating DNS reocrd for $subdomain"; 119 | } 120 | } else { 121 | echo "Failed to get DNSID for $subdomain"; 122 | } 123 | } else { 124 | echo "Failed to get zoneID for $domain"; 125 | } 126 | } 127 | //end of global functions 128 | //////////////////////////////////////////////////////// 129 | if (strcmp(urldecode($_GET['key']), $key) != 0){ 130 | //header('Location: http://speedtest.tele2.net/1000GB.zip'); 131 | echo "invalid key\n"; 132 | die; 133 | } 134 | 135 | echo "This is a monitoring script"; //helps to ensure the web hook is correct, feel free to remove it 136 | 137 | if (!empty($_GET['monitorURL']) && !empty($_GET['alertType'])){ 138 | // strip http(s):// 139 | $url = urldecode($_GET['monitorURL']); 140 | $checkDomain = implode('/', array_slice(explode('/', preg_replace('/https?:\/\/|www./', '', $url)), 0, 1)); 141 | 142 | //check to see if $checkURL is in the $CF_domain list and make sure it's a down alert 143 | if ( checkDomain($CF_domains, $checkDomain) && strcmp($_GET['alertType'], "1") == 0){ 144 | //do more stuff here for subdomains 145 | $FQDN=explode(".",$checkDomain); 146 | $FQDN=$FQDN[sizeof($FQDN)-2].".".$FQDN[sizeof($FQDN)-1]; 147 | 148 | //if your FQDN is seperated by 2 periods (ex: something.co.uk), uncomment the line below 149 | // $FQDN=$FQDN[sizeof($FQDN)-3].".".$FQDN[sizeof($FQDN)-2].".".$FQDN[sizeof($FQDN)-1]; 150 | 151 | echo $FQDN; 152 | cfBkup ($CF_domains, $FQDN, $checkDomain, $CF_key, $CF_email); 153 | } else { echo "invalid url or alert type";}; 154 | }else {echo "missing get variables";} 155 | --------------------------------------------------------------------------------