keepcalculator

06 Numbers and Powers

Moodle nginx Cloudflare TLS proxy and upload settings

Card No. 06·68654 · Updated · Standard formula, shown below

This article explains how to configure Moodle behind an nginx reverse proxy with Cloudflare TLS termination, and how to set the upload limits correctly so file submissions work. You will learn the exact nginx and Moodle settings, the Cloudflare proxy caveats, and the most common failure points, with step-by-step fixes.

Background: why Moodle behind nginx and Cloudflare needs special settings

Moodle is a PHP application that relies on the web server for several things: HTTPS termination, request size limits, and redirect generation. When you put nginx in front of Moodle and Cloudflare in front of nginx, two problems appear. First, Moodle sees HTTP from nginx (unless you set the X-Forwarded-Proto header), so it generates http:// links and may complain about insecure connections. Second, Cloudflare has a default request body limit of 100 MB on free plans, and nginx has its own client_max_body_size default of 1 MB. If any of these limits is lower than your Moodle upload max, file uploads will fail with a generic error.

How the proxy chain works and where the TLS ends

In a typical setup, the client connects to Cloudflare over HTTPS. Cloudflare decrypts the request and forwards it to your nginx server over HTTP (or HTTPS if you use Full (strict) mode). nginx then proxies to PHP-FPM, which runs Moodle. The key point is that TLS terminates at Cloudflare, not at nginx, unless you configure nginx with its own certificate and use Cloudflare’s Full (strict) mode. For Moodle to know the original scheme, you must pass the X-Forwarded-Proto header from Cloudflare through nginx to PHP.

Here is the minimal nginx server block for a Moodle site behind Cloudflare:

