Allowing Moodle cURL Requests to Localhost for Development
How to allow Moodle's cURL wrapper to reach localhost services during development, and what security implications to be aware of.
You’re building a Moodle plugin that calls a service on your local machine — a REST API, a microservice, an AI backend, a webhook handler. You send the request through Moodle’s built-in cURL wrapper. Nothing comes back. No connection error, just silence, or a vague failure in your plugin’s response.
The cause: Moodle blocks outbound cURL requests to localhost (127.0.0.1, ::1) by default. This is intentional — it guards against Server-Side Request Forgery (SSRF), where an attacker tricks the server into fetching internal resources. On production that protection is essential. On a local dev machine it just gets in the way.
This guide shows how to lift that restriction.
What you’ll see
When a request is blocked, Moodle sets the curl error to “The URL is blocked.” and returns that string as the response body (not false or an empty response), and it triggers a coreeventurl_blocked event in the logs. If you only check the response body and not $curl->error, the failure can look like an empty or unexpected result, which is why this is easy to misdiagnose.
Fix: admin UI (recommended)
Go to Site administration > Security > HTTP security.
Step 1 — cURL blocked hosts list: clear this field entirely. When empty, Moodle skips the reserved-address check that blocks localhost.
Step 2 — cURL allowed ports list: confirm ports 80 and 443 are listed, then add the port your local service uses (e.g., 8000). If this list contains any entries at all, Moodle blocks every port not listed.
Step 3: click Save changes, then go to Site administration > Development > Purge all caches.
Fix: config.php (alternative)
If you can’t access the admin UI, or want a scripted setup, add these lines to config.php after the require_once(__DIR__ . '/lib/setup.php'); line:
$CFG->curlsecurityblockedhosts = ''; // empty = localhost not blocked
$CFG->curlsecurityallowedport = "80n443n8000"; // one port per line, NOT comma-separated
Moodle parses both of these settings by splitting on newlines, never commas, so each value must be on its own line. A comma-separated string is treated as a single invalid entry, which for the port allowlist means every port (including 80 and 443) gets blocked, the opposite of what you want. The simplest way to avoid the quoting pitfall is to set them in the UI on the HTTP security page (one host/port per line) rather than in config.php. Then purge caches.
Verify
Retry the cURL call from your plugin. If it still fails, enable developer debugging (Site administration > Development > Debugging, set to DEVELOPER) and check the output for messages from curl_security_helper.
Don’t carry this into production
An empty blocked hosts list disables SSRF protection for all local addresses. On a production server, the list should contain at minimum:
127.0.0.0/8
::1/128
10.0.0.0/8
172.16.0.0/12
192.168.0.0/16
Only remove entries if you have a specific, trusted internal service and understand the exposure.
Solin specializes in Moodle development environments and plugin testing.
Contact us