Skip to main content

CLI utility for easy nginx reverse proxy configuration with automatic redirect handling.

Project description

ENR = Easy Nginx Redirects

CLI Tool Made with Pure Python3 Nginx as Proxy Docker as Deploy MIT license PyPI version Contact Developer

CLI utility for quick & easy generating nginx configuration and running Docker containers with nginx reverse proxy.

Uses only Python standard library without external dependencies. So the utility works immediately after copying files - does not require installation of additional Python packages.

This utility demonstrates the capabilities of nginx's ngx_http_proxy_module using directives: proxy_pass, proxy_pass_header, proxy_intercept_errors, proxy_ssl_verify. These are the specific directives currently implemented, but ngx_http_proxy_module module offers many more options. The project serves as an example of a Python wrapper for automatic Docker container configuration and nginx-proxy integration, as well as building Python modules into a single executable script. You can fork this project and customize the nginx and Docker templates for your needs. Pull requests are welcome.

Table of Contents

Use Cases

  • 🐳 One-click proxying for any local (Dockerized and non-Dockerized) or remote HTTP or HTTPS service. This tools runs proxy service as single docker container that is convenient to manage. Support docker networks and hostnames, host.docker.internal domain
  • 🔒 Secure Access to Any Service. Make any web service accessible through HTTPS with just one command, even if it doesn't have security certificates
  • 🏗️ Website Constructor Integration. Free domain binding for website builders like Tilda, Wix, Webflow, etc. Best for fully static sites, forms may require additional configuration
  • 🔗 Custom Subdomain Links. Create short, easy-to-remember subdomain links using your own domain name instead of generic shortener services
  • 😉 Your Own Everything. PRs and forks are welcome

Installation

Quick Installation

Since it uses only Python standard library without external dependencies you can install it via:

curl -fsSL --compressed https://raw.githubusercontent.com/pavelsr/enr/main/enr.pyz > /usr/local/bin/enr && \
    chmod +x /usr/local/bin/enr

or you can install it as regular Python module:

# pipx (recommended)
pipx install enr    # or from GitHub:
pipx install git+https://github.com/pavelsr/enr.git@main

# pip
pip install enr     # or from GitHub:
pip install git+https://github.com/pavelsr/enr.git@main

Other installation ways

Other installation ways

Installation from source code

git clone https://github.com/pavelsr/enr.git
cd enr
# Run without installation
./enr.py example.com http://localhost:3000
# Editable install (convenient for test and development):
pip install -e .
# Install development dependencies
pip install -e ".[dev]"
# Install using flit in development mode
flit install --symlink
# Creates enr.pyz from modules:
make build  # Creates enr.pyz from modules
# Or use flit directly
flit build

Notes about pipx

Since this is a CLI utility, it's recommended to install it using pipx to avoid conflicts with other Python packages:

# Install pipx if not already installed
python -m pip install --user pipx
python -m pipx ensurepath

# Install ENR
pipx install enr

Advantages of pipx installation:

  • ✅ Isolated environment - no conflicts with other Python packages
  • ✅ Easy updates - pipx upgrade enr
  • ✅ Easy uninstall - pipx uninstall enr
  • ✅ Global availability - enr command available everywhere

If GitHub is blocked in your network

You can copy the project manually using scp or rsync from a machine where GitHub is NOT blocked:

# Using scp, only enr.pyz
scp ./enr.pyz user@host.example.com:/usr/local/bin/enr

# Using scp, whole source code
ssh user@host.example.com "mkdir -p ~/enr" && scp -r * user@host.example.com:~/enr/

# Using rsync, whole source code (requires rsync on both machines)
rsync -avz . user@host.example.com:~/enr/

Note: Replace user@host.example.com with your actual server details. The rsync command automatically creates the 'enr' folder if it doesn't exist.

Usage

usage: enr [-h] [--port PORT] [--container-name CONTAINER_NAME] [--network NETWORK] [--config-dir CONFIG_DIR] [--dry-run] [--force] [--with-letsencrypt] [--version] server_name proxy_pass

