Custom Media on players will work with IPs, but not with a CNAME or A/AAAA pointing to the IP

I have a question regarding custom media in condos; specifically I’m trying to do this on my dedicated condo, but I’ve also been trying on a local condo throw up in single player mode. For a few years now I’ve been trying to work this out, but cannot find a way to accomplish it.

I have my own domain; one of the subdomains I have configured serves a shimmie2 (booru board) instance, which supplies my own image hosting for canvases in my dedicated condo; both are running in seperate docker containers on a dedicated server I have, under a hypervisor (unraid).

I have been trying to figure out a way to play custom media; I have the codec down pat now, but cannot get anything to play with my domain, using a subdomain for another docker server, this one just an nginx server, set up to allow direct files to the media I have, in WEBM format.

If I format the links into the media players in my condos like http://<IPv4>:<port>/path/to/file.webm it worked, but it would never work with my domain, like https://my.domain.com/path/to/file.webm even though the domain would point straight to the IP address. I tried both as a CNAME to the A record I was updating for the base domain, with an nginx proxy manager set up in another docker container to serve requests for the subdomain to the docker container that actually has it.

These days I’m now on Starlink (thank god, good riddance Spectrum-Charter, I sincerely wish they would actually fix all the crap around me that’s been broken since they laid it out 50 or 60 years ago), so I’m now trying with http://<IPv6>/path/to/file.webm and https://my.domain.com/path/to/file.webm instead, but I’m finding the same things.

With the IP addresses, it works perfectly fine, playing in game for anyone that can do native IPv6 resolution themselves, and resolving in my nginx access.log files as <External IPv6 address> - - \[11/Jul/2026:10:48:49 -0400\] "GET </path/to/file.webm> HTTP/1.1" 206 14723346 "media-cache.towerunite.com/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Tower/++UE4+Release-4.22-CL-0 UnrealEngine/4.22.3-0+++UE4+Release-4.22 Safari/537.36", but when I try with the domain name, no matter how I format it (including as a straight AAAA to the IPv6 GUA that the docker container itself has, for the nginx server), it will never produce anything in my access.log or my error.log file

Does all custom media end up going through that media-cache address, and if so, is there some kind of like domain whitelist that would explain why it doesn’t resolve my subdomain/domain for the media players, but nothing has ever been an issue with my image hosting, likely because there’s no cache there, and its on end-clients to resolve it?

The domain itself does load as direct files, and will play in web browsers. I would of course, be happy to share (in private) specifics regarding the domain I have, the subdomain I’ve had set up to try to get this working, how I’m routing said DNS records for this as well as my self-hosted image server, using cloudflare, etc. if it would be able to help me narrow it down.

I checked out my domain configuration and I’m currently not using any public records, I’m using Cloudflare Tunnel on my server. When I had made that initial post with the roundabout instructions I was just using a free A record no-ip domain, though.

Hrm, interesting - I know how to set up tunnels with cloudflared/zero trust - gone ahead and stopped my ddns writer for the subdomain I’m using, deleted the DNS record for it manually, and set up the tunnel to use it instead.

With the published application route on the tunnel in cloudflare, its working in the browser still, but still refuses to playback in game. Using the current ipv6 gua still works for the exact same path/file :sob:

I know you mentioned your friends can play your video in their browsers, but have you had anyone else try loading your hosted video on a Tower Unite player?

I did; I had two friends both able to open the link in their browsers and paste it into the game, after I made them media admins, and it was still just a blank, black screen

edit: I’ve got multiple files/links I’ve been trying with as well, not just one file. For each, I can get it to work if I format the link with the ipv6 address, but not with the domain

What about any Cloudflare security settings, do you have any events that show under there? Maybe the mediacache fetcher from TU is getting blocked by some kind of bot filter? The thing is that I have Bot Prevention and other security features enabled and I don’t have issues with them on. But your issue is completely isolated to the domain, so it HAS to be something in your host’s portal.

I don’t think so? I have bot prevention enabled as well, but aside from that and DDoS protection, I don’t recall having set up any further cloudflare security stuff explicitly - the media cache’s ip from nslookup also isn’t showing up anywhere in my security section under cloudflare.

Given I don’t see the hostname or IP the media cache currently returns for me, I don’t know how much use making a manual security rule is, but I’m making one so if hostname == media-cache.towerunite.com all waf components are to be skipped, maybe that’ll accomplish something? I’m not sure the media cache has a static IP, but again, neither the hostname or the current IP come up in my logs on cloudflare.

Most of it alarmingly is tagged as Microsoft, from the US, trying a lot of non-existent endpoints on a service that isn’t even up, and wouldn’t allow for those endpoints anyway, because they don’t exist, and the reverse proxy doesn’t allow anything out for the container in question anyway. Excluding that, its a bit of Spain addresses doing the same for yet another container, where the endpoint doesn’t exist and the reverse proxy wouldn’t be allowed, and one singular entry from Lithuania, trying a known CVE against another seperate subdomain, which was rightly caught and blocked.

