QuotaGuard and Heroku Integration Guide
Table of contents
- 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:
| Product | Use Case | Protocol | Best For |
|---|---|---|---|
| QuotaGuard | Dynamic IPs | HTTP | Bypassing API rate limits shared across Heroku apps |
| QuotaGuard Static | Static IPs | HTTP/SOCKS5 | Firewall allowlisting, API integrations |
| QuotaGuard Shield | Static IPs (HIPAA/PCI) | HTTPS/SOCKS5 over TLS | Regulated 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 Region | QuotaGuard Proxy |
|---|---|
| us (Virginia) | US-East |
| eu (Ireland/Frankfurt) | EU |
| sydney | AP-Southeast-2 |
| tokyo | AP-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:
| Setting | Value |
|---|---|
| Remote Destination | tcp://your-database.example.com:3306 |
| Local Port | 3306 |
| Transparent | true |
| Encrypted | false |
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
Step 5: Harden Your Setup (Recommended)
By default, QGTunnel fetches configuration from the QuotaGuard API on startup. For production reliability, download the configuration file:
- In your QuotaGuard dashboard, go to Tunnel > Download configuration
- Save it as
.qgtunnelin your project root - 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
- Open your QuotaGuard dashboard
- Navigate to Setup > Inbound
- Enter your Heroku app URL (e.g.,
https://your-app.herokuapp.com) - 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:
| Configuration | Added Latency |
|---|---|
| Same region (Heroku US + QuotaGuard US-East) | 5-15ms |
| Cross-region | 50-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
- Verify your Heroku app can reach external networks
- Check if the destination IP/port is correct
- For QGTunnel, enable debug mode:
heroku config:set QGTUNNEL_DEBUG=true
Wrong IP Address Returned
- Verify the proxy URL is being read correctly in your code
- Check that you’re applying the proxy to the specific request, not just setting an environment variable
- Some HTTP clients require explicit proxy configuration per-request
QGTunnel Not Starting
- Verify
bin/qgtunnelandvendor/nss_wrapper/are committed to git - Check that transparent mode dependencies are present if using transparent mode
- 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