QuotaGuard and Heroku Integration Guide

    QuotaGuard and Heroku Integration Guide

    QuotaGuard Static IPs allow your Heroku applications to send outbound traffic through a load-balanced pair of static IP addresses. Once set up, you can use QuotaGuard’s IPs to connect to IP-restricted databases and APIs that require a known IP address for allowlisting.

    You do not need QuotaGuard to connect between services within your Heroku Private Space or to other Heroku add-ons. QuotaGuard is for connecting to external services that require static IP allowlisting.

    QuotaGuard Products on Heroku

    Heroku offers three QuotaGuard products:

    ProductUse CaseProtocolBest For
    QuotaGuardDynamic IPsHTTPBypassing API rate limits shared across Heroku apps
    QuotaGuard StaticStatic IPsHTTP/SOCKS5Firewall allowlisting, API integrations
    QuotaGuard ShieldStatic IPs (HIPAA/PCI)HTTPS/SOCKS5 over TLSRegulated industries, sensitive data

    This guide covers QuotaGuard Static. For HIPAA or PCI compliance requirements, see the QuotaGuard Shield documentation.

    Getting Started

    Provisioning the Add-on

    Add QuotaGuard Static to your Heroku application using the CLI:

    heroku addons:create quotaguardstatic:micro --app your-app-name
    

    You’ll see output confirming your static IPs:

    Creating quotaguardstatic:micro on your-app-name... done, (~$19/month)
    Your static IPs are [52.34.188.175, 52.37.142.13]
    

    Available plans range from a free Starter tier (250 requests) to Unlimited ($1,599/month). View all plans at elements.heroku.com/addons/quotaguardstatic.

    Automatic Configuration

    Once provisioned, Heroku automatically sets the QUOTAGUARDSTATIC_URL environment variable in your app. Verify it with:

    heroku config:get QUOTAGUARDSTATIC_URL --app your-app-name
    

    Expected output:

    http://username:password@us-east-static-01.quotaguard.com:9293
    

    This URL contains your credentials and the proxy hostname. Your app uses this to route requests through your static IPs.

    Finding Your Static IPs

    Your two static IPs are displayed when you provision the add-on. You can also find them in the QuotaGuard dashboard:

    heroku addons:open quotaguardstatic --app your-app-name
    

    Why two IPs? QuotaGuard provides a load-balanced pair for high availability. Traffic may route through either IP at any time. Add both IPs to your firewall allowlists.

    Region Matching

    QuotaGuard automatically provisions your proxy in the same AWS region as your Heroku app:

    Heroku RegionQuotaGuard Proxy
    us (Virginia)US-East
    eu (Ireland/Frankfurt)EU
    sydneyAP-Southeast-2
    tokyoAP-Northeast-1

    This ensures minimal latency between your app and the proxy.

    Configuring Your Application

    HTTP Proxy Overview

    The HTTP proxy routes outbound HTTP and HTTPS traffic through your static IPs. When your app makes a request to an HTTPS endpoint, the proxy transparently issues a CONNECT request, establishing an encrypted tunnel between your app and the destination. The proxy never sees the encrypted payload.

    Ruby / Rails

    Ruby’s rest-client gem supports proxies natively:

    require "rest-client"
    
    RestClient.proxy = ENV["QUOTAGUARDSTATIC_URL"]
    
    res = RestClient.get("https://ip.quotaguard.com")
    puts "Your Static IP is: #{res.body}"
    

    Using Net::HTTP:

    require 'net/http'
    require 'uri'
    
    proxy_uri = URI.parse(ENV['QUOTAGUARDSTATIC_URL'])
    
    http = Net::HTTP.new(
      'api.example.com',
      443,
      proxy_uri.host,
      proxy_uri.port,
      proxy_uri.user,
      proxy_uri.password
    )
    http.use_ssl = true
    
    response = http.get('/data')
    puts response.body
    

    Python / Django

    Using the Requests library:

    import os
    import requests
    
    proxies = {
        "http": os.environ['QUOTAGUARDSTATIC_URL'],
        "https": os.environ['QUOTAGUARDSTATIC_URL']
    }
    
    res = requests.get("https://ip.quotaguard.com/", proxies=proxies)
    print(res.text)
    

    Using urllib:

    import os
    import urllib.request
    
    os.environ['http_proxy'] = os.environ['QUOTAGUARDSTATIC_URL']
    
    url = 'https://ip.quotaguard.com/'
    proxy_handler = urllib.request.ProxyHandler()
    opener = urllib.request.build_opener(proxy_handler)
    
    with opener.open(url) as response:
        print(response.read().decode('utf-8'))
    

    Node.js

    Using axios with https-proxy-agent:

    const axios = require('axios');
    const { HttpsProxyAgent } = require('https-proxy-agent');
    
    const proxyUrl = process.env.QUOTAGUARDSTATIC_URL;
    const agent = new HttpsProxyAgent(proxyUrl);
    
    axios.get('https://ip.quotaguard.com', { httpsAgent: agent })
      .then(res => console.log('Static IP:', res.data))
      .catch(err => console.error('Error:', err));
    

    Install the proxy agent: npm install https-proxy-agent

    Using native http module (for HTTP endpoints):

    const http = require("http");
    const { URL } = require("url");
    
    const proxy = new URL(process.env.QUOTAGUARDSTATIC_URL);
    const target = new URL("http://ip.quotaguard.com/");
    
    const options = {
      hostname: proxy.hostname,
      port: proxy.port || 80,
      path: target.href,
      headers: {
        "Proxy-Authorization": "Basic " + Buffer.from(proxy.username + ":" + proxy.password).toString("base64"),
        "Host": target.hostname
      }
    };
    
    http.get(options, (res) => {
      res.pipe(process.stdout);
    });
    

    Using fetch with undici:

    import { ProxyAgent, fetch } from 'undici';
    
    const proxyUrl = process.env.QUOTAGUARDSTATIC_URL;
    const dispatcher = new ProxyAgent(proxyUrl);
    
    const response = await fetch('https://ip.quotaguard.com', { dispatcher });
    const data = await response.json();
    console.log('Static IP:', data.ip);
    

    PHP

    Using cURL:

    <?php
    
    function lookup() {
        $quotaguard_env = getenv("QUOTAGUARDSTATIC_URL");
        $quotaguard = parse_url($quotaguard_env);
    
        $proxyUrl = $quotaguard['host'] . ":" . $quotaguard['port'];
        $proxyAuth = $quotaguard['user'] . ":" . $quotaguard['pass'];
    
        $url = "https://ip.quotaguard.com/";
    
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($ch, CURLOPT_PROXY, $proxyUrl);
        curl_setopt($ch, CURLOPT_PROXYAUTH, CURLAUTH_BASIC);
        curl_setopt($ch, CURLOPT_PROXYUSERPWD, $proxyAuth);
        $response = curl_exec($ch);
        return $response;
    }
    
    $res = lookup();
    print_r($res);
    ?>
    

    Java

    import java.net.*;
    import java.io.*;
    
    public class QuotaGuardExample {
        public static void main(String[] args) throws IOException {
            URL proxyUrl = new URL(System.getenv("QUOTAGUARDSTATIC_URL"));
            String userInfo = proxyUrl.getUserInfo();
            String user = userInfo.substring(0, userInfo.indexOf(':'));
            String password = userInfo.substring(userInfo.indexOf(':') + 1);
    
            System.setProperty("http.proxyHost", proxyUrl.getHost());
            System.setProperty("http.proxyPort", Integer.toString(proxyUrl.getPort()));
    
            Authenticator.setDefault(new Authenticator() {
                protected PasswordAuthentication getPasswordAuthentication() {
                    return new PasswordAuthentication(user, password.toCharArray());
                }
            });
    
            URL url = new URL("https://ip.quotaguard.com");
            URLConnection conn = url.openConnection();
            BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
    
            String inputLine;
            while ((inputLine = in.readLine()) != null) {
                System.out.println(inputLine);
            }
            in.close();
        }
    }
    

    Go

    package main
    
    import (
        "fmt"
        "io"
        "net/http"
        "net/url"
        "os"
    )
    
    func main() {
        proxyURL, err := url.Parse(os.Getenv("QUOTAGUARDSTATIC_URL"))
        if err != nil {
            panic(err)
        }
    
        client := &http.Client{
            Transport: &http.Transport{
                Proxy: http.ProxyURL(proxyURL),
            },
        }
    
        resp, err := client.Get("https://ip.quotaguard.com")
        if err != nil {
            panic(err)
        }
        defer resp.Body.Close()
    
        body, _ := io.ReadAll(resp.Body)
        fmt.Println("Static IP:", string(body))
    }
    

    Clojure

    (defn get-quota-guard []
      (let [proxy-uri (java.net.URI. (env :quotaguardstatic-url))
            auth-str (.getUserInfo proxy-uri)
            encoded-auth (.encodeToString (java.util.Base64/getEncoder) (.getBytes auth-str))]
        (client/get "https://ip.quotaguard.com"
          {:proxy-host (.getHost proxy-uri)
           :proxy-port (.getPort proxy-uri)
           :headers {"Proxy-Authorization" (str "Basic " encoded-auth)}})))
    

    Database Connections (SOCKS5)

    For non-HTTP protocols like PostgreSQL, MySQL, MongoDB, SFTP, or any TCP connection, use QGTunnel with the SOCKS5 proxy.

    What is QGTunnel?

    QGTunnel is a wrapper process that creates local port mappings to route traffic through QuotaGuard’s SOCKS5 proxy. It runs alongside your application and transparently proxies specified connections.

    Step 1: Download QGTunnel

    Download and extract QGTunnel to your app’s root directory:

    curl https://s3.amazonaws.com/quotaguard/qgtunnel-latest.tar.gz | tar xz
    

    This creates bin/qgtunnel and supporting files in vendor/nss_wrapper/.

    Step 2: Configure Tunnels in the Dashboard

    Open your QuotaGuard dashboard:

    heroku addons:open quotaguardstatic --app your-app-name
    

    Navigate to Settings > Setup > Tunnel > Create Tunnel.

    Example MySQL configuration:

    SettingValue
    Remote Destinationtcp://your-database.example.com:3306
    Local Port3306
    Transparenttrue
    Encryptedfalse

    Transparent mode overrides DNS so your app can connect to the original hostname while traffic routes through the tunnel. Without transparent mode, connect to 127.0.0.1:3306 instead.

    Encrypted mode adds end-to-end TLS encryption for protocols that don’t natively encrypt traffic (like Redis). Skip this if your protocol already uses TLS.

    Step 3: Update Your Procfile

    Prepend bin/qgtunnel to your existing commands:

    Before:

    web: bundle exec puma -C config/puma.rb
    

    After:

    web: bin/qgtunnel bundle exec puma -C config/puma.rb
    

    Step 4: Deploy

    Commit the QGTunnel files and deploy:

    git add bin/qgtunnel vendor/nss_wrapper/
    git commit -m "Add QGTunnel for database connections via static IP"
    git push heroku main
    

    By default, QGTunnel fetches configuration from the QuotaGuard API on startup. For production reliability, download the configuration file:

    1. In your QuotaGuard dashboard, go to Tunnel > Download configuration
    2. Save it as .qgtunnel in your project root
    3. Commit and deploy

    This removes the dependency on QuotaGuard’s API during app startup.

    Example: MySQL with Rails

    With QGTunnel configured for MySQL on port 3306 in transparent mode, your database.yml requires one addition:

    production:
      adapter: mysql2
      host: your-database.example.com
      database: your_database
      username: your_user
      password: <%= ENV['DATABASE_PASSWORD'] %>
      connect_timeout:
    

    The empty connect_timeout: forces the mysql2 adapter to use nil, which prevents connection hangs with the proxy.

    Example: PostgreSQL with Django

    DATABASES = {
        'default': {
            'ENGINE': 'django.db.backends.postgresql',
            'NAME': 'your_database',
            'USER': 'your_user',
            'PASSWORD': os.environ.get('DATABASE_PASSWORD'),
            'HOST': 'your-database.example.com',
            'PORT': '5432',
        }
    }
    

    With transparent mode enabled for your-database.example.com:5432, no code changes are required.

    Debugging QGTunnel

    Enable verbose logging:

    heroku config:set QGTUNNEL_DEBUG=true --app your-app-name
    

    Check your logs for QGTunnel output:

    heroku logs --tail --app your-app-name | grep qgtunnel
    

    Inbound Proxy (Reverse Proxy)

    QuotaGuard can also provide a static IP for inbound traffic to your Heroku app. This is useful when external services need to reach your app via a known IP address.

    This feature is available on Micro plans and above.

    Setting Up Inbound Proxy

    1. Open your QuotaGuard dashboard
    2. Navigate to Setup > Inbound
    3. Enter your Heroku app URL (e.g., https://your-app.herokuapp.com)
    4. QuotaGuard generates a unique URL like a62b1d0b4983db763450411fd393b3ce-us-east-1.getstatica.com

    This URL resolves to your two static IPs. External services can access your app through this URL, and you can allowlist both IPs in their firewalls.

    For custom domains, follow the SSL configuration instructions in the dashboard.

    Testing Your Implementation

    Verify your static IP configuration by requesting ip.quotaguard.com:

    curl -x $QUOTAGUARDSTATIC_URL https://ip.quotaguard.com
    

    Expected response:

    {"ip":"52.34.188.175"}
    

    The returned IP should match one of your two static IPs shown in the dashboard. Run it multiple times to see both IPs.

    From your application:

    import os
    import requests
    
    proxies = {
        "http": os.environ['QUOTAGUARDSTATIC_URL'],
        "https": os.environ['QUOTAGUARDSTATIC_URL']
    }
    
    res = requests.get("https://ip.quotaguard.com/", proxies=proxies)
    print(f"Traffic is routing through: {res.json()['ip']}")
    

    Latency Considerations

    Using QuotaGuard adds a network hop to your requests:

    ConfigurationAdded Latency
    Same region (Heroku US + QuotaGuard US-East)5-15ms
    Cross-region50-150ms

    QuotaGuard automatically provisions your proxy in the same region as your Heroku app to minimize latency. If you need to change regions, contact support.

    For latency-sensitive applications, verify your proxy region matches your Heroku app region.

    Local Development

    To test QuotaGuard locally, add the environment variable to your .env file:

    heroku config -s | grep QUOTAGUARDSTATIC_URL >> .env
    

    Then use Heroku Local to run your app:

    heroku local web
    

    Note: Local requests count against your monthly quota. Use local testing sparingly.

    Add .env to your .gitignore:

    echo .env >> .gitignore
    

    Managing Your Subscription

    Upgrading or Downgrading

    heroku addons:upgrade quotaguardstatic:large --app your-app-name
    

    Your static IPs remain the same when changing plans. No allowlist updates required.

    Checking Usage

    View your current usage in the dashboard:

    heroku addons:open quotaguardstatic --app your-app-name
    

    Removing the Add-on

    heroku addons:destroy quotaguardstatic --app your-app-name
    

    Warning: This immediately removes your static IPs. Update any external allowlists before removing.

    Troubleshooting

    407 Proxy Authentication Required

    Your credentials are incorrect or expired. Verify your QUOTAGUARDSTATIC_URL:

    heroku config:get QUOTAGUARDSTATIC_URL --app your-app-name
    

    If you recently changed plans, the URL may have reset. Check the dashboard for your current credentials.

    Connection Timeout

    1. Verify your Heroku app can reach external networks
    2. Check if the destination IP/port is correct
    3. For QGTunnel, enable debug mode: heroku config:set QGTUNNEL_DEBUG=true

    Wrong IP Address Returned

    1. Verify the proxy URL is being read correctly in your code
    2. Check that you’re applying the proxy to the specific request, not just setting an environment variable
    3. Some HTTP clients require explicit proxy configuration per-request

    QGTunnel Not Starting

    1. Verify bin/qgtunnel and vendor/nss_wrapper/ are committed to git
    2. Check that transparent mode dependencies are present if using transparent mode
    3. Enable debug logging and check Heroku logs

    MySQL Connection Hangs

    Add connect_timeout: (empty value) to your database.yml to prevent the mysql2 adapter from setting a timeout that conflicts with the proxy.

    Support

    For issues with QuotaGuard on Heroku:

    • Email: support@quotaguard.com
    • Phone: 1-702-818-1043
    • Dashboard: Submit a ticket via the support tab

    Average response time is under 2 hours. For urgent issues, call directly.


    Ready to Get Started?

    Get in touch or provision the add-on now.

    Add QuotaGuard to Your Heroku App

    View Pricing


    Ready to Get Started?

    Get in touch or create a free trial account