I installed 1Panel on my VPS, deployed Discourse (via container), and set up a reverse proxy using OpenResty (via container). The domain is hosted on Cloudflare with the CDN (orange cloud) enabled. Below is the solution to fix the issue where user IPs appear as coming from Cloudflare instead of the user’s actual browser IP.
1. Create a Scheduled Task in 1Panel to Download the Latest Cloudflare IP List Weekly and Save It to the OpenResty Configuration Directory.
-
Click “Scheduled Tasks” in the left menu bar of 1Panel.
-
Click “Create Scheduled Task” in the upper right corner.
-
Fill in the parameters as follows (you can copy and paste directly):
-
Task Type: Select Shell Script.
-
Task Name: For example,
Auto-Update Cloudflare IP Ranges. -
Execution Cycle: Recommended Weekly (e.g., every Saturday at 03:00 AM).
-
Script Content: Copy and paste the complete code below directly (Note: Please modify the container name and configuration directory path at the top of the code first):
#!/bin/bash
# Configuration Area
CONTAINER_NAME="OpenResty container name in 1Panel"
# Change to the specific website proxy directory
CONF_DIR="/opt/1panel/www/sites/www.your-domain.club/proxy"
echo "[$(date)] Starting to pull the latest IP ranges from Cloudflare official source..."
TEMP_DIR=$(mktemp -d)
# Pull and convert IP list to Nginx-recognizable format
curl -fsS https://www.cloudflare.com/ips-v4 | sed 's/.*/set_real_ip_from \&;/' > $TEMP_DIR/cf-ips-v4.conf
curl -fsS https://www.cloudflare.com/ips-v6 | sed 's/.*/set_real_ip_from \&;/' > $TEMP_DIR/cf-ips-v6.conf
# Check and move files
if [[ -s $TEMP_DIR/cf-ips-v4.conf ]] && [[ -s $TEMP_DIR/cf-ips-v6.conf ]]; then
mv $TEMP_DIR/cf-ips-v4.conf $CONF_DIR/
mv $TEMP_DIR/cf-ips-v6.conf $CONF_DIR/
echo "[$(date)] Configuration files successfully updated to $CONF_DIR."
else
echo "[$(date)] Error: Failed to pull Cloudflare IPs!"
rm -rf $TEMP_DIR
exit 1
fi
rm -rf $TEMP_DIR
# Test and reload Nginx
echo "[$(date)] Performing Nginx configuration test..."
if docker exec $CONTAINER_NAME nginx -t; then
docker exec $CONTAINER_NAME nginx -s reload
echo "[$(date)] Success! OpenResty configuration reloaded."
else
echo "[$(date)] Failure! Nginx configuration test failed."
exit 1
fi
Once the task is created, you don’t need to wait until Saturday to test it. Find the task you just created in the “Scheduled Tasks” list.
Click the “Report” button on the right. Check the log window that pops up. If the last few lines show Success! OpenResty configuration reloaded, it means the entire process has run perfectly within 1Panel!
2. Add realip Configuration to OpenResty
Create a new file in the host directory /opt/1panel/www/sites/www.your-domain/proxy/ (the filename can be anything, as long as it ends with .conf, it will be automatically loaded by the main configuration’s include *.conf): realip.conf
# Use the path inside the container
include /www/sites/www.your-domain/proxy/cf-ips-v4.conf;
include /www/sites/www.your-domain/proxy/cf-ips-v6.conf;
real_ip_header CF-Connecting-IP;
real_ip_recursive on;
3. Reload OpenResty
docker exec OpenResty_container_name_in_1Panel nginx -t
docker exec OpenResty_container_name_in_1Panel nginx -s reload
4. Verify if OpenResty is Obtaining the Real IP
Check the OpenResty access log:
tail -f /opt/1panel/www/sites/www.your-domain/log/access.log
If the configuration is correct, the first IP in the logs should change to your real public IP, rather than Cloudflare ranges like 173.245.x.x.
5. Discourse Side
Edit app.yml and add the following content inside the run: code block:
# After deploying Discourse via Docker, the following three sections must be added to ensure the forum user's real IP is passed
- file:
path: /etc/nginx/conf.d/outlets/server/set-real-ip-from-docker.conf
chmod: 644
contents: |
set_real_ip_from 172.16.0.0/12;
set_real_ip_from 127.0.0.1;
- file:
path: /etc/nginx/conf.d/outlets/server/real-ip-header.conf
chmod: 644
contents: |
real_ip_header x-forwarded-for;
- file:
path: /etc/nginx/conf.d/outlets/server/real-ip-recursive.conf
chmod: 644
contents: |
real_ip_recursive on;
This way, Discourse’s built-in Nginx will:
- Trust the Docker network range (
172.16.0.0/12) - Read the real IP from
x-forwarded-for - Recursively parse multiple layers of proxies
6. Notes
Adding - "templates/cloudflare.template.yml" to Discourse’s app.yml is only effective when Discourse faces Cloudflare directly. Since an OpenResty reverse proxy is added in between, the source IP seen by the Discourse container is actually the Docker gateway (e.g., 172.17.0.1 or 172.18.0.1), so the set_real_ip_from <CloudflareIP> in that template will not match.
Therefore, there is no need to add - "templates/cloudflare.template.yml" to Discourse’s app.yml.
Updated 2026-07-03