How to create a dynamic redirect rule for multiple domains (hostnames) in Cloudflare

Written by - 0 comments

Published on - Listed in Nginx Cloud Internet


In a professional environment, you should have more than just a production environment (I really hope you do!). Testing, UAT, Preproduction and even more environments are common. 

Host (domain) agnostic redirects in Nginx 

To realistically test the different environments, your web application most likely uses a different (sub-) domain for the app. For example: test.example.com, uat.example.com, www.example.com.

If you use Nginx as a reverse proxy for redirects, you can add a configuration snippet which can be used by all environments:

  location /source {
    rewrite ^/(.*)$ https://$host/target redirect;
  }

This snippet creates a temporary redirect (302) on requests on the URI /source and redirects to the URI /target. By using the internal Nginx variable $host, this redirect is domain agnostic.

  • http://app.example.com/source  -> Redirect to https://app.example.com/target
  • http://www.example.com/source -> Redirect to https://www.example.com/target

That means, it will work for every requested domain (as long as the requested domain is accepted by the relevant server context).

Of course there are also other redirect possibilities in Nginx

So much for the theory with a Nginx server. But what if you use Cloudflare in front of your application? To "relieve" your application's web server, you should let Cloudflare handle the redirect.

Redirect rules in Cloudflare

Cloudflare is also able to do such a dynamic redirects - it's just a bit tricky to figure out how. The magic can be done in the Redirect Rules, which is (now) part of the Rules section.

A simple and classic (but static) redirect can be configured like this:

Static Redirect Rule in Cloudflare

In this (static) example, a filter is defined which matches our test domain (test.example.com) and our requested URI /source. If the filter matches, a redirect to https://test.example.com/target  should happen.

  • http://test.example.com/source -> https://test.example.com/target 

This works. But as you can see, this only works for the test.example.com domain.

Dynamic, domain agnostic redirects in Cloudflare

When the Redirect Rule Type is changed from "Static" to "Dynamic", the "URL" field changes to "Expression". This allows to enter functions and variables - make the redirect dynamic. But what functions are allowed? Which fields exist? This redirect example on the documentation is helpful in such a case.

To translate the example for my use-case (domain agnostic), I first adjusted the filter and set a "list" of potential domains which should trigger this redirect rule. This now uses the "is in" function on the domains in the expression preview. 

(http.host in {"test.example.com" "uat.example.com" "preprod.example.com" "www.example.com"} and starts_with(http.request.uri, "/source")) 

To add the requested domain, the relevant variable "host.name" can be used. As we need to mix fixed strings with a variable (host.name), we must use the concat() function to merge these fields together:

The expression field under "Then..." now shows: 

concat("https://", http.host, "/target")

Testing the dynamic redirect

I could successfully test the dynamic redirect on all matching domains using curl:

ck@linux:~$ curl https://test.example.com/source -I
HTTP/2 301 
date: Mon, 25 Aug 2025 12:32:10 GMT
content-type: text/html
content-length: 167
location: https://test.example.com/target
cache-control: max-age=3600
expires: Mon, 25 Aug 2025 13:32:10 GMT
server: cloudflare
cf-ray: 974b1c51ea97d2ca-FRA

ck@linux:~$ curl https://uat.example.com/source -I
HTTP/2 301 
date: Mon, 25 Aug 2025 12:32:16 GMT
content-type: text/html
content-length: 167
location: https://uat.example.com/target
cache-control: max-age=3600
expires: Mon, 25 Aug 2025 13:32:16 GMT
server: cloudflare
cf-ray: 974b1c766c1ddc58-FRA

It doesn't matter which domain was requested, as long as it matches the filter in Cloudflare's Redirect Rule. 


Add a comment

Show form to leave a comment

Comments (newest first)

No comments yet.

RSS feed

Blog Tags:

  AWS   Android   Ansible   Apache   Apple   Atlassian   BSD   Backup   Bash   Bluecoat   CMS   Chef   Cloud   Coding   Consul   Containers   CouchDB   DB   DNS   Database   Databases   Docker   ELK   Elasticsearch   Filebeat   FreeBSD   Galera   Git   GlusterFS   Grafana   Graphics   HAProxy   HTML   Hacks   Hardware   Icinga   Influx   Internet   Java   KVM   Kibana   Kodi   Kubernetes   LVM   LXC   Linux   Logstash   Mac   Macintosh   Mail   MariaDB   Minio   MongoDB   Monitoring   Multimedia   MySQL   NFS   Nagios   Network   Nginx   OSSEC   OTRS   Observability   Office   OpenSearch   PGSQL   PHP   Perl   Personal   PostgreSQL   Postgres   PowerDNS   Proxmox   Proxy   Python   Rancher   Rant   Redis   Roundcube   SSL   Samba   Seafile   Security   Shell   SmartOS   Solaris   Surveillance   Systemd   TLS   Tomcat   Ubuntu   Unix   VMWare   VMware   Varnish   Virtualization   Windows   Wireless   Wordpress   Wyse   ZFS   Zoneminder