Apache+mod_wsgi/Nginx+uWSGI环境下类X-Sendfile的内网大文件代理授权配置
Alright, let's break down how to set up this proxy-based file delivery (similar to X-Sendfile) for both Apache + mod_wsgi and Nginx + uWSGI environments. The core idea is: your WSGI app handles all auth/permission checks first, then tells the web server to fetch the internal file URL and forward it to the client—without ever exposing that internal address to the end user.
Apache + mod_wsgi Configuration
Step 1: Enable Required Modules
First, make sure you have the necessary Apache modules installed and enabled. These let you run the WSGI app and proxy requests to internal servers:
a2enmod wsgi proxy proxy_http systemctl restart apache2
Step 2: WSGI App Logic (Auth + Permission Checks)
Your app is responsible for validating the user's identity and permissions before allowing access to the file. Here's a quick Flask example:
from flask import Flask, abort, request app = Flask(__name__) def is_authenticated(): # Replace with your actual auth logic (e.g., session checks, JWT validation) return request.headers.get('Authorization') == 'ValidToken123' def has_file_access(user_id, file_id): # Replace with your permission check (e.g., database lookup) return True # Simplified for demo @app.route('/download/<file_id>') def initiate_download(file_id): # 1. Check if user is authenticated if not is_authenticated(): abort(401, description="Unauthorized: Please log in") # 2. Verify user has permission to access this file user_id = "current_user_id" # Get from your auth system if not has_file_access(user_id, file_id): abort(403, description="Forbidden: No access to this file") # 3. Return a custom header with the internal file URL internal_file_url = f"http://internal-fileserver:8080/secure-files/{file_id}" return '', 200, {'X-Proxy-Internal-URL': internal_file_url}
Step 3: Apache Virtual Host Configuration
Configure Apache to intercept the custom header, proxy the internal URL, and hide the header from clients. Here's a full virtual host example:
<VirtualHost *:80> ServerName your-public-domain.com # Mount your WSGI application WSGIScriptAlias / /var/www/your-app/wsgi.py <Directory /var/www/your-app> Require all granted </Directory> # Enable rewrite engine to handle internal proxying RewriteEngine On # Capture the internal URL from the WSGI response header RewriteCond %{LA-U:RESPONSE_X_Proxy_Internal_URL} (.+) # Proxy the request to the internal URL (P flag uses mod_proxy) RewriteRule ^(.*)$ %1 [P,L] # Ensure the custom header never reaches the client Header always unset X-Proxy-Internal-URL # Optimize for large file transfers ProxyTimeout 3600 # 1 hour timeout for big files ProxyBufferSize 64k ProxyBuffers 4 64k ProxyMaxForwards 10 # Restrict Apache to only proxy your internal server (security measure) <Proxy http://internal-fileserver:8080/*> Require ip 192.168.0.0/24 # Your internal subnet </Proxy> </VirtualHost>
Nginx + uWSGI Configuration
Step 1: Ensure uWSGI-Nginx Connection is Working
First, confirm your WSGI app is running via uWSGI (e.g., using a Unix socket or TCP port) and Nginx can reach it.
Step 2: WSGI App Logic (Same as Apache)
The auth/permission logic is identical—your app returns the X-Proxy-Internal-URL header only after validating the user. Here's a Django example:
from django.http import HttpResponse from django.contrib.auth.decorators import login_required from django.core.exceptions import PermissionDenied @login_required def download_file(request, file_id): # Check if user has permission to access this file if not request.user.has_perm('myapp.access_file', file_id): raise PermissionDenied("You don't have access to this file") # Get internal file URL from your database/config internal_url = f"http://internal-fileserver:8080/secure-files/{file_id}" response = HttpResponse(status=200) response['X-Proxy-Internal-URL'] = internal_url return response
Step 3: Nginx Server Block Configuration
Nginx needs to capture the custom header, proxy the internal request, and hide the header. We'll use Lua (via ngx_http_lua_module) for flexible header handling (if you don't have Lua, see alternative notes below):
server { listen 80; server_name your-public-domain.com; client_max_body_size 0; # Disable body size limit for large files # Handle dynamic requests and forward to uWSGI location / { include uwsgi_params; uwsgi_pass unix:/var/run/uwsgi/your-app.sock; # Or tcp://127.0.0.1:5000 # Use Lua to capture and remove the custom header set $internal_url ""; header_filter_by_lua_block { local url = ngx.resp.get_header("X-Proxy-Internal-URL") if url then ngx.var.internal_url = url ngx.resp.delete_header("X-Proxy-Internal-URL") end } # If internal URL is set, proxy to it if ($internal_url != "") { rewrite ^(.*)$ $internal_url break; proxy_pass $internal_url; # Pass client info to internal server (optional) proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; # Optimize for large files proxy_buffering off; proxy_cache off; proxy_max_temp_file_size 0; sendfile on; tcp_nopush on; proxy_timeout 3600s; } } # Security: Restrict proxy access to internal servers only location ~* ^http://internal-fileserver:8080/ { allow 192.168.0.0/24; deny all; } }
Alternative Without Lua
If you don't have the Lua module installed, you can use proxy_hide_header to hide the custom header, and rely on $sent_http_x_proxy_internal_url to capture the value. However, this is less flexible—here's a simplified snippet:
proxy_hide_header X-Proxy-Internal-URL; if ($sent_http_x_proxy_internal_url != "") { proxy_pass $sent_http_x_proxy_internal_url; # Add the same large file optimizations here }
Key Universal Notes
- Auth/Permissions First: Always handle authentication and permission checks in your WSGI app—web servers can't enforce application-specific access rules.
- Internal Network Security: Make sure your web server can only reach the internal file server (use subnet restrictions, firewall rules, or VPNs if needed).
- Large File Optimizations: Disable buffering, set long timeouts, and use sendfile to avoid memory issues during large transfers.
- Error Handling: Your app should return 401/403 errors if auth/permission checks fail—these will be passed directly to the client without triggering proxy logic.
内容的提问来源于stack exchange,提问作者Roman Susi

