API Rate Limiting and Security for Self-Hosted Supabase

Learn how to configure API rate limiting and security for self-hosted Supabase, including Kong setup, key management, and best practices.

Cover Image for API Rate Limiting and Security for Self-Hosted Supabase

When you deploy Supabase on your own server, you inherit full responsibility for protecting your API from abuse. Unlike Supabase Cloud, where rate limiting and security measures come pre-configured, self-hosted deployments require manual setup. This guide walks through configuring API rate limiting and security hardening for production self-hosted Supabase instances.

Why API Security Matters for Self-Hosted Deployments

Self-hosted Supabase exposes a RESTful API via PostgREST—a thin layer directly on top of PostgreSQL. Without proper rate limiting, a single malicious actor can exhaust your database connections, spike your server costs, or perform brute-force attacks against your authentication endpoints.

The consequences of inadequate API security include:

  • Database connection exhaustion: PostgreSQL has finite connections, and API abuse can quickly consume them all
  • Resource starvation: Uncontrolled requests can overwhelm your CPU and memory
  • Authentication attacks: Without rate limits, brute-force login attempts become trivial
  • Data exfiltration: Attackers can scrape your entire database through unprotected endpoints

Recent Supabase security updates in 2025-2026 have introduced new API key models with publishable keys and revocable secrets, but these features aren't yet available for self-hosted deployments. This means you'll need to implement equivalent protections yourself.

Understanding the Self-Hosted API Architecture

Supabase uses Kong as an API gateway in front of PostgREST. This architecture provides the foundation for implementing rate limiting, but the default configuration leaves much of the security work to you.

Key Components

Client Request → Kong (API Gateway) → PostgREST → PostgreSQL
                     ↓
              Rate Limiting
              Key Authentication
              Request Transformation

Kong sits between incoming requests and your database, making it the natural place to implement rate limiting and security policies. However, the default Docker Compose setup doesn't include aggressive rate limiting out of the box.

Configuring Kong Rate Limiting

Kong's rate limiting plugin restricts the number of requests a client can make within a time window. Here's how to configure it for your self-hosted Supabase deployment.

Step 1: Access Your Kong Configuration

In the default Supabase Docker setup, Kong's configuration lives in kong.yml. Locate this file in your deployment:

# Typically found at
./volumes/api/kong.yml

Step 2: Add Rate Limiting Plugin

Add the rate limiting plugin to your Kong configuration:

plugins:
  - name: rate-limiting
    config:
      minute: 100
      hour: 1000
      policy: local
      fault_tolerant: true
      hide_client_headers: false

This configuration allows 100 requests per minute and 1,000 per hour per client. Adjust these values based on your expected traffic patterns.

Step 3: Apply Per-Route Limits

Different endpoints warrant different rate limits. Authentication endpoints, for instance, should be more restrictive:

services:
  - name: auth-v1
    plugins:
      - name: rate-limiting
        config:
          minute: 10
          hour: 100
          policy: local

This limits authentication requests to 10 per minute—enough for legitimate users but restrictive enough to slow brute-force attacks.

Step 4: Configure IP-Based Limiting

For accurate client identification behind proxies, configure the Sb-Forwarded-For header handling:

# In your environment configuration
GOTRUE_SECURITY_SB_FORWARDED_FOR_ENABLED=true
GOTRUE_RATE_LIMIT_HEADER=X-Forwarded-For

This ensures rate limits apply per client IP rather than per proxy server.

Implementing Database-Level Rate Limiting

Kong-level rate limiting protects against API abuse, but you can add a second layer of protection at the database level. This approach, documented in Supabase's security guides, uses PostgreSQL to track and limit requests.

Creating the Rate Limits Table

CREATE SCHEMA IF NOT EXISTS private;

CREATE TABLE private.rate_limits (
  id BIGSERIAL PRIMARY KEY,
  ip_address TEXT NOT NULL,
  created_at TIMESTAMPTZ DEFAULT NOW()
);

CREATE INDEX idx_rate_limits_ip_created 
ON private.rate_limits (ip_address, created_at);

Creating the Rate Limit Check Function

CREATE OR REPLACE FUNCTION public.check_rate_limit()
RETURNS VOID AS $$
DECLARE
  client_ip TEXT;
  request_count INT;
BEGIN
  -- Get client IP from request headers
  client_ip := current_setting('request.headers', true)::json->>'x-forwarded-for';
  
  -- Count requests in the last 5 minutes
  SELECT COUNT(*) INTO request_count
  FROM private.rate_limits
  WHERE ip_address = client_ip
    AND created_at > NOW() - INTERVAL '5 minutes';
  
  IF request_count > 100 THEN
    RAISE EXCEPTION 'Rate limit exceeded' 
      USING ERRCODE = 'P0001';
  END IF;
  
  -- Log this request
  INSERT INTO private.rate_limits (ip_address)
  VALUES (client_ip);
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;

