Sometimes you want to share a handful of documents, photos, or backup files with a laptop or another phone on the same Wi‑Fi network, but you don't want to rely on cloud services. A lightweight HTTP server running directly on your Android device gives you:
This tutorial shows you how to set up exactly that, using the free python3 module that ships with Termux. By the end you will be able to point any browser on the same network to http://your‑phone‑ip:8000 and be prompted for a username and password.
Make sure your Android device is charged above 50 % and connected to a stable Wi‑Fi network. The server will only be reachable from devices on the same subnet.
libc support.adb push (optional).termux-setup-storage
This creates a ~/storage shortcut that maps to your shared internal storage (e.g., /sdcard).Why this matters: Termux runs in its own sandbox. The termux-setup-storage command links that sandbox to the public storage area, allowing the server to read the files you want to share.
pkg update && pkg upgrade -ypip package manager:
pkg install python -ypython3 --version
You should see something like Python 3.11.x.Note: The python package in Termux includes the http.server module we will use later, so no extra dependencies are required.
You can serve any directory that Termux can read. For simplicity we will use a folder inside the shared storage called SharedServer:
mkdir -p ~/storage/shared/SharedServeradb push:
adb push MyDocument.pdf /sdcard/SharedServer/ls ~/storage/shared/SharedServerIf you prefer to keep the files private, you can serve a directory inside Termux's home (e.g., ~/my_private_share). Just remember that the HTTP server can only read what the Termux user can access.
Python’s built‑in http.server module does not provide authentication out of the box. We will wrap it with a tiny request handler that checks a hard‑coded username and password. This keeps the tutorial self‑contained and avoids installing extra packages.
nano (already bundled):
nano ~/serve_protected.pyadmin and Secret123 with your own credentials):
#!/usr/bin/env python3
import base64
from http.server import SimpleHTTPRequestHandler, HTTPServer
USERNAME = 'admin'
PASSWORD = 'Secret123'
class AuthHandler(SimpleHTTPRequestHandler):
def do_AUTHHEAD(self):
self.send_response(401)
self.send_header('WWW-Authenticate', 'Basic realm="Secure Area"')
self.send_header('Content-type', 'text/html')
self.end_headers()
def parse_auth_header(self):
auth_header = self.headers.get('Authorization')
if auth_header is None:
return False
auth_type, encoded = auth_header.split(' ', 1)
if auth_type != 'Basic':
return False
decoded = base64.b64decode(encoded.strip()).decode('utf-8')
user, pwd = decoded.split(':', 1)
return user == USERNAME and pwd == PASSWORD
def do_GET(self):
if not self.parse_auth_header():
self.do_AUTHHEAD()
self.wfile.write(b'Authentication required.')
else:
super().do_GET()
if __name__ == '__main__':
import os, sys
# Change directory to the folder we want to serve
os.chdir(os.path.expanduser('~/storage/shared/SharedServer'))
port = int(sys.argv[1]) if len(sys.argv) > 1 else 8000
server_address = ('', port)
httpd = HTTPServer(server_address, AuthHandler)
print(f'Serving on http://0.0.0.0:{port} (press Ctrl+C to stop)')
httpd.serve_forever()
chmod +x ~/serve_protected.pyExplanation: The script changes the working directory to ~/storage/shared/SharedServer, starts a basic HTTP server on port 8000 (or a custom port you pass as an argument), and intercepts each GET request. If the Authorization header is missing or incorrect, the server returns a 401 Unauthorized response prompting the browser for credentials.
./serve_protected.py 8000Serving on http://0.0.0.0:8000 (press Ctrl+C to stop)nohup or tmux, but that is beyond the scope of this tutorial.Now the server is listening on all network interfaces of the phone. The next step is to find the phone’s IP address on the Wi‑Fi network.
ip addr show wlan0inet . The number before the slash is your IP, e.g., 192.168.1.42/24. The part before the slash (192.168.1.42) is what you will use in the browser.If your device uses a different interface name (some manufacturers label Wi‑Fi as wlp2s0), replace wlan0 accordingly.
On a laptop, tablet, or another phone connected to the same Wi‑Fi network, open a web browser and type:
http://192.168.1.42:8000
The browser will pop up a native authentication dialog. Enter the username and password you defined in the script (default admin / Secret123). After successful authentication you should see a directory listing of the SharedServer folder, and you can click any file to download it.
Some networks block port 8000, or you might want to avoid conflicts with other services. You can start the script with a different port number:
./serve_protected.py 8080
If you only want the server reachable from the Wi‑Fi interface (and not from USB tethering, for example), modify the server_address line in the script to bind to the specific IP:
server_address = ('192.168.1.42', port)
Remember to update the URL you type in the client browser accordingly.
If the server returns a 403 error, the most common cause is that Termux does not have permission to read the target directory. Re‑run termux-setup-storage and ensure the folder lives under /sdcard/SharedServer (or the ~/storage/shared symlink). Also double‑check the file permissions with ls -l – they should be at least readable by the owner.
This usually means the client browser cached a previous 401 response without credentials. Clear the browser cache or open a private/incognito window and try again.
Termux processes are tied to the session. To keep the server alive you can:
tmux (pkg install tmux) and start the script inside a tmux session.nohup:
nohup ./serve_protected.py 8000 &
The output will be redirected to nohup.out.chmod 600).openssl to create a self‑signed certificate and run python3 -m http.server with the --bind and --directory options, but that adds complexity beyond the scope of this guide.Modify the do_GET method to filter extensions. Example:
if not self.path.endswith('.pdf'):
self.send_error(403, "Only PDF files are allowed")
return
Add a simple logger inside the handler:
import datetime
def log_request(self, code='-'):
now = datetime.datetime.now().isoformat()
with open('/sdcard/SharedServer/access.log', 'a') as f:
f.write(f"{now} - {self.client_address[0]} - {self.command} {self.path} - {code}\n")
# Call log_request at the end of do_GET after super().do_GET()
If you connect your phone to a laptop via USB and enable USB tethering, the interface is usually rndis0. Replace wlan0 with rndis0 in the ip addr command to obtain the correct IP.
When you no longer need the server:
Ctrl+C in the Termux window.nohup or tmux, locate the process ID with ps aux | grep serve_protected.py and kill it with kill.rm ~/serve_protected.py
rm -r ~/storage/shared/SharedServer
Remember that any files you placed in SharedServer remain on your device unless you delete them manually.
pkg install python.With these steps you have a lightweight, password‑protected file server that works on any Android phone without root. It’s perfect for quick ad‑hoc transfers, classroom demos, or sharing large media files without uploading them to the cloud.