My domain is also purchased straight from cloudflare, under the .dev TLD, aside from also being used for DNS records. Maybe that makes some kind of difference? That entire TLD is HSTS, and I do have full (strict) SSL certificates signed by cloudflare in operation; are you doing similar?

My domain is purchased from Dynadot, using Cloudflare’s nameservers and served through cloudflared tunnel. I use a .boo TLD which is also on the HSTS preload list so it’s probably not that…I think the main difference in our setup here is that you’re running nginx proxy manager and I’m not.

Can you curl your video spoofed as the TU user agent and referrer and check the headers?

In Windows:

curl.exe -v -o NUL -r 0-1023 -A "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Tower/++UE4+Release-4.22-CL-0 UnrealEngine/4.22.3-0+++UE4+Release-4.22 Safari/537.36" -e "https://media-cache.towerunite.com/" https://your.domain/video.mp4

Check for:

  1. HTTP 206 Partial Content response
  2. Content Type is video/mp4
  3. content-range is present and total after slash matches file size
  4. presence of cf-mitigated

Here’s an example of this against my hosted mp4:

* IPv6: (none)
* IPv4: <CF-ANYCAST-1>, <CF-ANYCAST-2>
*   Trying <CF-ANYCAST-1>:443...
* schannel: disabled automatic use of client certificate
* ALPN: curl offers http/1.1
* ALPN: server accepted http/1.1
* Established connection to example.tld (<CF-ANYCAST-1> port 443) from <LAN-IP> port <PORT>
  % Total    % Received % Xferd  Average Speed  Time    Time    Time   Current
                                 Dload  Upload  Total   Spent   Left   Speed
  0      0   0      0   0      0      0      0                              0* using HTTP/1.x
