A lightweight, Python-based Custom HTTP/HTTPS Server designed to run as a Linux systemd service or a macOS launchd agent. It is ideal for securely serving static files, logs, test results, build artifacts, or
internal documentation.
This version includes:
-
Native HTTPS support using a configurable SSL certificate and key
-
Optional HTTP β HTTPS redirection
-
Dynamic configuration loading from
/usr/local/etc/custom-https-server.confwith local fallback -
Automatic creation and management of log files in the userβs home directory
-
OS-specific defaults optimized for Linux and macOS
-
Threaded HTTP and HTTPS servers with optional authentication support
-
Read and Write access modes to control UI and file operations:
-
Read mode
- No upload or delete buttons
- No file-selection checkboxes
- No JavaScript errors
- Breadcrumb navigation, search, and sorting fully functional
-
Write mode
- Full interactive web UI enabled
- File upload, delete, progress indicator, and select-all supported
- Clean separation of HTML and JavaScript logic
-
-
Pure Python 3 β no external dependencies
-
Serves any local directory
-
MIME-type aware (
.log,.tap,.xml,.html,.pdf,.md, etc.) -
Works as:
- systemd service on Linux
- launchd agent on macOS
-
Supports both HTTP and HTTPS simultaneously
-
Optional HTTP β HTTPS redirection
-
Reads config dynamically
-
Auto-creates logs if missing
-
Configurable via
.conffile or CLI arguments -
Threaded server for concurrent requests
-
Interactive terminal output is preserved
custom-https-server/
βββ custom-https-server/
β βββ custom-https-server.service # systemd unit file (Linux)
β βββ custom_https_server.py # Main HTTP/HTTPS server script
β βββ default-config.conf # Default configuration (path/port/SSL)
β βββ install.sh # Installer script (Linux & macOS)
β βββ macos-launchd-setup.sh # macOS launchd agent setup
β βββ uninstall.sh # Uninstaller script
β βββ update-service.sh # Update macOS plist / Linux systemd runtime args
βββ .ignore # Ignore file
βββ HTTPS_Server.jpeg # Screenshot / demo image
βββ LICENSE # MIT License
βββ README.md # Project documentation
git clone https://github.com/kumarmuthu/custom-https-server.git
cd custom-https-server/custom-https-server
chmod +x install.sh uninstall.sh update-service.sh
sudo ./install.sh
# Install with virtualenv
sudo ./install.sh -venv true
# OR install using system python
sudo ./install.sh -venv falsesudo ./install.sh -path /root -port 8080π‘οΈ Note:
sudois required for system paths like/optor/usr/local.
Edit the config file:
Linux:
sudo vi /etc/custom-https-server.confMac:
sudo vi /usr/local/etc/custom-https-server.conf# -------------------------------
# Generic server
# -------------------------------
SERVE_PATH=/root
SERVE_PORT=8080
MODE=read
# -------------------------------
# OS-specific overrides (optional)
# -------------------------------
LINUX_SERVE_PATH=/var/www
MAC_SERVE_PATH=/Users/Shared
# -------------------------------
# Auth
# -------------------------------
AUTH_USERNAME=admin
AUTH_PASSWORD=password
# -------------------------------
# Logs
# -------------------------------
CFG_LOG_DIR=dynamic_path
CFG_LOG_FILE=dynamic_log_file
CFG_ERR_FILE=dynamic_err_file-
SERVE_PATHβ Default serve directory β Overridden byLINUX_SERVE_PATHorMAC_SERVE_PATHautomatically based on OS -
SERVE_PORTβ Primary HTTP port β Used only via config when running as a service -
MODE=read | writeβ Controls UI capabilities (explained below) -
CFG_LOG_*β Resolved dynamically at runtime β Logs are auto-created if paths donβt exist
Logs are auto-created if missing (
LOG_DIR,LOG_FILE,ERR_FILE) and stdout/stderr are displayed in terminal when running interactively.
Restart to apply changes:
sudo systemctl restart custom-https-serversudo systemctl status custom-https-server
sudo lsof -i :80 # Or your configured HTTP port
sudo lsof -i :443 # Or your configured HTTPS portThe server auto-creates logs in the user directory (LOG_DIR) if missing. Example:
tail -f /Users/<username>/custom_https_server_log/logs/custom_https_server.log
tail -f /Users/<username>/custom_https_server_log/logs/custom_https_server.errReplace
<username>with your actual user.
sudo firewall-cmd --permanent --add-port=80/tcp
sudo firewall-cmd --permanent --add-port=443/tcp
sudo firewall-cmd --reloadsudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw reloadcd custom-https-server/custom-https-server
sudo ./uninstall.shgit clone https://github.com/kumarmuthu/custom-https-server.git
cd custom-https-server/custom-https-server
chmod +x install.sh uninstall.sh macos-launchd-setup.sh update-service.shInstall with:
sudo ./install.sh -path /Users/<username> -port 8080-pathβ Directory to serve-portβ HTTP/HTTPS port
The installer automatically sets up a launchd agent.
Check if service is running:
launchctl list | grep custom_https_serverValidate plist syntax (optional):
plutil ~/Library/LaunchAgents/com.custom_https_server.plistWatch logs in real time:
tail -f /Users/<username>/custom_https_server_log/logs/custom_https_server.log
tail -f /Users/<username>/custom_https_server_log/logs/custom_https_server.errReplace
<username>with your actual macOS username.
Automated:
cd custom-https-server/custom-https-server
sudo ./uninstall.shManual:
launchctl unload ~/Library/LaunchAgents/com.custom_https_server.plist
rm -f ~/Library/LaunchAgents/com.custom_https_server.plist
launchctl list | grep custom_https_serverpython3 custom_https_server.py \
--path /Users/muthukumar \
--bind 0.0.0.0 \
--mode read \
--user admin \
--pass password-
Ports are automatically loaded from the configuration file
-
If no config file exists, internal defaults are used
-
CLI arguments override config values only when explicitly provided
-
Intended for:
- Development
- Debugging
- Temporary/manual runs
-
Not used by systemd or launchd
Important: When running as a systemd service (Linux) or a launchd agent (macOS), the server is started without any CLI arguments.
In service mode:
-
No
--port,--path, or other CLI flags are passed -
All runtime settings are loaded dynamically from:
/usr/local/etc/custom-https-server.conf or /etc/custom-https-server.conf -
This includes:
- HTTP / HTTPS ports
- Serve path (with OS overrides)
- Authentication credentials
- SSL certificate and key (if enabled)
read/writemode
π CLI arguments are ignored in service mode by design
π They exist only for standalone or development usage
The server supports two operating modes that control UI behavior and filesystem access. You can configure the mode during installation, via config file, or at runtime using CLI arguments.
MODE=read
This is the default and recommended mode for logs, reports, artifacts, and internal documentation.
Behavior:
- β No upload buttons
- β No delete buttons
- β No file selection checkboxes
- β No JavaScript console errors
- β Clean, static browsing UI
- β Breadcrumb navigation works
- β File search works
- β Sorting by name / size / date works
- β Read-only access to files and directories
Use cases:
- Test results
- Logs
- Build artifacts
- Documentation servers
- CI/CD output sharing
- Audit-safe environments
Example config:
MODE=readCLI example:
python3 custom_https_server.py --mode readMODE=write
Enables the full interactive UI with filesystem modification support.
Behavior:
- β Upload files (with progress)
- β Delete files and directories
- β Select-all / multi-select support
- β Clean separation of HTML + JavaScript
- β No inline JS hacks
- β Full UI controls enabled
β οΈ Filesystem is writable β use with care
UI Features Enabled:
- Upload button
- Delete button
- Progress bar
- File checkboxes
- Bulk operations
Use cases:
- Temporary file drops
- Internal file sharing
- Controlled admin environments
- Dev / test systems
Example config:
MODE=writeCLI example:
python3 custom_https_server.py --mode write-
Use
readmode for:- Production
- CI systems
- Public or semi-public access
-
Use
writemode only when:- Authentication is enabled
- Access is restricted
- You trust all users
The server intentionally keeps read mode completely free of write-related UI elements and JS, ensuring a clean, stable browsing experience.
The server mode (read / write) is controlled only via the config file.
After changing the config, always reload using update-service.sh.
sudo vi /etc/custom-https-server.confChange:
MODE=read # or writesudo ./update-service.shβ What this does internally:
- Stops the systemd service
- Waits for the port to be released
- Force-kills stale processes if needed
- Updates
ExecStart - Reloads systemd
- Restarts the service cleanly
β οΈ Do NOT usesystemctl restartdirectly after config changes β it may fail if the port is still in use.
sudo vi /usr/local/etc/custom-https-server.confChange:
MODE=read # or writesudo ./update-service.shβ What this does internally:
- Updates
ProgramArgumentsin the plist - Disables
KeepAlivetemporarily - Properly
bootouts the LaunchAgent - Kills lingering processes (user/root)
- Waits for the port to fully free
- Re-enables
KeepAlive bootstraps the agent as the original GUI user
launchctl unload ~/Library/LaunchAgents/com.custom_https_server.plist
launchctl load ~/Library/LaunchAgents/com.custom_https_server.plistπ« Problems:
- Breaks when run as root
- Fails on Ventura+
- Leaves zombie processes
- Port conflict issues
Always use this after changing config:
sudo ./update-service.shThis guarantees:
- No port conflicts
- No zombie processes
- Correct user context
- Safe restart on both macOS & Linux
| Mode | UI | Upload | Delete | Safe for Prod |
|---|---|---|---|---|
| read | Minimal | β | β | β |
| write | Full UI | β | β |
The following image gives you a glimpse of the application's interface:
MIT License β free to use, modify, and distribute.
Developed and maintained by Muthukumar S GitHub: https://github.com/kumarmuthu
