To redirect subdomain traffic from a VPS running multiple Docker containers behind Cloudflare, you must configure Nginx to return HTTP 301 or 302 status codes rather than proxying requests. This approach avoids SSL termination conflicts and ensures that browsers correctly update their address bar while respecting the limited port availability on your host machine.
The Problem with Proxying vs. Redirecting
When you have multiple Docker containers running on a single VPS, you often face a constraint where only a few ports (typically 80 and 443) are exposed to the public internet. A common mistake is attempting to useproxy_pass in Nginx for every subdomain. While this works for serving content directly, it creates significant issues when Cloudflare is involved.
If you proxy traffic internally, Cloudflare sees the connection as coming from your server. If your internal Docker containers do not have valid SSL certificates configured, Cloudflare’s "Flexible" SSL mode can cause mixed content errors because the origin server speaks plain HTTP while the client expects HTTPS. Furthermore, if you intend to simply route users to different services without serving them yourself, proxying adds unnecessary latency and complexity.
A true redirect tells the client's browser to make a new request to a different URL. This shifts the burden of connection handling to the destination service. For example, if app.example.com should go to a backend running on port 8080, a redirect instructs the browser to visit http://example.com:8080 (or a mapped domain). This article explains how to set up these redirects cleanly using Nginx within a Docker environment.
For more details on managing Nginx configurations in Docker, see Serve Web Host with NGINX and Direct Subdomain Docker Server: Port.
Configuring Nginx for Subdomain Redirects
The core mechanism for redirection in Nginx is thereturn directive inside a server block. Unlike proxy_pass, which forwards the request silently, return sends an immediate response code to the client.
You need to create a separate server block for each subdomain you wish to redirect. Here is the general structure:
server {
listen 80;
server_name app.example.com;
# Return a permanent redirect
return 301 http://other-service.example.com$request_uri;
}
In this example, any request to app.example.com will result in the browser receiving a 301 Moved Permanently header. The browser will then automatically navigate to other-service.example.com preserving the original path and query string via $request_uri.
If you are redirecting to a specific port on the same domain, ensure the target port is accessible. However, since you are behind Cloudflare, you generally want to avoid exposing raw ports to the public internet due to security risks. It is better to map each subdomain to its own distinct domain name managed by Cloudflare, or use Cloudflare Workers for advanced routing if direct port exposure is unavoidable.
Handling SSL and Cloudflare Integration
Since your origin Docker containers likely lack public SSL certificates, you should not attempt to terminate SSL in Nginx for these redirect blocks unless you are using a unified certificate for all subdomains. Instead, rely on Cloudflare to handle the SSL termination between the user and your VPS. Ensure your Cloudflare DNS records point all relevant subdomains to your VPS IP address. In the Cloudflare dashboard, set the SSL/TLS encryption mode to "Full" or "Full (Strict)". Avoid "Flexible" mode if you want to maintain secure connections end-to-end, although for simple redirects, the browser handles the final handshake with the destination. If you are redirecting to another subdomain that also uses Cloudflare, the SSL context is handled by the respective Cloudflare edge locations. Your Nginx server simply acts as a signpost. This prevents the mixed content issues mentioned earlier because no actual content is being served over HTTP from your Docker containers; only HTTP headers are being exchanged. For insights on securing your setup with proper SSL termination, review Cloudflare SSL Termination with Docker: Host vs. Containers.Docker Deployment Strategy
To manage these Nginx configurations effectively, deploy Nginx as a dedicated Docker container. Use a volume mount to inject your configuration files into the container. This allows you to update redirect rules without rebuilding the image. Create a directory on your host machine, such as/etc/nginx/conf.d, and place your server block files there. Then, mount this directory into your Nginx container:
volumes:
- ./nginx/conf.d:/etc/nginx/conf.d
- ./nginx/logs:/var/log/nginx
This separation ensures that your Nginx instance remains lightweight and focused solely on routing. If you need to add more complex logic later, such as conditional redirects based on headers, you can expand the configuration files in this mounted directory.
It is also crucial to ensure that your Docker network allows communication if you were to switch back to proxying, but for redirects, network isolation is less critical since the traffic leaves the VPS entirely after the initial handshake.
Troubleshooting Common Issues
Sometimes redirects may loop if not configured correctly. Ensure that the destination URL does not point back to the same Nginx server unless intended. Also, verify that Cloudflare is not caching your 301 responses incorrectly. You can bypass cache by addingno-cache directives if necessary, though 301s are typically cached aggressively by browsers.
If you encounter 404 errors after redirection, check that the destination service is actually listening on the specified address. Remember that internal Docker IPs are not accessible from the public internet, so your redirect targets must be publicly resolvable domains or IP addresses.
For further reading on resolving similar Nginx context issues, check out Troubleshooting Nginx-proxy Ignoring Server Context.
Frequently Asked Questions
Can I use Flexible SSL mode with Nginx redirects?
Yes, you can use Flexible SSL mode in Cloudflare when using Nginx for redirects because Nginx does not serve the actual content. However, "Full" mode is recommended to ensure the connection between Cloudflare and your VPS is encrypted, providing better security for the initial handshake.Why am I getting infinite redirect loops?
Infinite loops usually occur if the destination URL points back to the same subdomain or if the rewrite conditions are misconfigured. Ensure that thereturn directive points to a completely different domain or service that does not trigger the same Nginx rule.
Do I need to expose ports in Docker for redirects?
No, you do not need to expose the destination container's ports to the host if you are using external domains for redirection. The redirect simply tells the browser where to go; it does not require the VPS to forward traffic internally.How do I preserve query parameters during redirection?
Use the$request_uri variable in your Nginx return directive. This variable automatically includes the path and query string from the original request, ensuring that parameters like ?id=123 are passed to the new URL.