> GET /path/to/file.mp4 HTTP/1.1
> Host: example.tld
> Range: bytes=0-1023
> User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Tower/++UE4+Release-4.22-CL-0 UnrealEngine/4.22.3-0+++UE4+Release-4.22 Safari/537.36
> Accept: */*
> Referer: https://media-cache.towerunite.com/
>
* Request completely sent off
* schannel: remote party requests renegotiation
* schannel: renegotiating SSL/TLS connection
* schannel: SSL/TLS connection renegotiated
< HTTP/1.1 206 Partial Content
< Date: Thu, 30 Jul 2026 21:07:22 GMT
< Content-Type: video/mp4
< Content-Length: 1024
< Connection: keep-alive
< Age: 84
< etag: "<REDACTED>"
< last-modified: Sat, 13 Jun 2026 04:30:37 GMT
< Server: cloudflare
< content-range: bytes 0-1023/11763387
< Cache-Control: max-age=14400
< cf-cache-status: HIT
< Nel: {"report_to":"cf-nel","success_fraction":0.0,"max_age":604800}
< Report-To: {"group":"cf-nel","max_age":604800,"endpoints":[{"url":"https://a.nel.cloudflare.com/report/v4?s=<REDACTED>"}]}
< CF-RAY: <REDACTED>
< alt-svc: h3=":443"; ma=86400

{ [668 bytes data]
100   1024 100   1024   0      0   5970      0                              0
* Connection #0 to host example.tld:443 left intact```

Thanks again for working through this with me; I’ve had some time to focus to make sure I’m reading everything right again, and have run that.

I’m running curl -v -o NUL -r 0-1023 -A "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Tower/++UE4+Release-4.22-CL-0 UnrealEngine/4.22.3-0+++UE4+Release-4.22 Safari/537.36" -e "https://media-cache.towerunite.com/" https://my.domain.dev/path/to/file.webm

And get this output:

*   Trying 172.67.154.158:443...
* Host my.domain.dev:443 was resolved.
* IPv6: 2606:4700:3035::ac43:9a9e, 2606:4700:3037::6815:620
* IPv4: 172.67.154.158, 104.21.6.32
* ALPN: curl offers h2,http/1.1
} [5 bytes data]
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
} [1570 bytes data]
* SSL Trust Anchors:
*   CAfile: /etc/ssl/certs/ca-certificates.crt
{ [5 bytes data]
* TLSv1.3 (IN), TLS handshake, Server hello (2):
{ [1210 bytes data]
* TLSv1.3 (IN), TLS change cipher, Change cipher spec (1):
{ [1 bytes data]
* TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8):
{ [19 bytes data]
* TLSv1.3 (IN), TLS handshake, Unknown (25):
{ [2339 bytes data]
* TLSv1.3 (IN), TLS handshake, CERT verify (15):
{ [79 bytes data]
* TLSv1.3 (IN), TLS handshake, Finished (20):
{ [52 bytes data]
* TLSv1.3 (OUT), TLS change cipher, Change cipher spec (1):
} [1 bytes data]
* TLSv1.3 (OUT), TLS handshake, Finished (20):
} [52 bytes data]
* SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384 / X25519MLKEM768 / id-ecPublicKey
* ALPN: server accepted h2
* Server certificate:
*   subject: CN=domain.dev
*   start date: Aug 19 16:28:19 2026 GMT
*   expire date: Nov 17 16:28:18 2026 GMT
*   issuer: C=US; O=Let's Encrypt; CN=YE2
*   Certificate level 0: Public key type EC/prime256v1 (256/128 Bits/secBits), signed using ecdsa-with-SHA384
*   Certificate level 1: Public key type EC/secp384r1 (384/192 Bits/secBits), signed using ecdsa-with-SHA384
*   Certificate level 2: Public key type EC/secp384r1 (384/192 Bits/secBits), signed using ecdsa-with-SHA384
*   Certificate level 3: Public key type EC/secp384r1 (384/192 Bits/secBits), signed using ecdsa-with-SHA384
*   subjectAltName: "my.domain.dev" matches cert's "*.domain.dev"
* OpenSSL verify result: 0
* SSL certificate verified via OpenSSL.
* Established connection to my.domain.dev (172.67.154.158 port 443) from [local IPv4 client address] port 60322 
  % Total    % Received % Xferd  Average Speed  Time    Time    Time   Current
                                 Dload  Upload  Total   Spent   Left   Speed
  0      0   0      0   0      0      0      0                              0* using HTTP/2
* [HTTP/2] [1] OPENED stream for https://my.domain.dev/path/to/file.webm
* [HTTP/2] [1] [:method: GET]
* [HTTP/2] [1] [:scheme: https]
* [HTTP/2] [1] [:authority: my.domain.dev]
* [HTTP/2] [1] [:path: /path/to/file.webm]
* [HTTP/2] [1] [range: bytes=0-1023]
* [HTTP/2] [1] [user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Tower/++UE4+Release-4.22-CL-0 UnrealEngine/4.22.3-0+++UE4+Release-4.22 Safari/537.36]
* [HTTP/2] [1] [accept: */*]
* [HTTP/2] [1] [referer: https://media-cache.towerunite.com/]
} [5 bytes data]
> GET /path/to/file.webm HTTP/2
> Host: my.domain.dev
> Range: bytes=0-1023
> User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Tower/++UE4+Release-4.22-CL-0 UnrealEngine/4.22.3-0+++UE4+Release-4.22 Safari/537.36
> Accept: */*
> Referer: https://media-cache.towerunite.com/
> 
* Request completely sent off
} [5 bytes data]
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
{ [230 bytes data]
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
{ [230 bytes data]
< HTTP/2 200 
< date: Thu, 20 Aug 2026 17:11:03 GMT
< content-type: video/webm
< server: cloudflare
< last-modified: Wed, 11 Feb 2026 18:04:52 GMT
< vary: Accept-Encoding
< etag: <REDACTED>
< expires: Fri, 21 Aug 2026 17:11:03 GMT
< cache-control: max-age=86400
< x-content-type-options: nosniff
< report-to: {"group":"cf-nel","max_age":604800,"endpoints":[{"url":"https://a.nel.cloudflare.com/report/v4?s=4DENcyNjmZCru1Ah2hhaxj6xk%2BYKv3y0P0fdxsbfLy5v%2Fyld%2BpGtD3UmOMPD88xoYFcjJ%2FZbtu%2Bm0aHzmXLxsp3MfVcHFuqH9hCe1IODO1r62NZmEticuIPd41uL2eCdkcg%3D"}]}
< cf-cache-status: DYNAMIC
< nel: {"report_to":"cf-nel","success_fraction":0.0,"max_age":604800}
< strict-transport-security: max-age=31536000; includeSubDomains; preload
< cf-ray: a2e303d488560ba1-ATL
< alt-svc: h3=":443"; ma=86400
< 
{ [5 bytes data]
100 49.06M   0 49.06M   0      0  3.56M      0           00:13          3.58M
* Connection #0 to host my.domain.dev:443 left intact

I’ve only obfuscated my.domain.dev as a standin for my subdomain, and /path/to/file.webm as a standin for the actual path to my file. I was under the impression h.264/mp4 files would not work at all; as a result, all of my media is encoded with libvpx-vp9 video, libopus audio, and in webm containers.

I definitely don’t get a HTTP 206 Partial Content response, I get HTTP/2 200 instead. I don’t think this should be an issue; if it was, why would it still work using http://[IPv6]/path/to/file.webm in game, but just not https://my.domain.dev/path/to/file.webm, being one file complete with a 200, rather than a 206/207, partial response?

Likewise, I get video/webm instead of video/mp4, but given my files are webm instead, I don’t see a problem there.

I am not using nginx proxy manager for my.domain.dev - I quite using it for this purpose, thinking it might be part of the issue. I do call my ssl certificates out of the directory where I have them stored, under npm, but just as a filebrowser path; this nginx server stands on its own, and has its own AAAA record being updated, completely independently from the way I do most of my other web servers, running through nginx-proxy-manager.

Here’s my default.conf (only one I have, given this is all this entire nginx docker container is set up for);

# Need to see about adding these in to improve playback in game; not allowed here in this config apparently
#worker_processes auto;
#events {
#    worker_connections 4096;
#    use epoll;
#    multi_accept on;
#}
map $scheme $hsts_header {
    https               "max-age=63072000;includeSubDomains; preload";
}

server {
    listen              80;
    listen              443 ssl;
    listen              [::]:80;
    listen              [::]:443 ssl;
    server_name         my.domain.dev;
    ssl_certificate     /path/to/fullchain.pem;
    ssl_certificate_key /path/to/privkey.pem;
    ssl_protocols       TLSv1.3;
    add_header          Strict-Transport-Security $hsts_header always;
    root                /path/to/media/root;

    # Set a list of index files to attempt to use.
    #index index.html index.htm;

    location / {
        # First attempt to server files as direct files,
        # then attempt to serve request as a directory, 
        # fail to a 404 if needed to avoid recursion redirects
        try_files $uri $uri/ =404;

        # Optimizations for video streaming
        sendfile on;
        tcp_nopush on;
        tcp_nodelay on;
        keepalive_timeout 65;
        keepalive_requests 1000;
        expires 1d;
        gzip on;
        gzip_types video/webm;
        types {
          video/webm webm;
        }
        add_header Accept-Ranges bytes;

        # Fancy index settings:
        # MAKE SURE FANCY INDEX IS ONLY ON WHEN WE NEED TO BE BROWSING THE INDEX!
        fancyindex                   on;
        fancyindex_directories_first on;
        #fancyindex_name_length      255; # Maximum length of any given filename.
        # Broken as of 9 July 2026? Won't stop [emerg] if this is uncommented!
        fancyindex_localtime         off; # on = use local time instead of GMT.
        fancyindex_exact_size        off; # on = output human-readable file sizes.

        auth_basic "Authentication Required";
        auth_basic_user_file /config/keys/.htpasswd;

        # Explicitly deny security risks
        location ~ /\.ht           {deny all;} # .ht*, such as .htaccess or .htpasswd

        # Implicit override in case we need to allow all file extensions for debugging.
        # If we don't need this for debugging, this should be commented out.
        # Don't ever set this to deny all, or it'll deny all access to literally everything lol
        #location ~* \.\w+$         {allow all;}

        # Specific file extensions we want denied or allowed.
        location ~* ^.+\.(mkv|mp4|nfo)$ {
                deny all;
        }
        location ~* ^.+\.(txt)$ {
                allow all;
                #try_files $uri =404;
                types {
                  text/basic txt;
                }
        }
        location ~* ^.+\.(webm)$ {
                allow all;
                auth_basic off;
        }
    }
}

Maybe it something to do with how I’ve configured the nginx server, but again, I hadn’t figured that, since the direct ipv6 address works, but the domain doesn’t. Hopefully this can shed a bit more light when you have time Nito - I’ve only obfuscated my domain, paths to files, and one instance of my local client IP address, just to be safe. All the other addresses in these logs are actually cloudflare’s, so I didn’t see any point to obfuscating them.


I just redacted one more thing, etag - not a clue what this is, or if there’s anything somebody could do with it, but you redacted it, so I’ve done the same. I do not see any content-range message, but maybe that’s because I’m pushing the entire file with my nginx config file? Same reason I get 200 instead of 206? Not sure if that’s part of the problem, or my lack of cf-mitigated; maybe the latter makes more of a difference, since the same AAAA address being written as the record for my.domain.dev at cloudflare works, but the domain, through cloudflare, doesn’t?


I’ve had a quick search for that cf-mitigated header, and maybe not? Seems like that should only be presented when cloudflare wants to challenge, and the response isn’t coming direct from the origin server, which makes me think its a good thing I don’t get that, but also curious why you expect it to happen, and how a lack of a cloudflare “are you human” challenge could be preventing it from working in-game, somehow :face_with_spiral_eyes:


Thinking on it, maybe what’s happen is cloudflare isn’t allowing the entire file through, because I’m trying to push the entire thing in one go, with the 200 response? With the AAAA, my origin server can send the files straight to the clients, but introducing the domain name, means it should be going through cloudflare; maybe its a rate-limit thing, and I actually do need to be not sending the entire file at once, and expecting a 206 response and that content-range header? Seems viable to me, at least - will have to see about what I’d need to change about my nginx config to do that…