# Only allow Cloudflare IPs for Discourse server

**URL:** https://meta.discourse.org/t/only-allow-cloudflare-ips-for-discourse-server/70489
**Category:** Self-hosting
**Created:** [September 21, 2017, 6:01am UTC](https://meta.discourse.org/t/only-allow-cloudflare-ips-for-discourse-server/70489 "2017-09-21T06:01:47Z")
**Posts on this page:** 19
**Page:** 1

<div class="post-metadata">

### Author: ![Brock\_Busby](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/brock_busby/32/118746_2.png) [@Brock\_Busby](https://meta.discourse.org/u/Brock_Busby)
#### Post date: [September 21, 2017, 6:01am UTC](https://meta.discourse.org/t/only-allow-cloudflare-ips-for-discourse-server/70489/1 "2017-09-21T06:01:47Z")

</div>

All of our servers, site and Discourse community (discussions.ftw.in) sit behind Cloudflare.

Can we set at the Discourse server level to only allow Cloudflare IPs and deny all other IP traffic?

We’ve been under a DDoS layer 7 attack and learned our main site IP was exposed.  
To fix this, we updated our .htaccess on our main site server to only allow traffic from Cloudflare IPs and deny all other traffic. This does not let our origin server IP get exposed.

We are not exactly sure how to do the same above for our Discourse server at the server level. In my searches, I’ve seen cloudflare.template.yml mentioned and that it’s set to make sure we are seeing our user IPs, but I’m not sure.

Thanks in advance!

---

<div class="post-metadata">

### Author: ![codinghorror](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/codinghorror/32/110067_2.png) [@codinghorror](https://meta.discourse.org/u/codinghorror)
#### Post date: [September 21, 2017, 6:28am UTC](https://meta.discourse.org/t/only-allow-cloudflare-ips-for-discourse-server/70489/2 "2017-09-21T06:28:43Z")

</div>

This does not matter; once your IP is exposed it can be attacked and you must either

1. Move to a new IP

2. Get your upstream network provider to block the unwanted traffic (it is usually not https)

After that you must be very careful to not expose your IP at any time.

---

<div class="post-metadata">

### Author: ![michaeld](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/michaeld/32/1594_2.png) [@michaeld](https://meta.discourse.org/u/michaeld)
#### Post date: [September 21, 2017, 8:23am UTC](https://meta.discourse.org/t/only-allow-cloudflare-ips-for-discourse-server/70489/3 "2017-09-21T08:23:04Z")

</div>

> [@codinghorror](#):
>
> This does not matter; once your IP is exposed it can be attacked

Layer 7 attacks can be blocked in nginx by blocking everything but CF.

@Brock_Busby  
Here are some pointers, don’t know how to do it in Docker config though.

Make sure you are using an email provider, otherwise the emails will reveal your IP.

> **[Whitelisting CloudFlare in Nginx - Eric Helgeson](https://erichelgeson.github.io/blog/2014/01/18/whitelisting-cloudflare-in-nginx/)**
>
> I recently moved from Apache2 to Nginx as my web server of choice because of its low memory footprint so I can run it...

---

<div class="post-metadata">

### Author: ![Brock\_Busby](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/brock_busby/32/118746_2.png) [@Brock\_Busby](https://meta.discourse.org/u/Brock_Busby)
#### Post date: [September 21, 2017, 2:57pm UTC](https://meta.discourse.org/t/only-allow-cloudflare-ips-for-discourse-server/70489/4 "2017-09-21T14:57:46Z")

</div>

This is perfect @michaeld. Thank you! And yes, we are on SparkPost. Hopefully after this change it will only expose the Cloudflare IP.

> [@codinghorror](#):
>
> Move to a new IP
> 
> Get your upstream network provider to block the unwanted traffic (it is usually not https)

And correct. We will move to a new IP after denying only Cloudflare IPs and not allowing any other IPs.

Thanks all

---

<div class="post-metadata">

### Author: ![Sunny\_Nan](https://avatars.discourse-cdn.com/v4/letter/s/e79b87/32.png) [@Sunny\_Nan](https://meta.discourse.org/u/Sunny_Nan)
#### Post date: [September 21, 2017, 4:30pm UTC](https://meta.discourse.org/t/only-allow-cloudflare-ips-for-discourse-server/70489/5 "2017-09-21T16:30:16Z")

</div>

Hey everyone,

I am reading this thread and we need to know this information as well. We are running Discourse using the docker install on an Ubuntu server hosted on AWS.

How would we whitelist Cloudflare IPs with this build? I just ssh’d in and did not see any obvious NGINX paths within the container.

Any help would be greatly appreciated.

Best,

Sunny

---

<div class="post-metadata">

### Author: ![riking](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/riking/32/170938_2.png) [@riking](https://meta.discourse.org/u/riking)
#### Post date: [September 21, 2017, 4:41pm UTC](https://meta.discourse.org/t/only-allow-cloudflare-ips-for-discourse-server/70489/6 "2017-09-21T16:41:09Z")

</div>

Look at how `./templates/cloudflare.template.yml` downloads the IP list and constructs `set_real_ip_from` from that list.

So you’d copy or modify that file to also generate `allow` directives, and add a `deny all;`.

---

<div class="post-metadata">

### Author: ![Brock\_Busby](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/brock_busby/32/118746_2.png) [@Brock\_Busby](https://meta.discourse.org/u/Brock_Busby)
#### Post date: [September 21, 2017, 7:49pm UTC](https://meta.discourse.org/t/only-allow-cloudflare-ips-for-discourse-server/70489/7 "2017-09-21T19:49:26Z")

</div>

Also not being familiar with nginx and yaml, I followed your instructions @riking and created a copy named `./templates/cloudflare.allowdeny.yml` with the following code.

Can you please verify this will do the job? Much appreciated.

```plaintext
run:
  - file:
      path: /tmp/add-cloudflare-ips
      chmod: +x
      contents: |
        #!/bin/bash -e
        # Download list of CloudFlare ips
        wget https://www.cloudflare.com/ips-v4/ -O - > /tmp/cloudflare-ips
        wget https://www.cloudflare.com/ips-v6/ -O - >> /tmp/cloudflare-ips
        # Make into nginx commands and escape for inclusion into sed append command
        CONTENTS=$(</tmp/cloudflare-ips sed 's/^/allow /' | sed 's/$/;/' | tr '\n' '\\' | sed 's/\\/\\n/g')

        echo CloudFlare IPs:
        echo $(echo | sed "/^/a $CONTENTS")
        echo "deny all;"
        # Insert into discourse.conf
        sed -i "/sendfile on;/a $CONTENTS\nreal_ip_header CF-Connecting-IP;" /etc/nginx/conf.d/discourse.conf
        # Clean up
        rm /tmp/cloudflare-ips

  - exec: "/tmp/add-cloudflare-ips"
  - exec: "rm /tmp/add-cloudflare-ips"

```

---

<div class="post-metadata">

### Author: ![riking](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/riking/32/170938_2.png) [@riking](https://meta.discourse.org/u/riking)
#### Post date: [September 21, 2017, 7:54pm UTC](https://meta.discourse.org/t/only-allow-cloudflare-ips-for-discourse-server/70489/8 "2017-09-21T19:54:12Z")

</div>

Well, you’re going to need the other rules too, so we might as well only download the list once. You missed replacing real\_ip\_header with deny all. Here’s a combined version:

```plaintext
run:
  - file:
      path: /tmp/add-cloudflare-ips
      chmod: +x
      contents: |
        #!/bin/bash -e
        # Download list of CloudFlare ips
        wget https://www.cloudflare.com/ips-v4/ -O - > /tmp/cloudflare-ips
        wget https://www.cloudflare.com/ips-v6/ -O - >> /tmp/cloudflare-ips
        # Make into nginx commands and escape for inclusion into sed append command
        CONTENTS1=$(</tmp/cloudflare-ips sed 's/^/allow /' | sed 's/$/;/' | tr '\n' '\\' | sed 's/\\/\\n/g')
        CONTENTS2=$(</tmp/cloudflare-ips sed 's/^/set_real_ip_from /' | sed 's/$/;/' | tr '\n' '\\' | sed 's/\\/\\n/g')

        echo CloudFlare IPs:
        echo $(echo | sed "/^/a $CONTENTS1")

        # Insert into discourse.conf
        sed -i "/sendfile on;/a deny all;$CONTENTS1\n $CONTENTS2\nreal_ip_header CF-Connecting-IP;" /etc/nginx/conf.d/discourse.conf
        # Clean up
        rm /tmp/cloudflare-ips

  - exec: "/tmp/add-cloudflare-ips"
  - exec: "rm /tmp/add-cloudflare-ips"

```

Save that to your cloudflare.allowdeny and remove the stock version from your app.yml. And then test it, of course 🙂

---

<div class="post-metadata">

### Author: ![Brock\_Busby](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/brock_busby/32/118746_2.png) [@Brock\_Busby](https://meta.discourse.org/u/Brock_Busby)
#### Post date: [September 21, 2017, 8:00pm UTC](https://meta.discourse.org/t/only-allow-cloudflare-ips-for-discourse-server/70489/9 "2017-09-21T20:00:01Z")

</div>

This is great thanks @riking, but since you maintain `cloudflare.template.yml` I would not like to remove or mess with it, and let it download twice. …or does it download for every user or just when the container is built?

---

<div class="post-metadata">

### Author: ![riking](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/riking/32/170938_2.png) [@riking](https://meta.discourse.org/u/riking)
#### Post date: [September 21, 2017, 8:03pm UTC](https://meta.discourse.org/t/only-allow-cloudflare-ips-for-discourse-server/70489/10 "2017-09-21T20:03:29Z")

</div>

Nah, the file hasn’t changed in a while and I don’t think it will anytime soon. The primary priority here is to not break `git pull`, hence having a copy.

Yes, it’s only when the container is built; but the fastest code is the code that doesn’t run. Why download twice when you can do it once.

---

<div class="post-metadata">

### Author: ![Brock\_Busby](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/brock_busby/32/118746_2.png) [@Brock\_Busby](https://meta.discourse.org/u/Brock_Busby)
#### Post date: [September 21, 2017, 8:04pm UTC](https://meta.discourse.org/t/only-allow-cloudflare-ips-for-discourse-server/70489/11 "2017-09-21T20:04:25Z")

</div>

Got it. You rock. We will test and report back!

---

<div class="post-metadata">

### Author: ![Brock\_Busby](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/brock_busby/32/118746_2.png) [@Brock\_Busby](https://meta.discourse.org/u/Brock_Busby)
#### Post date: [September 21, 2017, 8:23pm UTC](https://meta.discourse.org/t/only-allow-cloudflare-ips-for-discourse-server/70489/12 "2017-09-21T20:23:52Z")

</div>

We are getting a 403 Forbidden on our sandbox which means the “deny all” is working. 😉  
I can confirm we are proxied through Cloudflare.

Two things @riking

1. Should “deny all;” come _after_ $CONTENTS1, since that is the list of allow?
2. I noticed you output $CONTENTS1 twice, but $CONTENTS2 only once

---

<div class="post-metadata">

### Author: ![riking](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/riking/32/170938_2.png) [@riking](https://meta.discourse.org/u/riking)
#### Post date: [September 21, 2017, 8:24pm UTC](https://meta.discourse.org/t/only-allow-cloudflare-ips-for-discourse-server/70489/13 "2017-09-21T20:24:56Z")

</div>

The echo is printed to the console during rebuild and isn’t saved anywhere.

I don’t know about the order; I remember seeing someone have deny all first followed by allows so that’s what I did.

---

<div class="post-metadata">

### Author: ![Brock\_Busby](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/brock_busby/32/118746_2.png) [@Brock\_Busby](https://meta.discourse.org/u/Brock_Busby)
#### Post date: [September 21, 2017, 10:57pm UTC](https://meta.discourse.org/t/only-allow-cloudflare-ips-for-discourse-server/70489/14 "2017-09-21T22:57:56Z")

</div>

We couldn’t get this to work. Kept getting a 403. We tried moving “deny all;” around and we even tried breaking up the two files and still wouldn’t work.

We are on AWS and realized we can manage using the security group to allow and deny. This is working for us right now.

Appreciate the help though. I think a nice-to-have for those on Cloudflare is an option to turn on to whitelist only Cloudflare IPs through the method above. Perhaps an admin checkbox. It makes a lot of sense and great addition for security. And honestly a nice selling point!

Next thing we need to solve though… our IP is still showing up in emails. We are using SparkPost as our email vendor so we are scratching our heads why it’s still showing.

---

<div class="post-metadata">

### Author: ![pain](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/pain/32/89202_2.png) [@pain](https://meta.discourse.org/u/pain)
#### Post date: [September 22, 2017, 3:15am UTC](https://meta.discourse.org/t/only-allow-cloudflare-ips-for-discourse-server/70489/15 "2017-09-22T03:15:18Z")

</div>

they probably got your ip by posting an ip logger  
my post mentioned here where i had same problem [https://meta.discourse.org/t/disable-discourse-from-crawling-links/69778](https://meta.discourse.org/t/disable-discourse-from-crawling-links/69778)

---

<div class="post-metadata">

### Author: ![AMSTim](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/amstim/32/63242_2.png) [@AMSTim](https://meta.discourse.org/u/AMSTim)
#### Post date: [September 22, 2017, 8:33pm UTC](https://meta.discourse.org/t/only-allow-cloudflare-ips-for-discourse-server/70489/16 "2017-09-22T20:33:40Z")

</div>

I don’t use AWS, but block access to our instance using firewall rules to filter out IPs, not on an approved list. Not sure what AWS offers in terms of firewall support.

I am using PFSense, and it can pull a list of IPs from a URL at an interval. I have scripts that create the approved IP list for PFsense to pull. IPs on the list make it to the forum, IPs not on the list are redirected to a page explaining the block.

I use this same technique to block access to Atlassian Confluence instance. Since it’s a firewall rule you can block any type of traffic you want, to any destination you want. Very flexible.

---

<div class="post-metadata">

### Author: ![system](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/system/32/443519_2.png) [@system](https://meta.discourse.org/u/system)
#### Post date: [October 15, 2019, 1:37pm UTC](https://meta.discourse.org/t/only-allow-cloudflare-ips-for-discourse-server/70489/17 "2019-10-15T13:37:40Z")

</div>



---

<div class="post-metadata">

### Author: ![system](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/system/32/443519_2.png) [@system](https://meta.discourse.org/u/system)
#### Post date: [June 21, 2023, 5:20pm UTC](https://meta.discourse.org/t/only-allow-cloudflare-ips-for-discourse-server/70489/18 "2023-06-21T17:20:56Z")

</div>



---

<div class="post-metadata">

### Author: ![Stephen](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/stephen/32/95011_2.png) [@Stephen](https://meta.discourse.org/u/Stephen)
#### Post date: [June 21, 2023, 6:22pm UTC](https://meta.discourse.org/t/only-allow-cloudflare-ips-for-discourse-server/70489/19 "2023-06-21T18:22:16Z")

</div>


