Preventing SSRF in NGINX

Server Side Request Forgery is one of the most dangerous vulnerabilities in NGINX. Learn how it happens and how to prevent it.

Updated: January 2025 8 min read

What is SSRF?

Server Side Request Forgery (SSRF) is a vulnerability where an attacker can make your server send HTTP requests to unintended destinations. In NGINX, this typically happens when user-controlled input is used in proxy_pass directives.

⚠️ Why SSRF is Critical

SSRF can allow attackers to:

  • Access internal services (databases, admin panels, cloud metadata)
  • Bypass firewalls and network segmentation
  • Steal cloud credentials (AWS, GCP, Azure metadata endpoints)
  • Scan internal networks

How SSRF Happens in NGINX

SSRF occurs when variables that can be controlled by attackers are used to determine where NGINX proxies requests. The most common vulnerable pattern:

location /proxy/ {
    # $host is taken from the HTTP Host header - attacker controlled!
    proxy_pass http://$host/;
}

An attacker can exploit this by sending:

GET /proxy/admin HTTP/1.1
Host: internal-admin-panel.local

NGINX will then proxy the request to http://internal-admin-panel.local/admin, potentially exposing internal services.

Attacker → [Host: internal-db] → NGINX
NGINX → [proxy_pass http://internal-db/] → Internal Database
The attacker controls where NGINX sends the request

Dangerous Variables

These NGINX variables can be controlled by attackers and should never be used in proxy_pass:

  • $host — From the Host header or server_name
  • $http_host — Directly from the Host header
  • $request_uri — The full original URI including query string
  • $uri — The normalized URI (can be manipulated)
  • $arg_* — Query string parameters
  • $http_* — Any HTTP header
  • Capture groups from regex locations without validation

Vulnerable Patterns

Pattern 1: Dynamic Host

# Attacker controls the backend via Host header
location /api/ {
    proxy_pass http://$host$request_uri;
}

Pattern 2: Query Parameter Backend

# Attacker controls backend via ?target= parameter
location /fetch {
    proxy_pass http://$arg_target;
}

Pattern 3: Path-Based Backend

# Attacker can inject @internal into the path
location ~ ^/service/(?[^/]+)/ {
    proxy_pass http://$backend:8080;
}

Pattern 4: Header-Based Routing

# Attacker sets X-Backend-Server header
location /internal/ {
    proxy_pass http://$http_x_backend_server;
}

Secure Patterns

Use Hardcoded Backends

# Backend is fixed, not user-controlled
location /api/ {
    proxy_pass http://backend-server:8080;
}

Use Upstream Groups

upstream api_backends {
    server backend1.internal:8080;
    server backend2.internal:8080;
}

location /api/ {
    proxy_pass http://api_backends;
}

Whitelist with Map

# Only allow known backends
map $arg_service $backend {
    default         "";
    "users"         "users-service:8080";
    "orders"        "orders-service:8080";
    "products"      "products-service:8080";
}

location /api/ {
    if ($backend = "") {
        return 400;
    }
    proxy_pass http://$backend;
}

Validate Regex Captures

# Validate capture groups against allowed values
location ~ ^/v1/(?v[12])/ {
    # $version can only be "v1" or "v2" - safe
    proxy_pass http://api-$version.internal;
}

Detecting SSRF with Gixy

Gixy automatically detects SSRF vulnerabilities in your NGINX configuration:

$ gixy /etc/nginx/nginx.conf

==================== Results ====================

⚠ [ssrf] Server Side Request Forgery
  Severity: HIGH
  Description: Using variables that can contain "\n" or be
               controlled by an attacker in proxy_pass may
               lead to SSRF vulnerabilities.
  Reason: At least variable "$host" can be controlled by
          an attacker via the Host header.
  Pseudo config:
      server {
          location /api/ {
              proxy_pass http://$host$request_uri;
          }
      }
  File: /etc/nginx/conf.d/api.conf
  Line: 15

==================== Summary ====================
Total issues: 1 (High: 1)
💡 Best Practice: Run gixy in your CI/CD pipeline to catch SSRF vulnerabilities before they reach production. Use gixy -lll to fail builds on HIGH severity issues only.

Cloud Metadata Attacks

SSRF is especially dangerous in cloud environments. Attackers can target metadata endpoints to steal credentials:

  • AWS: http://169.254.169.254/latest/meta-data/
  • GCP: http://metadata.google.internal/computeMetadata/v1/
  • Azure: http://169.254.169.254/metadata/instance

Even if your NGINX config seems safe, always block these IPs as defense in depth:

# Block cloud metadata endpoints
location / {
    # Deny requests trying to access metadata
    if ($http_host ~* "169\.254\.169\.254|metadata\.google\.internal") {
        return 403;
    }
    
    # Your normal proxy config
    proxy_pass http://backend;
}

Checklist

  • ✓ Never use $host, $http_host, or $http_* in proxy_pass
  • ✓ Use hardcoded backends or upstream groups
  • ✓ Whitelist allowed values with map directives
  • ✓ Validate regex capture groups before use
  • ✓ Block cloud metadata IP ranges
  • ✓ Run Gixy in CI/CD to catch issues automatically

Further Reading