Cloudflare Configuration guide Beginner

How to Point a Cloudflare Domain at Your VPS

Create the A record, pick the right proxy status, set the SSL mode to Full (strict) and lock the origin so only Cloudflare can reach it — plus what errors 521, 522, 525 and 526 actually mean.

A Cloudflare DNS record row with the orange proxy cloud enabled

Tested with

CloudflareDashboard
July 2026
OperatingSystem
Ubuntu 24.04 LTS
Nginx
1.24
OriginCertificate
Cloudflare Origin CA, RSA 2048

Before you start

  • A domain already using Cloudflare nameservers
  • A VPS with a public IPv4 address and a web server listening on 443
On this page

Two settings decide whether this works: the proxy status on the DNS record, and the SSL/TLS encryption mode on the zone. Get those wrong and you get a redirect loop or a 5xx error page that looks like your server is down when it is not.

This assumes your domain already uses Cloudflare’s nameservers and shows as Active in the dashboard.

What this sets up

Visitors resolve app.example.com to a Cloudflare edge IP. Cloudflare terminates TLS, applies caching and WAF rules, then opens a second TLS connection to your VPS. Your origin IP is not in public DNS, and your firewall rejects anyone who tries to reach it directly.

Not covered here: Cloudflare Tunnel (which removes the need for inbound ports entirely), load balancing across several origins, or Workers in front of the origin.

Create the DNS record

In DNS → Records, add:

FieldValue
TypeA
Nameapp (or @ for the apex)
IPv4 addressyour VPS public address, e.g. 203.0.113.42
Proxy statusProxied
TTLAuto

Add an AAAA record too if your VPS has IPv6. Cloudflare answers over both regardless, but a real AAAA record lets IPv6 clients reach the origin if you ever turn the proxy off.

TTL is greyed out on proxied records and that is expected. Cloudflare’s edge is already answering for the hostname, so there is nothing meaningful to cache downstream and changes take effect within seconds. On DNS-only records the TTL applies normally — drop it to a low value a day before a migration, not on the day itself.

The apex is a special case. CNAME example.com -> something.example.net is not valid DNS, but Cloudflare resolves the target and answers with its addresses instead, so an apex CNAME works here where it would not elsewhere. That is CNAME flattening, and it is on by default for proxied records.

The same record over the API:

create-record.sh
curl -sS -X POST \
  "https://api.cloudflare.com/client/v4/zones/${ZONE_ID}/dns_records" \
  -H "Authorization: Bearer ${CF_API_TOKEN}" \
  -H "Content-Type: application/json" \
  --data '{
    "type": "A",
    "name": "app",
    "content": "203.0.113.42",
    "ttl": 1,
    "proxied": true
  }'

"ttl": 1 means Auto. Use a scoped API token with Zone.DNS: Edit on that one zone — not a global API key.

Confirm the proxy is actually in front

A proxied record resolves to Cloudflare, never to your server:

bash
dig +short app.example.com
Expected output
bash
104.21.44.17
172.67.191.203

Those are Cloudflare addresses. If dig returns 203.0.113.42, the record is DNS-only and the orange cloud is off.

Set the encryption mode to Full (strict)

Go to SSL/TLS → Overview and select Full (strict).

The modes behave very differently:

  • Flexible — Cloudflare talks to your origin over plain HTTP. Any HTTPS redirect on your server becomes an infinite loop. Do not use it.
  • Full — Cloudflare uses HTTPS to the origin but accepts any certificate, including self-signed and expired ones. Encrypted, not authenticated.
  • Full (strict) — HTTPS with certificate validation. This is the setting you want.

Full (strict) requires a certificate on your origin that Cloudflare trusts: either a publicly trusted certificate such as Let’s Encrypt, or a Cloudflare Origin CA certificate.

Install a Cloudflare Origin CA certificate

Origin CA certificates are free, last up to 15 years, and are trusted only by Cloudflare. That is exactly the right trade for an origin that should never be reached directly.

  1. In SSL/TLS → Origin Server, choose Create Certificate.
  2. Keep the generated private key option, list the hostnames (example.com, *.example.com), and create.
  3. Copy the certificate and the key — the key is shown once and never again.