server {
    listen 80;
    server_name moodle.example.com;
    client_max_body_size 200M;

    location / {
        proxy_pass http://127.0.0.1:8080;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

Notice that client_max_body_size is set to 200M. This must be larger than the largest file you allow in Moodle. If you use Cloudflare’s free plan, the 100 MB limit applies, so you cannot upload files larger than 100 MB unless you upgrade to a paid plan or bypass the proxy for uploads (not recommended for security).

Moodle upload settings: the three limits you must set

Moodle has three separate upload limits, and all three must be high enough for your largest file:

  • PHP upload_max_filesize – the maximum size of a single file PHP will accept. Default is often 2M or 8M.
  • PHP post_max_size – the maximum size of the entire POST request, which includes all files plus form fields. Set this at least 20% larger than the largest file.
  • Moodle’s maxbytes – set in Site administration → Security → Site policies. This is the per-file limit Moodle enforces.

For example, to allow 200 MB files, set in your php.ini:

upload_max_filesize = 200M
post_max_size = 220M
memory_limit = 256M

Then in Moodle, set maxbytes to 200 MB. Also check the Site administration → Plugins → Activity modules → Assignment settings, because each assignment activity can override the site limit.

If you are using nginx, also verify that client_max_body_size is set in the server or location block. A common mistake is setting it only in the main http block but forgetting that a server block overrides it.

Configuring nginx to pass the correct scheme and IP

When Cloudflare is in front, nginx receives requests on port 80 (or 443 if you use full SSL). To make Moodle generate HTTPS links and log the real visitor IP, you need to:

  1. Set proxy_set_header X-Forwarded-Proto $scheme; – this tells Moodle the original protocol was HTTPS.
  2. Set proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; – this preserves the client IP chain.
  3. In Moodle’s config.php, add the following lines to trust the proxy headers:
$CFG->sslproxy = true;
$CFG->reverseproxy = true;

The sslproxy setting makes Moodle assume the connection is secure when the X-Forwarded-Proto header is https. The reverseproxy setting tells Moodle to use the X-Forwarded-For header for the client IP. Without these, Moodle may show a “not secure” warning or log the nginx server IP as the user’s IP.

Cloudflare proxy settings that affect uploads

Cloudflare sits between the user and nginx, and its settings can break uploads in subtle ways:

  • Request body size limit – On free plans, the limit is 100 MB. Paid plans allow up to 500 MB. If you need larger files, you must either upgrade or use a different upload method (e.g., a separate non-proxied subdomain).
  • SSL/TLS encryption mode – Use Full (strict) if you have a valid certificate on nginx. If you use Flexible, Cloudflare connects to nginx over HTTP, which is fine for a test site but insecure for production. The X-Forwarded-Proto header will still say https because Cloudflare sends that header.
  • Cloudflare caching – Moodle pages are dynamic, so you should not cache HTML pages. Use a Page Rule to bypass cache for /login/* and /course/* or set the cache level to “No Query String” or “Standard”. Uploads themselves are not cached, but the response after upload might be.
  • Cloudflare firewall rules – Some security rules may block large POST requests. Check the firewall analytics if uploads fail with a 403 or 524 error.

Common problems and how to fix them

Here are the most frequent issues and their solutions:

  • “File exceeds maximum size” – Check all three limits: PHP, nginx, and Moodle. Use phpinfo() to verify the PHP values, and test with a small file first.
  • Upload fails with 413 Request Entity Too Large – This is nginx’s client_max_body_size. Increase it and reload nginx.
  • Upload fails with 524 Timeout – Cloudflare times out after 100 seconds for free plans. Large files may take longer. You can try increasing PHP max_execution_time and post_max_size, but the real fix is to use a paid Cloudflare plan or a different upload path.
  • Moodle shows “Not secure” or redirects to HTTP – Set $CFG->sslproxy = true; and ensure the X-Forwarded-Proto header is passed.
  • User IP is always the same – Set $CFG->reverseproxy = true; and make sure nginx passes X-Forwarded-For.

Comparing nginx proxy vs. direct Apache or IIS

Many Moodle admins use Apache or IIS directly. The main difference is that nginx is lighter and handles static files better, but it requires a separate PHP-FPM process. The proxy headers are the same concept: you must set X-Forwarded-Proto and X-Forwarded-For. In Apache, you would use mod_proxy and RequestHeader set X-Forwarded-Proto https. In IIS, you would use URL Rewrite and the ARR module. The advantage of nginx is its explicit client_max_body_size, which is easier to reason about than Apache’s LimitRequestBody (which is often unset by default).

Costs and alternatives for large uploads

If you need to support files larger than 100 MB, you have a few options:

  • Cloudflare paid plan – Pro plan allows up to 100 MB, Business up to 200 MB, Enterprise up to 500 MB. This is the simplest if you want to keep Cloudflare in front.
  • Separate upload subdomain – Create upload.example.com that bypasses Cloudflare (grey cloud). This allows unlimited size but loses Cloudflare’s security features for that subdomain.
  • Use a file storage service – Moodle can use external repositories like Amazon S3 or Google Drive. The upload goes directly to the storage service, bypassing Cloudflare and nginx limits. This is the most scalable solution for very large files.

For most educational sites, 100 MB is enough for documents and images. Video files are usually better hosted on a streaming platform or a separate media server.

Frequently Asked Questions

Why does my Moodle upload fail with a 524 error when using Cloudflare?

A 524 error means Cloudflare connected to your nginx server but did not receive a response within 100 seconds. This usually happens with large files that take longer to upload or process. Increase PHP max_execution_time and post_max_size, and consider using a paid Cloudflare plan that allows longer timeouts.

Do I need to set sslproxy in Moodle if I use Cloudflare?

Yes, if Cloudflare terminates TLS and forwards to nginx over HTTP, Moodle will see HTTP and generate insecure links. Setting $CFG->sslproxy = true; tells Moodle to trust the X-Forwarded-Proto header and treat the connection as HTTPS.

What is the difference between upload_max_filesize and post_max_size?

upload_max_filesize limits the size of a single file. post_max_size limits the entire POST request, which includes all files plus form fields. If you upload two 100 MB files, the POST size is over 200 MB, so post_max_size must be larger than the sum of all files.

Can I set client_max_body_size to 0 to allow unlimited uploads?

Technically yes, but it is not recommended because it allows denial-of-service attacks. Always set a reasonable limit that matches your largest allowed file, and increase it only when needed.

Why does Moodle show the wrong client IP when behind Cloudflare?

Because nginx sees the Cloudflare IP, not the visitor’s IP. You must pass the X-Forwarded-For header and set $CFG->reverseproxy = true; in Moodle’s config.php. Also ensure nginx is not overwriting the header.

Do I need to configure Cloudflare caching for Moodle?

Yes, you should avoid caching dynamic Moodle pages. Use a Page Rule to bypass cache for /login/* and /course/*, or set the cache level to “No Query String”. Otherwise, users may see stale pages or login issues.

Related

Method: the standard formula for this calculation, rounded as each result states. See how the calculators work or report an error.

More cards in this drawer

The whole Numbers and Powers drawer →