Site icon Efficient Coder

Fix Dify MCP Server 403 Forbidden Error: SSRF Proxy Blocking SSE Connection

How to Fix Dify MCP Server 403 Forbidden Error (SSE Connection Failed)

TL;DR: If Dify throws httpx.ProxyError: 403 Forbidden when connecting to an MCP Server, the request is being blocked by Dify’s built-in SSRF protection proxy (Squid). The fix is to whitelist your MCP Server’s address in squid.conf and restart the proxy container.


The Problem

When adding an MCP tool provider in the Dify console, you may encounter the following error in your API container logs:

api-1 | ERROR [sse_client.py:300] - Error connecting to SSE endpoint
...
httpcore.ProxyError: 403 Forbidden

The above exception was the direct cause of the following exception:
...
httpx.ProxyError: 403 Forbidden

The full stack trace points to ssrf_proxy_sse_connect inside sse_client.py:

File "/app/api/core/mcp/client/sse_client.py", line 286, in sse_client
    with ssrf_proxy_sse_connect(

This is triggered when Dify tries to authenticate and establish an SSE connection with the MCP Server via POST /console/api/workspaces/current/tool-provider/mcp/auth.


Root Cause: Dify’s SSRF Protection Proxy

Despite how the error looks, this is not a problem with your MCP Server itself. The connection is being blocked by Dify’s built-in SSRF (Server-Side Request Forgery) protection proxy.

What Is SSRF and Why Does Dify Block It?

SSRF is a class of security vulnerability where an attacker tricks a server into making HTTP requests to unintended destinations — often internal network resources that should never be publicly reachable. To prevent this, Dify routes all outbound HTTP requests from the API container through a Squid proxy, which filters requests against a set of access control rules.

When you enter an MCP Server URL in the Dify console, Dify’s API container tries to connect to that endpoint. If the destination address falls into a blocked category — such as a private IP range or loopback address — the Squid proxy denies the request with a 403 Forbidden response before it ever reaches your MCP Server.


What Triggers the 403?

Scenario Why It’s Blocked
MCP Server running on localhost or 127.0.0.1 Loopback addresses are blocked by default
MCP Server on a private IP (192.168.x.x, 10.x.x.x, 172.16–31.x.x) Private IP ranges are blocked by default
A public domain that resolves to a private IP DNS rebinding protection catches this too
Misconfigured SSRF proxy May block all outbound requests, including public ones

How to Fix It

Option 1: Whitelist Your MCP Server in squid.conf (Recommended)

This is the most targeted fix. It grants access only to the specific address you need, without weakening your overall security posture.

Locate the Squid configuration file in your Dify project:

docker/ssrf_proxy/squid.conf

Add an ACL rule for your MCP Server’s IP address or domain:

# Allow access to your internal MCP Server by IP
acl mcp_server dst 192.168.1.100
http_access allow mcp_server

# Or allow by domain name
acl mcp_domain dstdomain mcp.your-domain.com
http_access allow mcp_domain

After saving the file, restart the proxy container:

docker compose restart ssrf_proxy

Option 2: Disable the SSRF Proxy (Dev/Internal Environments Only)

If you’re running Dify in a controlled internal environment and don’t need the SSRF protection layer, you can disable it entirely by clearing the proxy environment variables in your .env file:

SSRF_PROXY_HTTP_URL=
SSRF_PROXY_HTTPS_URL=

Then restart the API service:

docker compose restart api

⚠️ Security Warning: Disabling the SSRF proxy removes a meaningful layer of protection. Do not use this approach in any internet-facing production deployment.

Option 3: Verify Your MCP Server Is Publicly Reachable

If your MCP Server is hosted on a public domain, rule out basic connectivity issues before touching the proxy config:

  1. Check DNS resolution — does the domain resolve to the expected IP?
    nslookup your-mcp-domain.com
    
  2. Check port availability — is the port open in your firewall or cloud security group?
  3. Test the SSE endpoint directly — can curl reach it?
    curl -N https://your-mcp-domain.com/sse
    

If all three checks pass but the 403 persists, the issue is definitely in the proxy configuration. Return to Option 1.


Troubleshooting Checklist

Use this checklist to systematically narrow down the cause:

□ Is the MCP Server URL using localhost or 127.0.0.1?
□ Is the MCP Server on a private IP subnet?
□ Is the ssrf_proxy container actually running?
      → docker compose ps
□ Does squid.conf have an ACL rule for this address?
□ Did you restart ssrf_proxy after updating the config?
□ Does the MCP Server's domain resolve to a private IP?

Summary

When Dify fails to connect to an MCP Server with httpx.ProxyError: 403 Forbidden, the root cause is almost always the same: Dify’s built-in SSRF protection proxy is blocking the outbound request. Once you understand that, the path forward is straightforward — whitelist the MCP Server address in squid.conf, or disable the proxy in environments where that tradeoff is acceptable.


Frequently Asked Questions

Does this error mean my MCP Server is misconfigured?
Not necessarily. The 403 is returned by Dify’s internal Squid proxy, not by your MCP Server. Your server may be perfectly healthy but simply unreachable due to the proxy rules.

Why does Dify use a proxy for outbound requests at all?
Dify is often self-hosted and may run alongside sensitive internal services. The SSRF proxy prevents a scenario where a malicious or misconfigured tool URL causes Dify to probe internal infrastructure.

Will whitelisting an IP affect other security rules?
No. Squid evaluates ACL rules top-to-bottom. Adding an allow rule for a specific IP only opens that one destination; all other existing rules remain unchanged.

Does this apply to both SSE and HTTP transport for MCP?
Yes. Both transports go through the same SSRF proxy layer in Dify, so the same fix applies regardless of which transport your MCP Server uses.

Exit mobile version