Search code examples
jsonnginxproxyasp.net-corekestrel

File requests with Query Strings failing in ASP.NET Core behind NGINX


I have an ASP.NET Core site running behind an NGINX reverse-proxy on Linux. I'm running into an issue where if I do a file request through the proxy with a query string attached (i.e. for cache-busting), I'm getting 404 errors, but if I request the exact same URL directly from the application (not through NGINX), it works fine, and if I drop the query string, it also works fine.

Examples below (the NGINX proxy is listening on port 5000 and the application is listening on port 5002)...

If I use a url like: http://host-name:5000/path/file.json

I get the result back correctly, and this is what appears on the application's console output:

[40m[32minfo[39m[22m[49m: Microsoft.AspNetCore.Hosting.Internal.WebHost[1]
      Request starting HTTP/1.1 GET http://host-name:5000/path/file.json  
Microsoft.AspNetCore.Hosting.Internal.WebHost:Information: Request starting HTTP/1.1 GET http://host-name:5000/path/file.json  
[40m[32minfo[39m[22m[49m: Microsoft.AspNetCore.StaticFiles.StaticFileMiddleware[2]
      Sending file. Request path: '/path/file.json'. Physical path: '/home/coreuser/debug/wwwroot/path/file.json'
Microsoft.AspNetCore.StaticFiles.StaticFileMiddleware:Information: Sending file. Request path: '/path/file.json'. Physical path: '/home/coreuser/debug/wwwroot/path/file.json'
[40m[32minfo[39m[22m[49m: Microsoft.AspNetCore.Hosting.Internal.WebHost[2]
      Request finished in 400.9508ms 200 application/json

If I use a url like: http://host-name:5002/path/file.json

I get the result back correctly, and this is what appears on the application's console output:

[40m[32minfo[39m[22m[49m: Microsoft.AspNetCore.Hosting.Internal.WebHost[1]
      Request starting HTTP/1.1 GET http://host-name:5002/path/file.json  
Microsoft.AspNetCore.Hosting.Internal.WebHost:Information: Request starting HTTP/1.1 GET http://host-name:5002/path/file.json  
[40m[32minfo[39m[22m[49m: Microsoft.AspNetCore.StaticFiles.StaticFileMiddleware[2]
      Sending file. Request path: '/path/file.json'. Physical path: '/home/coreuser/debug/wwwroot/path/file.json'
Microsoft.AspNetCore.StaticFiles.StaticFileMiddleware:Information: Sending file. Request path: '/path/file.json'. Physical path: '/home/coreuser/debug/wwwroot/path/file.json'
[40m[32minfo[39m[22m[49m: Microsoft.AspNetCore.Hosting.Internal.WebHost[2]
      Request finished in 28.2031ms 200 application/json
Microsoft.AspNetCore.Hosting.Internal.WebHost:Information: Request finished in 28.2031ms 200 application/json

If I use a url like: http://host-name:5002/path/file.json?_dc=1020

I get the result back correctly, and this is what appears on the application's console output:

[40m[32minfo[39m[22m[49m: Microsoft.AspNetCore.Hosting.Internal.WebHost[1]
      Request starting HTTP/1.1 GET http://host-name:5002/path/bootstrap.json?_dc=1020  
Microsoft.AspNetCore.Hosting.Internal.WebHost:Information: Request starting HTTP/1.1 GET http://host-name:5002/path/bootstrap.json?_dc=1020  
[40m[32minfo[39m[22m[49m: Microsoft.AspNetCore.StaticFiles.StaticFileMiddleware[2]
      Sending file. Request path: '/path/file.json'. Physical path: '/home/coreuser/debug/wwwroot/path/file.json'
Microsoft.AspNetCore.StaticFiles.StaticFileMiddleware:Information: Sending file. Request path: '/path/file.json'. Physical path:     '/home/coreuser/debug/wwwroot/path/file.json'
[40m[32minfo[39m[22m[49m: Microsoft.AspNetCore.Hosting.Internal.WebHost[2]
      Request finished in 146.8157ms 200 application/json
Microsoft.AspNetCore.Hosting.Internal.WebHost:Information: Request finished in 146.8157ms 200 application/json

If I use a url like: http://host-name:5000/path/file.json?_dc=1020

I get a 404 error, and this is what appears on the application's console output:

[40m[32minfo[39m[22m[49m: Microsoft.AspNetCore.Hosting.Internal.WebHost[1]
      Request starting HTTP/1.1 GET http://host-name:5000/path/file.json?_dc=1020  
Microsoft.AspNetCore.Hosting.Internal.WebHost:Information: Request starting HTTP/1.1 GET http://host-name:5000/path/file.json?_dc=1020  
[40m[32minfo[39m[22m[49m: Microsoft.AspNetCore.Hosting.Internal.WebHost[2]
      Request finished in 379.4175ms 404 
Microsoft.AspNetCore.Hosting.Internal.WebHost:Information: Request finished in 379.4175ms 404 

Now, I'm not clear on whether this is an issue with NGINX messing with what it is forwarding to the ASP.NET Core application, or it isn't just an issue with the ASP.NET Core application (and/or Kestrel) getting thrown off by the combination of query string and the proxy's port number showing up on the request.

The relevant part of the NGINX configuration looks like this:

server {
    server_name host-name;
    listen 5000 default_server;
    listen [::]:5000 default_server;
    location / {
        proxy_pass http://localhost:5002;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection keep-alive;
        proxy_set_header Host $http_host;
        proxy_cache_bypass $http_upgrade;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Host $http_host;
        proxy_set_header X-Forwarded-Server $host;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Forwarded-Path $request_uri;
    }
}

Any ideas?

EDIT:

I've since modified my server block to look like this:

server {
    server_name host-name;
    listen 5000 default_server;
    listen [::]:5000 default_server;
    root /var/www/path-to-debug/wwwroot;
    location / {
        if ($query_string ~ "^(.*)_dc=(.*)$") {
          rewrite ^(.*)$ $uri?;
        }
        try_files $uri @proxy;
    }
    location @proxy {
        proxy_pass http://localhost:5002;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection keep-alive;
        proxy_set_header Host $http_host;
        proxy_cache_bypass $http_upgrade;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Host $http_host;
        proxy_set_header X-Forwarded-Server $host;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Forwarded-Path $request_uri;
    }
}

Now that specific file is being fetched properly (bypassing Kestrel completely), but that seems to be messing with one of my controller calls that also has the _dc=XXXX attached.


Solution

  • I've gotten this part to work by modifying my server proxy block as below...it feels like a hack to me, but I haven't found a better solution yet. I also still have other issues, but will post separate questions for those if/when I can't figure them out.

    server {
        server_name host-name;
        listen 5000 default_server;
        listen [::]:5000 default_server;
        root /var/www/path-to-debug/wwwroot;
        location / {
            if ($query_string ~ "^(.*)_dc=(.*)$") {
              rewrite ^(.*)$ $uri?;
            }
            try_files $uri @proxy;
        }
        location @proxy {
            proxy_pass http://localhost:5002;
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection keep-alive;
            proxy_set_header Host $http_host;
            proxy_cache_bypass $http_upgrade;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Host $http_host;
            proxy_set_header X-Forwarded-Server $host;
            proxy_set_header X-Forwarded-Proto $scheme;
            proxy_set_header X-Forwarded-Path $uri;
        }
    }