positional arguments:
  server_name           Domain name for the server
  proxy_pass            Upstream server URL (e.g., http://localhost:3000)

options:
  -h, --help            show this help message and exit
  --port PORT, -p PORT  Port to listen on (default: 80)
  --container-name CONTAINER_NAME, -n CONTAINER_NAME
                        Docker container name (defaults to server_name)
  --network NETWORK     Docker network name (default: nginx-proxy)
  --config-dir CONFIG_DIR, -d CONFIG_DIR
                        Directory to save nginx config (default: current directory)
  --dry-run             Generate config only, don't run Docker container
  --force, -f           Force overwrite existing config file
  --with-letsencrypt    Automatically add Let's Encrypt environment variables for SSL support
  --version             Show version and exit

Examples:
enr example.com http://<container_name>:3000
enr example.com http://host.docker.internal:8000 --port 3000
enr example.com http://host.docker.internal:8000 --with-letsencrypt
enr shop.example.com https://marketplace.example/seller/<seller_id>
enr example.com https://example.tilda.ws --container-name my-tilda-proxy
enr test.com http://localhost:5000 --dry-run --config-dir ./configs --force

nginx-proxy Compatibility

ENR is specifically designed to work with nginx-proxy - a popular solution for automatic Docker container proxying. ENR automatically:

  • Generates compatible nginx configurations
  • Runs containers in the nginx-proxy network (by default)
  • Sets the VIRTUAL_HOST environment variable for automatic discovery
  • Adds Let's Encrypt variables for SSL certificates when using HTTPS

Integration with nginx-proxy

# Start nginx-proxy (if not already running)
docker run -d -p 80:80 -p 443:443 \
  --name nginx-proxy \
  --restart always \
  -v /var/run/docker.sock:/tmp/docker.sock:ro \
  nginxproxy/nginx-proxy

# Imagine that you have non-dockerized service running at 8000 port locally

# Using ENR with nginx-proxy
./enr.py example.com http://host.docker.internal:8000

Quick nginx-proxy deployment

For a complete example of nginx-proxy deployment with Let\x27s Encrypt support, see: 🔗 https://gitlab.com/pavelsr/nginx-proxy

This repository contains ready-to-use scripts for quick deployment of nginx-proxy with automatic SSL certificate management.

Under the Hood

Some Technical Overview about Architecture and Implementation Details

Requirements

  • Python 3.11+ (recommended), 3.10+ (minimum)
  • Docker

For full functionality, it is recommended to use:

Dependencies

The project has no external dependencies - uses only Python standard library:

  • argparse - command line argument processing
  • pathlib - file system path operations
  • subprocess - running Docker commands
  • str.format() - nginx configuration formatting (instead of jinja2)
  • zipapp - building single script (instead of stickytape or PyInstaller)

Generated Nginx Configuration

The utility creates an nginx configuration of the following type:

server {
  server_name example.com;
  listen 80;

  location / {
    proxy_pass http://localhost:3000;
    proxy_pass_header Host;
    proxy_intercept_errors on;
    error_page 301 302 307 = @handle_redirect;
    # recursive_error_pages on;
  }

  location @handle_redirect {
    set $saved_redirect_location '$upstream_http_location';
    proxy_pass $saved_redirect_location;
  }
}

Docker Container Running

After generating the configuration, the utility runs a Docker container with the command:

# For HTTP
docker run --network nginx-proxy \
  -e VIRTUAL_HOST=example.com \
  -v $(pwd)/example.com.proxy.conf:/etc/nginx/conf.d/default.conf \
  --name example.com \
  -d --restart always nginx:alpine

# For HTTPS (Let's Encrypt variables are automatically added)
docker run --network nginx-proxy \
  -e VIRTUAL_HOST=example.com \
  -e LETSENCRYPT_HOST=example.com \
  -e LETSENCRYPT_EMAIL=443@example.com \
  -v $(pwd)/example.com.proxy.conf:/etc/nginx/conf.d/default.conf \
  --name example.com \
  -d --restart always nginx:alpine

Features & Roadmap

Implemented Features:

  • Easy to use - one command to set up reverse proxy
  • No external dependencies - only Python needed (recommended version 3.11)
  • Single script - can be installed with one curl command, without git/pip/pipx
  • Docker integration - automatic container running
  • nginx-proxy integration - nginx-proxy automatically discover containers by VIRTUAL_HOST
  • Automatic protocol addition - automatically adds http:// to domains without protocol
  • Automatic arguments addition - automatically adds arguments for support host.docker.internal
  • Automatic Let's Encrypt SSL support - automatically adds environment variables for HTTPS
  • Named configurations - files are created as {server_name}.proxy.conf for better organization
  • Single-source versionin - __init__.py
  • Flit-based build system
  • Zipapp-based single-file build

TODO (Roadmap):

  • Nginx configs as named templates
  • More high-level tests
  • traefik and other proxy servers integration

Development & Contributing

All necessary development commands are available in the Makefile following best practices. To view the complete list of commands, run:

make help
Common Makefile commands
# Install development dependencies
make install-dev

# Install pre-commit hooks
make pre-commit-install

# Run all checks (formatting, linting, tests)
make check

# Or separately:
make format    # Code formatting
make lint      # Style checking
make test      # Run tests

# Pre-commit hooks
make pre-commit-run    # Manual pre-commit hooks run
make pre-commit-clean  # Clean pre-commit cache

# Clean temporary files
make clean

Git Guidelines

Before submitting a PR:

  1. Squash your commits into a single, meaningful commit. E.g.

    git reset --soft HEAD~42
    git commit -m "feat: add new nginx configuration feature"
    
  2. Use descriptive commit messages that explain what the change does

    E.g.

    git commit -m "fix: resolve docker container startup issue (#456)"
    

    Good practice: Include issue number if one exists (as shown in the example above)

FYI: I prefer trunk-based development rather than gitflow branching model

Version Management

To change version ONLY ONE STEP REQUIRED: Update version in __init__.py: __version__ = "x.y.z"

That's it! All other files automatically get the new version when you run:

  • make build - builds single script with current version
  • make build-dist - builds distribution packages with current version
Benefits of this approach
  • Single source of truth: Version managed in ONE file only (__init__.py)
  • Automatic propagation: All other files automatically get the version from this file
  • Flit dynamic versioning: Uses flit's built-in dynamic = ["version"] feature
  • No git dependency: Version management works independently of git tags
  • No manual sync needed: Version is automatically read from module during build
Files that automatically get the version
pyenr/
├── enr/
│ └── init.py # ← MAIN VERSION FILE (change here ONLY)
├── pyproject.toml # ← gets version automatically via flit dynamic
├── setup.py # ← imports version from enr.init (legacy compatibility)
├── enr.pyz # ← single executable script (built via make build)
└── dist/ # ← packages get version automatically

Versioning F.A.Q.

Q: How to check current version? A: Run python -c "import enr; print(enr.__version__)"

Q: Version didn't update after changing version.py? A: Make sure to run make build or make build-dist after changing the version. Flit will automatically read the new version.

Q: Flit build failed? A: Make sure all files are committed to git, as flit requires a clean git state.

Versioning scheme

This project follows Semantic Versioning

Pre-commit Hooks

The project uses pre-commit hooks for automatic code quality checking before each commit

What is automatically checked:

  • ✅ Single script build (make build)
  • ✅ Code formatting (Black)
  • ✅ Style checking (ruff)
  • ✅ Test running (pytest)
  • ✅ Automatic addition of changed enr.pyz to commit

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

enr-0.1.1.tar.gz (19.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

enr-0.1.1-py3-none-any.whl (12.3 kB view details)

Uploaded Python 3

File details

Details for the file enr-0.1.1.tar.gz.

File metadata

  • Download URL: enr-0.1.1.tar.gz
  • Upload date:
  • Size: 19.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.10.12

File hashes

Hashes for enr-0.1.1.tar.gz
Algorithm Hash digest
SHA256 61052a8397532598ebaa1c4d6cb5a9f39e2501ac52e90c4a514d4913a5dd9700
MD5 080b4dd2949e34fd3de46f8acf1502c7
BLAKE2b-256 dd615b1d77583beb725f6f3a66bd9aaa8b2e033bd8bfd802d2c0e8ee8b281b54

See more details on using hashes here.

File details

Details for the file enr-0.1.1-py3-none-any.whl.

File metadata

  • Download URL: enr-0.1.1-py3-none-any.whl
  • Upload date:
  • Size: 12.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.10.12

File hashes

Hashes for enr-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 8cfd1846265b45bcfc0552ddc2556149b3539aa0d5006976ba1b4a056d813141
MD5 e52a36599d12cc71cbe909d9e42d7af5
BLAKE2b-256 875b1c19c4cc9214a5c47a6b92aedc6a2d36c49414051219b7289e7ad85f1165

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page