Python Flask Application Deployed as a Tor Hidden Service
Flask is a lightweight Python web framework widely used for building web applications and APIs. Deploying a Flask application as a Tor hidden service follows the same pattern as any web application behind Tor: Flask (via Gunicorn WSGI server) listens on localhost, Nginx proxies requests from the Tor daemon's HiddenServicePort forwarding to Nginx. This guide covers the complete workflow from local Flask development with Tor testing to production deployment, with Tor-specific patterns for Flask applications (handling .onion URL generation, understanding what request information is and is not available, and testing Tor-specific failure scenarios).
Need this done for your project?
We implement, you ship. Async, documented, done in days.
For local Flask development with Tor access: install Tor locally (brew install tor on macOS, apt install tor on Ubuntu), configure a development hidden service in torrc: HiddenServiceDir /tmp/flask-dev-hs/, HiddenServicePort 80 127.0.0.1:5000. Start Flask in debug mode: flask run --host=127.0.0.1 --port=5000. Start Tor: tor. Get the development .onion address: cat /tmp/flask-dev-hs/hostname. Access via Tor Browser at http://yourlocaldev.onion. This provides a development environment that closely matches production, including Tor's latency (which you want to develop against) and the .onion URL format. Use a separate Development Tor profile with Log debug to see descriptor publishing events during development.
Production Deployment with Gunicorn and Nginx
Production deployment: install Gunicorn (pip install gunicorn), create a systemd service unit for Gunicorn: gunicorn runs as a daemon listening on 127.0.0.1:8000 (or a Unix socket). Configure Nginx as a reverse proxy: upstream flask { server 127.0.0.1:8000; } server { listen 80; proxy_pass http://flask; }. Configure Tor hidden service pointing to Nginx port 80. Start services in order: database (if any), Gunicorn, Nginx, Tor. The systemd dependency ordering (After=network.target, Before=tor.service) ensures Nginx is ready before Tor tries to publish the service. The .onion address is in /var/lib/tor/flask-app/hostname - configure your Flask application to use this in URL generation.
Tor-Specific Flask Patterns
Flask applications on .onion need to handle Tor-specific considerations. URL generation: Flask's url_for() generates URLs with the SERVER_NAME configured in app.config. Set SERVER_NAME to your .onion address: app.config['SERVER_NAME'] = 'youraddress.onion'. This ensures url_for() generates correct .onion URLs in templates and redirects. Preferred_url_scheme: if your .onion service uses HTTP (no TLS), set PREFERRED_URL_SCHEME = 'http'. Remote address: in Tor hidden service deployments, request.remote_addr is always 127.0.0.1 - do not use this for any application logic (rate limiting by IP, logging user IPs) since it is always the same value. User identification requires session cookies or application-level authentication, not IP-based identification.
Session Management and Security for .onion Flask Apps
Flask session management uses signed cookies. Configure a strong secret key: app.config['SECRET_KEY'] = secrets.token_hex(32). Store the key in environment variables, not source code. For .onion services using HTTP (not HTTPS): set SESSION_COOKIE_SECURE = False (HTTPS is not required for .onion security since Tor provides transport encryption), SESSION_COOKIE_HTTPONLY = True (prevents JavaScript access to session cookie), SESSION_COOKIE_SAMESITE = 'Strict' (prevents cross-site request forgery). CSRF protection: use Flask-WTF which provides CSRF token validation. CSRF tokens are important even on .onion services - a malicious .onion site in the same browser could potentially trigger cross-site requests if CSRF is not enforced. Content Security Policy: set a restrictive CSP header in Nginx or Flask: default-src 'self', which prevents loading external resources (that would de-anonymize users by triggering clearnet requests).
Testing Tor-Specific Application Behavior
Test your Flask application for Tor-specific behaviors: (1) test with simulated Tor latency - add latency to localhost requests using tc netem: tc qdisc add dev lo root netem delay 300ms. This simulates Tor's latency in local testing and reveals latency-sensitive bugs, (2) test circuit change behavior - use the Tor control port to issue SIGNAL NEWNYM and verify your application handles clients reconnecting from different apparent sessions correctly, (3) test 127.0.0.1-only access - verify the application is not accessible from external IP: curl http://YOUR_VPS_PUBLIC_IP/ should fail with connection refused. (4) test .onion URL generation - ensure url_for() generates .onion URLs, not localhost URLs, by checking application logs and response headers. Use pytest with the requests library and PySocks for integration testing that routes requests through Tor.