This function can be called from database triggers on sensitive tables. Note that database-level rate limiting only works for POST, PUT, PATCH, and DELETE requests—GET requests served by read replicas can't write to the rate limits table.

API Key Management for Self-Hosted Deployments

Self-hosted Supabase uses JWT-based keys (anon and service_role) rather than the newer publishable/secret key model available on Supabase Cloud. This means you need to be especially careful about key security.

Key Types and Usage

KeyPurposeExposure
ANON_KEYClient-side API accessPublic (protected by RLS)
SERVICE_ROLE_KEYServer-side admin accessNever expose publicly
JWT_SECRETSigns all JWTsNever expose publicly

Securing Your Environment File

Your .env file contains all sensitive keys. For production deployments:

# Restrict file permissions
chmod 600 .env

# Use a secrets manager in production
# AWS Secrets Manager, HashiCorp Vault, etc.

Consider using Vault for secrets management to automate key rotation and access control.

Key Rotation Strategy

Unlike Supabase Cloud, self-hosted deployments don't have built-in key rotation. Implement your own rotation process:

  1. Generate new keys: Create new JWT secret and API keys
  2. Deploy in parallel: Configure both old and new keys to work simultaneously
  3. Update clients: Gradually migrate clients to new keys
  4. Monitor usage: Watch for requests using old keys
  5. Revoke old keys: Remove old keys once migration is complete

For JWT secrets specifically, wait at least one hour plus your token expiry time before revoking old secrets to avoid signing out active users.

Row-Level Security: Your Last Line of Defense

Even with rate limiting and key management in place, Row-Level Security (RLS) remains your most important protection. If a key is compromised, RLS prevents unauthorized data access.

Verifying RLS Status

Check that RLS is enabled on all tables:

SELECT 
  schemaname,
  tablename,
  rowsecurity
FROM pg_tables
WHERE schemaname = 'public';

Any table showing rowsecurity = false is exposed via the API without protection. See our complete RLS guide for implementation details.

Common RLS Mistakes

The most frequent security issues we see in self-hosted deployments:

  1. RLS disabled during development: Forgetting to enable it before production
  2. Service role key in client code: Bypasses all RLS policies
  3. Overly permissive policies: Using true as the check expression
  4. Missing policies on new tables: Adding tables without corresponding RLS rules

Monitoring and Alerting

Rate limiting is only effective if you can see when it's being triggered. Set up monitoring for your API security:

Kong Metrics

Enable Prometheus metrics in Kong to track:

plugins:
  - name: prometheus
    config:
      status_code_metrics: true
      latency_metrics: true
      bandwidth_metrics: true

Key Metrics to Monitor

  • Rate limit violations: Spike in 429 responses indicates attack or misconfiguration
  • Authentication failures: Multiple failed logins from same IP
  • API latency: Sudden increases may indicate resource exhaustion
  • Connection pool usage: PostgreSQL connections approaching limits

For comprehensive monitoring setup, see our observability guide.

How Supascale Simplifies API Security

Configuring rate limiting, key management, and monitoring for self-hosted Supabase requires significant DevOps expertise. Supascale handles much of this complexity:

  • Automated configuration: Security best practices applied by default
  • OAuth provider management: Configure Google, GitHub, Discord authentication through a simple UI
  • Backup security: Your backups are encrypted and stored securely in your S3-compatible storage
  • One-time pricing: Pay $39.99 once for unlimited projects—no ongoing security management costs

For teams that want the cost benefits of self-hosting without the security configuration burden, Supascale provides a middle path between fully managed and DIY deployments.

Conclusion

API security for self-hosted Supabase requires attention to multiple layers: Kong rate limiting, database-level protections, key management, and Row-Level Security. While Supabase Cloud handles these concerns for you, self-hosted deployments put this responsibility squarely on your team.

Start with Kong rate limiting to prevent obvious abuse, then layer in database-level checks for sensitive operations. Treat your API keys with the same care as database credentials, and verify RLS is enabled on every table before going to production.

The security configuration described here represents baseline protection. For high-stakes deployments handling sensitive data, consider additional measures like Web Application Firewalls (WAF), intrusion detection, and regular security audits.

Ready to simplify your self-hosted Supabase security? Check out Supascale's pricing for a one-time purchase that includes automated security configuration.


Further Reading