bash
sudo install -d -m 0750 -o root -g root /etc/ssl/cloudflare
sudo nano /etc/ssl/cloudflare/origin.pem   # paste the certificate
sudo nano /etc/ssl/cloudflare/origin.key   # paste the private key
sudo chmod 640 /etc/ssl/cloudflare/origin.pem
sudo chmod 600 /etc/ssl/cloudflare/origin.key
/etc/nginx/sites-available/app.example.com
server {
    listen 443 ssl;
    http2 on;
    server_name app.example.com;

    ssl_certificate     /etc/ssl/cloudflare/origin.pem;
    ssl_certificate_key /etc/ssl/cloudflare/origin.key;

    # Log the visitor's address, not Cloudflare's.
    real_ip_header CF-Connecting-IP;

    location / {
        proxy_pass http://127.0.0.1:3000;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}
bash
sudo nginx -t && sudo systemctl reload nginx

Lock the origin to Cloudflare

With the proxy on, nothing except Cloudflare should be allowed to reach ports 80 and 443.

/usr/local/sbin/ufw-cloudflare.sh
#!/usr/bin/env bash
set -euo pipefail

for ip in $(curl -fsS https://www.cloudflare.com/ips-v4) \
          $(curl -fsS https://www.cloudflare.com/ips-v6); do
  ufw allow proto tcp from "$ip" to any port 80,443 comment 'cloudflare'
done
ufw reload
bash
sudo chmod 700 /usr/local/sbin/ufw-cloudflare.sh
sudo /usr/local/sbin/ufw-cloudflare.sh
sudo ufw status numbered | head -20

Cloudflare changes these ranges occasionally. Re-run the script from a monthly cron job, and delete the old comment 'cloudflare' rules first so the list does not grow without bound.

Keep SSH reachable. Allow port 22 from your own address, or from anywhere if you have key-only authentication and fail2ban, but never let this script be the only thing standing between you and a locked-out server.

Common errors

Error 521 — web server is down. Cloudflare reached your IP and the connection was refused. Either nothing is listening on 443, or your firewall dropped Cloudflare after you ran the script above. Check sudo ss -tlnp | grep :443.

Error 522 — connection timed out. Packets are being dropped rather than refused. Almost always a firewall: ufw, a cloud provider security group, or both. Test from outside with curl -v --connect-timeout 5 https://203.0.113.42.

Error 525 — SSL handshake failed. You are on Full or Full (strict) and the origin’s TLS is broken — wrong certificate path, key mismatch, or nginx serving a default HTTP-only vhost on 443.

Error 526 — invalid SSL certificate. Full (strict) specifically. The origin certificate is expired, self-signed, or does not cover the hostname. An Origin CA certificate generated for example.com alone will not validate for app.example.com; include the wildcard.

ERR_TOO_MANY_REDIRECTS. Flexible mode plus an HTTPS redirect on the origin. Switch to Full (strict).

Every request logs Cloudflare’s IP. Add set_real_ip_from entries for the Cloudflare ranges, or the equivalent RemoteIPTrustedProxy in Apache and trustedIPs in Traefik.

Production notes

Turn on Always Use HTTPS in SSL/TLS → Edge Certificates so Cloudflare handles the http-to-https redirect at the edge, then remove the redirect from nginx. One redirect is enough.

Enable Authenticated Origin Pulls if you want the origin to verify that the connection came from Cloudflare using a client certificate, rather than trusting the IP allowlist alone. It is a strictly stronger control and takes ten minutes.

Do not leave a DNS-only record pointing at the same IP — a direct.example.com or a stale mail record leaks the origin address and undoes the firewall work. Check every record in the zone, including ones you did not create.

Frequently asked questions

Should the orange cloud be on or off?

On for anything that serves HTTP or HTTPS on a standard port — that is what gives you the WAF, caching and a hidden origin IP. Off for SSH, mail, game servers, or any protocol Cloudflare's proxy does not handle.

Why does my site redirect endlessly after enabling Cloudflare?

The encryption mode is set to Flexible. Cloudflare connects to your origin over plain HTTP, your server sees an insecure request and redirects to HTTPS, and the loop repeats. Switch to Full (strict).

Can I still SSH to the server after proxying the domain?

Not through the proxied hostname. Use a separate DNS-only record such as `ssh.example.com`, connect by IP address, or put SSH behind a Cloudflare Tunnel. The proxy only forwards HTTP and HTTPS on a fixed set of ports.

Do I still need Let's Encrypt if I use a Cloudflare Origin certificate?

No, as long as every visitor arrives through Cloudflare. An Origin CA certificate is only trusted by Cloudflare, so a direct browser visit to the IP will show a certificate warning. Keep Let's Encrypt if you need the origin to be independently reachable.

How long do DNS changes take to apply?

Proxied records change almost immediately, because Cloudflare's edge is already answering for the hostname. DNS-only records are subject to the TTL you set — leave it on Auto during a migration.

Sources

  1. Manage DNS records Cloudflare Primary source Accessed 14 May 2026
  2. Proxy status Cloudflare Primary source Accessed 14 May 2026
  3. SSL/TLS encryption modes Cloudflare Primary source Accessed 14 May 2026
  4. Cloudflare Origin CA certificates Cloudflare Primary source Accessed 14 May 2026
  5. Cloudflare IP ranges Cloudflare Primary source Accessed 14 May 2026
4 min read

Part of a learning path

  • Cloudflare intermediate

    How to Deploy an Astro Site to Cloudflare Workers

    Ship a static or server-rendered Astro site to Cloudflare Workers with Wrangler — adapter setup, wrangler.jsonc, nodejs_compat, secrets, custom domains and the errors you hit on the first deploy.

    4 min
  • Cloudflare advanced

    How to Run Cloudflare Tunnel in Front of Traefik

    Expose Docker services through Cloudflare with no inbound ports open — cloudflared plus Traefik, the forwarded-header setting that trips people up, and how to verify the firewall is closed.

    4 min
  • Security intermediate

    How to Secure a VPS That Runs Docker Containers

    SSH hardening, a default-deny firewall, and the fix for the problem that catches almost everyone — Docker publishing container ports straight past UFW into the public internet.

    4 min