{"id":37177485,"url":"https://github.com/janmbaco/go-reverseproxy-ssl","last_synced_at":"2026-01-14T20:42:14.871Z","repository":{"id":57518964,"uuid":"208439310","full_name":"janmbaco/go-reverseproxy-ssl","owner":"janmbaco","description":"Deploy a reverse proxy that allow encrypt the communication with a valid cetificate provice by Let's Encrypt.","archived":false,"fork":false,"pushed_at":"2025-11-11T02:40:44.000Z","size":350,"stargazers_count":3,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-11-11T04:13:44.629Z","etag":null,"topics":["go","golang","lets-encrypt","proxy-server","reverse-proxy","ssl","tsl"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/janmbaco.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null},"funding":{"github":["janmbaco"]}},"created_at":"2019-09-14T12:46:26.000Z","updated_at":"2025-11-11T02:40:47.000Z","dependencies_parsed_at":"2024-03-02T00:22:48.623Z","dependency_job_id":"5c3a3191-856c-4317-a1d3-173df80b2f35","html_url":"https://github.com/janmbaco/go-reverseproxy-ssl","commit_stats":null,"previous_names":[],"tags_count":16,"template":false,"template_full_name":null,"purl":"pkg:github/janmbaco/go-reverseproxy-ssl","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/janmbaco%2Fgo-reverseproxy-ssl","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/janmbaco%2Fgo-reverseproxy-ssl/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/janmbaco%2Fgo-reverseproxy-ssl/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/janmbaco%2Fgo-reverseproxy-ssl/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/janmbaco","download_url":"https://codeload.github.com/janmbaco/go-reverseproxy-ssl/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/janmbaco%2Fgo-reverseproxy-ssl/sbom","scorecard":{"id":505372,"data":{"date":"2025-08-11","repo":{"name":"github.com/janmbaco/go-reverseproxy-ssl","commit":"94fd139993eff351952e48f8e0a0c4c821f1ddad"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":4.6,"checks":[{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Dangerous-Workflow","score":10,"reason":"no dangerous workflow patterns detected","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Code-Review","score":0,"reason":"Found 0/21 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Token-Permissions","score":0,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Info: jobLevel 'actions' permission set to 'read': .github/workflows/codeql-analysis.yml:26","Info: jobLevel 'contents' permission set to 'read': .github/workflows/codeql-analysis.yml:27","Warn: no topLevel permission defined: .github/workflows/codeql-analysis.yml:1","Warn: no topLevel permission defined: .github/workflows/go.yml:1","Warn: no topLevel permission defined: .github/workflows/version_go.yml:1","Info: no jobLevel write permissions found"],"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: GNU General Public License v3.0: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Pinned-Dependencies","score":4,"reason":"dependency not pinned by hash detected -- score normalized to 4","details":["Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/codeql-analysis.yml:40: update your workflow using https://app.stepsecurity.io/secureworkflow/janmbaco/go-reverseproxy-ssl/codeql-analysis.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/codeql-analysis.yml:44: update your workflow using https://app.stepsecurity.io/secureworkflow/janmbaco/go-reverseproxy-ssl/codeql-analysis.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/codeql-analysis.yml:55: update your workflow using https://app.stepsecurity.io/secureworkflow/janmbaco/go-reverseproxy-ssl/codeql-analysis.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/codeql-analysis.yml:69: update your workflow using https://app.stepsecurity.io/secureworkflow/janmbaco/go-reverseproxy-ssl/codeql-analysis.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/go.yml:14: update your workflow using https://app.stepsecurity.io/secureworkflow/janmbaco/go-reverseproxy-ssl/go.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/go.yml:17: update your workflow using https://app.stepsecurity.io/secureworkflow/janmbaco/go-reverseproxy-ssl/go.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/version_go.yml:13: update your workflow using https://app.stepsecurity.io/secureworkflow/janmbaco/go-reverseproxy-ssl/version_go.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/version_go.yml:16: update your workflow using https://app.stepsecurity.io/secureworkflow/janmbaco/go-reverseproxy-ssl/version_go.yml/master?enable=pin","Warn: containerImage not pinned by hash: Dockerfile:1: pin your Docker image by updating ubuntu:rolling to ubuntu:rolling@sha256:95a416ad2446813278ec13b7efdeb551190c94e12028707dd7525632d3cec0d1","Info:   0 out of   8 GitHub-owned GitHubAction dependencies pinned","Info:   3 out of   3 third-party GitHubAction dependencies pinned","Info:   0 out of   1 containerImage dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"Branch-Protection","score":-1,"reason":"internal error: error during branchesHandler.setup: internal error: githubv4.Query: Resource not accessible by integration","details":null,"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"SAST","score":7,"reason":"SAST tool detected but not run on all commits","details":["Info: SAST configuration detected: CodeQL","Warn: 3 commits out of 14 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}}]},"last_synced_at":"2025-08-19T23:09:03.527Z","repository_id":57518964,"created_at":"2025-08-19T23:09:03.527Z","updated_at":"2025-08-19T23:09:03.527Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28434491,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-14T18:57:19.464Z","status":"ssl_error","status_checked_at":"2026-01-14T18:52:48.501Z","response_time":107,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["go","golang","lets-encrypt","proxy-server","reverse-proxy","ssl","tsl"],"created_at":"2026-01-14T20:42:13.951Z","updated_at":"2026-01-14T20:42:14.860Z","avatar_url":"https://github.com/janmbaco.png","language":"Go","funding_links":["https://github.com/sponsors/janmbaco"],"categories":[],"sub_categories":[],"readme":"# Go ReverseProxy SSL\n\n[![Go Report Card](https://img.shields.io/badge/go%20report-A+-brightgreen.svg?style=flat)](https://goreportcard.com/report/github.com/janmbaco/go-reverseproxy-ssl/v3)\n[![Go Version](https://img.shields.io/badge/go-1.25+-blue.svg)](https://golang.org/dl/)\n[![License](https://img.shields.io/badge/license-GPL%20v3.0-green.svg)](LICENSE)\n[![Release](https://img.shields.io/badge/release-v3.0.0-blue.svg)](https://github.com/janmbaco/go-reverseproxy-ssl/releases/tag/v3.0.0)\n\n**Add automatic HTTPS to any web app, API, or gRPC service in 2 minutes.** No Nginx config, no manual certificates — just point your domain and run.\n\n## What It Does\n\nYou have a web app running on `localhost:3000`. You want:\n- ✓ **Automatic HTTPS** with Let's Encrypt certificates\n- ✓ **No configuration hell** (just a simple JSON file)\n- ✓ **Multiple apps/APIs** on different subdomains\n- ✓ **gRPC and SSH** support with the same certificates\n- ✓ **Web UI** to manage virtual hosts\n\nThis reverse proxy handles all of that automatically.\n\n```\nYour App (localhost:3000) + This Proxy = https://yourdomain.com (automatic cert)\n```\n\n### When to Use This\n\n**✅ Primary use case:**\n- VPS/dedicated server (DigitalOcean, Linode, Hetzner, OVH)\n- Cloud VM (AWS EC2, Google Compute Engine, Azure Virtual Machines)\n- Self-hosted server at home or office\n- Docker Compose setup with multiple services\n- Full control over ports 80 and 443\n\n**✅ Advanced use case (centralized reverse proxy):**\n- Unified entry point for multiple PaaS backends (Heroku, Vercel, Azure App Service, etc.)\n- Custom domain management with Let's Encrypt for all services\n- Route `api.yourdomain.com` → Heroku app, `web.yourdomain.com` → Vercel, etc.\n\n---\n\n## Quick Start\n\n### Prerequisites\n\n1. **Your own server** (VPS or cloud VM with root access)\n2. **Domain name** pointed to your server's IP (e.g., `example.com` → `203.0.113.10`)\n3. **Ports 80 and 443** open in your firewall\n4. **Docker** (recommended) or Go 1.25+\n\n### Example 1: Single Web Application\n\nYou have a Node.js API running on port 3000 in your DigitalOcean droplet. Want it accessible via `https://myapp.com` with automatic SSL:\n\n**Using Docker with environment variables:**\n\n```bash\ndocker run -d \\\n  --name reverseproxy \\\n  -p 80:80 -p 443:443 -p 8081:8081 \\\n  -e DOMAIN=myapp.com \\\n  -e BACKEND_HOST=localhost \\\n  -e BACKEND_PORT=3000 \\\n  -e BACKEND_SCHEME=http \\\n  -v reverseproxy-certs:/app/certs \\\n  --network host \\\n  janmbaco/go-reverseproxy-ssl:v3\n```\n\n**That's it!** Visit `https://myapp.com` — certificate is automatically issued by Let's Encrypt.\n\n**How it works:**\n1. DNS `myapp.com` → Your VPS IP (203.0.113.10)\n2. Proxy listens on ports 80/443\n3. Let's Encrypt validates via HTTP-01 challenge (port 80)\n4. Certificate issued and cached in `/app/certs`\n5. Proxy forwards HTTPS requests to `http://localhost:3000`\n\n**Platform-specific notes:**\n- **DigitalOcean/Linode Droplet**: Use `--network host` or backend service IP\n- **AWS EC2 / Azure VM**: Ensure ports 80/443 in security groups/NSG\n- **Docker Compose**: Replace `localhost` with service name (see Example 3)\n\n---\n\n### Example 2: Multiple Subdomains (ENV vars only)\n\nYou have three services and want separate subdomains with automatic HTTPS — **no config.json needed**:\n- React app on port 3000 → `www.myapp.com`\n- Express API on port 8080 → `api.myapp.com`\n- Admin dashboard on port 5000 → `app.myapp.com`\n\n**Using Docker with environment variables:**\n\n```bash\ndocker run -d \\\n  --name reverseproxy \\\n  -p 80:80 -p 443:443 -p 8081:8081 \\\n  -e WWW_DOMAIN=www.myapp.com \\\n  -e WWW_BACKEND=localhost:3000 \\\n  -e API_DOMAIN=api.myapp.com \\\n  -e API_BACKEND=localhost:8080 \\\n  -e APP_DOMAIN=app.myapp.com \\\n  -e APP_BACKEND=localhost:5000 \\\n  -v reverseproxy-certs:/app/certs \\\n  --network host \\\n  janmbaco/go-reverseproxy-ssl:v3\n```\n\n**Result:**\n- `https://www.myapp.com` → React app (automatic cert)\n- `https://api.myapp.com` → Express API (automatic cert)\n- `https://app.myapp.com` → Admin dashboard (automatic cert)\n\n**Backend format:**\n- Basic: `host:port` (defaults to HTTP)\n- With scheme: `host:port:https` (for HTTPS backends)\n\n**Examples:**\n```bash\n-e WWW_BACKEND=frontend-service:3000           # HTTP backend\n-e API_BACKEND=api.herokuapp.com:443:https    # HTTPS backend\n-e APP_BACKEND=172.17.0.1:5000                # Docker bridge IP\n```\n\n---\n\n### Example 3: Multiple Services (Docker Compose)\n\nSame setup as Example 2, but using Docker Compose with custom `config.json` for advanced features:\n\nYou have:\n- Frontend on port 3000 → `www.myapp.com`\n- API on port 8080 → `api.myapp.com`\n- Admin panel on port 5000 → `admin.myapp.com`\n\n**docker-compose.yml:**\n\n```yaml\nversion: \"3.9\"\n\nservices:\n  reverseproxy:\n    image: janmbaco/go-reverseproxy-ssl:v3\n    ports:\n      - \"80:80\"\n      - \"443:443\"\n      - \"8081:8081\"\n    volumes:\n      - ./config.json:/app/config/config.json:ro\n      - certs:/app/certs\n    networks: [app]\n\n  frontend:\n    image: my-frontend:latest\n    expose: [\"3000\"]\n    networks: [app]\n\n  api:\n    image: my-api:latest\n    expose: [\"8080\"]\n    networks: [app]\n\n  admin:\n    image: my-admin:latest\n    expose: [\"5000\"]\n    networks: [app]\n\nnetworks:\n  app:\nvolumes:\n  certs:\n```\n\n**config.json:**\n\n```json\n{\n  \"web_virtual_hosts\": [\n    {\n      \"from\": \"www.myapp.com\",\n      \"scheme\": \"http\",\n      \"host_name\": \"frontend\",\n      \"port\": 3000\n    },\n    {\n      \"from\": \"api.myapp.com\",\n      \"scheme\": \"http\",\n      \"host_name\": \"api\",\n      \"port\": 8080\n    },\n    {\n      \"from\": \"admin.myapp.com\",\n      \"scheme\": \"http\",\n      \"host_name\": \"admin\",\n      \"port\": 5000\n    }\n  ],\n  \"default_host\": \"www.myapp.com\",\n  \"reverse_proxy_port\": \":443\",\n  \"config_ui_port\": \":8081\"\n}\n```\n\nRun: `docker-compose up -d`\n\n**Result:**\n- `https://www.myapp.com` → frontend (automatic cert)\n- `https://api.myapp.com` → API (automatic cert)\n- `https://admin.myapp.com` → admin panel (automatic cert)\n\n---\n\n### Example 4: gRPC-Web Service\n\nBackend gRPC service on port 9090, expose as `https://grpc.myapp.com` for browser access:\n\n**Simple config (transparent mode - proxies all services/methods):**\n\n```json\n{\n  \"grpc_web_virtual_hosts\": [\n    {\n      \"from\": \"grpc.myapp.com\",\n      \"scheme\": \"http\",\n      \"host_name\": \"grpc-service\",\n      \"port\": 9090,\n      \"grpc_web_proxy\": {\n        \"is_transparent_server\": true,\n        \"allow_all_origins\": true\n      }\n    }\n  ],\n  \"default_host\": \"grpc.myapp.com\",\n  \"reverse_proxy_port\": \":443\"\n}\n```\n\n**Advanced config (selective service/method proxying):**\n\n```json\n{\n  \"grpc_web_virtual_hosts\": [\n    {\n      \"from\": \"grpc.myapp.com\",\n      \"scheme\": \"http\",\n      \"host_name\": \"grpc-service\",\n      \"port\": 9090,\n      \"grpc_web_proxy\": {\n        \"grpc_services\": {\n          \"hello.HelloService\": [\"SayHello\", \"ServerStreamingChat\"],\n          \"user.UserService\": [\"GetUser\", \"UpdateUser\"]\n        },\n        \"is_transparent_server\": false,\n        \"authority\": \"grpc-service:9090\",\n        \"allow_all_origins\": false,\n        \"allowed_origins\": [\"https://myapp.com\"]\n      }\n    }\n  ],\n  \"default_host\": \"grpc.myapp.com\",\n  \"reverse_proxy_port\": \":443\"\n}\n```\n\n**JavaScript gRPC-Web client:**\n\n```javascript\nconst client = new GreeterClient('https://grpc.myapp.com');\n```\n\n\u003e **Note**: The `grpc_web_proxy` field is **required** for gRPC-Web virtual hosts. Set `is_transparent_server: true` to proxy all services automatically, or set it to `false` and specify `grpc_services` for fine-grained control.\n\n---\n\n## Web Configuration UI\n\nInstead of editing JSON, use the built-in web interface:\n\n1. **Access UI**: `http://localhost:8081` (or your server IP:8081)\n2. **Add virtual host**: Click \"New Virtual Host\"\n3. **Fill form**:\n   - Domain: `example.com`\n   - Backend Host: `my-app`\n   - Backend Port: `3000`\n   - Protocol: `http`\n4. **Save** → Configuration updated automatically\n\n**UI Features:**\n- Visual virtual host management\n- Certificate upload for custom certs\n- Configuration preview (JSON)\n- Live validation\n\n\u003e **Security**: Bind to `127.0.0.1:8081` for localhost-only access, or put behind authentication.\n\n---\n\n## Common Use Cases\n\n### Use Case 1: \"I have a React dev server on localhost:3000\"\n\n**Minimal config:**\n\n```json\n{\n  \"web_virtual_hosts\": [\n    {\n      \"from\": \"myapp.com\",\n      \"scheme\": \"http\",\n      \"host_name\": \"localhost\",\n      \"port\": 3000\n    }\n  ],\n  \"default_host\": \"myapp.com\",\n  \"reverse_proxy_port\": \":443\"\n}\n```\n\n**Docker command:**\n\n```bash\ndocker run -d \\\n  -p 80:80 -p 443:443 \\\n  -e DOMAIN=myapp.com \\\n  -e BACKEND_HOST=host.docker.internal \\\n  -e BACKEND_PORT=3000 \\\n  -v certs:/app/certs \\\n  janmbaco/go-reverseproxy-ssl:v3\n```\n\n**Verify:**\n```bash\ncurl -I https://myapp.com  # Should return 200 with Let's Encrypt cert\n```\n\n---\n\n### Use Case 2: \"I have API + Frontend in Docker Compose\"\n\n**Scenario**: Express API (port 8080) + Next.js (port 3000)\n\n**docker-compose.yml:**\n\n```yaml\nservices:\n  proxy:\n    image: janmbaco/go-reverseproxy-ssl:v3\n    ports: [\"80:80\", \"443:443\"]\n    volumes:\n      - ./config.json:/app/config/config.json:ro\n      - certs:/app/certs\n    networks: [backend]\n\n  api:\n    build: ./api\n    expose: [\"8080\"]\n    networks: [backend]\n\n  web:\n    build: ./web\n    expose: [\"3000\"]\n    networks: [backend]\n\nnetworks:\n  backend:\nvolumes:\n  certs:\n```\n\n**config.json:**\n\n```json\n{\n  \"web_virtual_hosts\": [\n    {\"from\": \"api.example.com\", \"host_name\": \"api\", \"port\": 8080, \"scheme\": \"http\"},\n    {\"from\": \"www.example.com\", \"host_name\": \"web\", \"port\": 3000, \"scheme\": \"http\"}\n  ],\n  \"default_host\": \"www.example.com\",\n  \"reverse_proxy_port\": \":443\"\n}\n```\n\n**Result:**\n- Frontend: `https://www.example.com`\n- API: `https://api.example.com`\n- Both with automatic HTTPS\n\n---\n\n### Use Case 3: \"I need custom SSL certificate (not Let's Encrypt)\"\n\n**When you need this:**\n- Internal/private CA\n- Wildcard certificate\n- EV (Extended Validation) certificate\n- Development with self-signed cert\n\n**config.json:**\n\n```json\n{\n  \"web_virtual_hosts\": [\n    {\n      \"from\": \"secure.internal.com\",\n      \"scheme\": \"http\",\n      \"host_name\": \"backend\",\n      \"port\": 8080,\n      \"server_certificate\": {\n        \"certificate_path\": \"/certs/internal.pem\",\n        \"private_key_path\": \"/certs/internal-key.pem\"\n      }\n    }\n  ],\n  \"default_host\": \"secure.internal.com\",\n  \"reverse_proxy_port\": \":443\"\n}\n```\n\n**Docker mount:**\n\n```bash\ndocker run -d \\\n  -p 80:80 -p 443:443 \\\n  -v ./certs:/certs:ro \\\n  -v ./config.json:/app/config/config.json:ro \\\n  janmbaco/go-reverseproxy-ssl:v3\n```\n\n**Alternative: Using environment variables with custom certificates:**\n\n```bash\ndocker run -d \\\n  --name reverseproxy \\\n  -p 80:80 -p 443:443 -p 8081:8081 \\\n  -e DOMAIN=secure.internal.com \\\n  -e BACKEND_HOST=backend \\\n  -e BACKEND_PORT=8080 \\\n  -e BACKEND_SCHEME=http \\\n  -e CERT_FILE=/certs/internal.pem \\\n  -e KEY_FILE=/certs/internal-key.pem \\\n  -v ./certs:/certs:ro \\\n  -v certs-storage:/app/certs \\\n  janmbaco/go-reverseproxy-ssl:v3\n```\n\n\u003e **Note**: When using custom certificates with ENV vars, mount your certificate directory with `-v` and specify paths with `CERT_FILE` and `KEY_FILE` environment variables.\n\n---\n\n### Use Case 4: \"I want path-based routing (example.com/api vs example.com/web)\"\n\n**Scenario**: Route `/api/*` to API service, `/` to web\n\n**Limitation**: This proxy routes by **domain** (not path). Use a single domain with backend path rewriting:\n\n**Workaround 1 - Use subdomains** (recommended):\n- `api.example.com` → API service\n- `www.example.com` → Web service\n\n**Workaround 2 - Backend handles routing**:\n- Single virtual host → Nginx/Traefik backend that does path routing\n\n**Future**: Path-based routing planned for v3.1\n\n---\n\n## Configuration\n\n### Minimal Configuration\n\n```json\n{\n  \"web_virtual_hosts\": [\n    {\n      \"from\": \"example.com\",\n      \"scheme\": \"http\",\n      \"host_name\": \"localhost\",\n      \"port\": 8080\n    }\n  ],\n  \"default_host\": \"example.com\",\n  \"reverse_proxy_port\": \":443\"\n}\n```\n\n### Full Configuration Reference\n\nSee [docs/CONFIGURATION.md](docs/CONFIGURATION.md) for complete reference including:\n- All virtual host types (Web, gRPC-Web)\n- Custom headers\n- Client certificate authentication (mTLS)\n- Backend mTLS\n- Logging configuration\n- Advanced options\n\n\u003e **Note**: `grpc_virtual_hosts`, `grpc_json_virtual_hosts`, and `ssh_virtual_hosts` are deprecated and ignored.\n\n---\n\n## DNS Setup\n\n### Required DNS Records\n\nPoint your domain to your server's public IP:\n\n```\n# A record\nexample.com        A    203.0.113.10\napi.example.com    A    203.0.113.10\nadmin.example.com  A    203.0.113.10\n\n# Optional: IPv6\nexample.com        AAAA 2001:db8::1\n```\n\n### Wildcard Subdomain (optional)\n\n```\n*.example.com      A    203.0.113.10\n```\n\nThen any subdomain works: `foo.example.com`, `bar.example.com`, etc.\n\n---\n\n## Firewall Configuration\n\n### Required Ports\n\n| Port | Protocol | Purpose |\n|------|----------|---------|\n| 80 | TCP | HTTP (redirects to HTTPS, Let's Encrypt challenges) |\n| 443 | TCP | HTTPS (main traffic) |\n| 8081 | TCP | Web Configuration UI (optional, can be localhost-only) |\n\n### Allow Ports\n\n**Linux (UFW):**\n```bash\nsudo ufw allow 80/tcp\nsudo ufw allow 443/tcp\nsudo ufw reload\n```\n\n**Linux (firewalld):**\n```bash\nsudo firewall-cmd --permanent --add-service=http\nsudo firewall-cmd --permanent --add-service=https\nsudo firewall-cmd --reload\n```\n\n**AWS Security Group:**\n```\nHTTP   TCP  80   0.0.0.0/0\nHTTPS  TCP  443  0.0.0.0/0\n```\n\n**Docker Host:**\n```bash\n# Ports automatically mapped with -p flag\ndocker run -p 80:80 -p 443:443 ...\n```\n\n---\n\n## FAQ\n\n### How does Let's Encrypt work?\n\n1. You request `https://example.com`\n2. Proxy checks for certificate in `/app/certs/example.com/`\n3. If not found, requests certificate from Let's Encrypt\n4. Let's Encrypt validates ownership via HTTP-01 challenge (port 80)\n5. Certificate issued and cached\n6. Automatically renewed 30 days before expiration\n\n**Requirements**: DNS must point to your server, port 80 accessible from internet.\n\n---\n\n### Can I use my own certificates?\n\nYes! Set `server_certificate` in virtual host config:\n\n```json\n{\n  \"from\": \"example.com\",\n  \"server_certificate\": {\n    \"certificate_path\": \"/certs/example.pem\",\n    \"private_key_path\": \"/certs/example-key.pem\"\n  }\n}\n```\n\nOr set default certificate for all domains:\n\n```json\n{\n  \"default_server_cert\": \"/certs/default.pem\",\n  \"default_server_key\": \"/certs/default-key.pem\"\n}\n```\n\n---\n\n### How do I handle multiple backends for the same domain?\n\n**Short answer**: Not directly supported.\n\n**Alternatives**:\n1. **Use different subdomains**: `app1.example.com`, `app2.example.com`\n2. **Backend load balancer**: Proxy → Nginx/HAProxy → Multiple backends\n3. **Wait for v3.1**: Load balancing feature planned\n\n---\n\n### What about Docker networking?\n\n**Docker Desktop (Mac/Windows)**:\n- Use `host.docker.internal` to access host machine\n\n**Docker Linux**:\n- Use `172.17.0.1` (default bridge gateway)\n- Or use Docker Compose with service names\n\n**Docker Compose** (recommended):\n```yaml\nservices:\n  proxy:\n    networks: [backend]\n  app:\n    networks: [backend]\n```\n\nConfig:\n```json\n{\n  \"host_name\": \"app\",  // Service name, not localhost\n  \"port\": 3000\n}\n```\n\n---\n\n### How do I check logs?\n\n**Docker logs:**\n```bash\ndocker logs -f reverseproxy\n```\n\n**Log files** (inside container):\n```\n/app/logs/YYYY-MM-DD.log\n```\n\n**Log levels** (in config.json):\n```json\n{\n  \"log_console_level\": 4,  // 1=Fatal, 2=Error, 3=Warning, 4=Info, 5=Trace\n  \"log_file_level\": 4\n}\n```\n\n---\n\n### Certificate not working?\n\nSee [docs/TROUBLESHOOTING.md](docs/TROUBLESHOOTING.md) for detailed troubleshooting, including:\n- Let's Encrypt validation failures\n- DNS propagation issues\n- Firewall blocking port 80\n- Docker networking problems\n\n---\n\n### Can I use this for gRPC-Web (from browser)?\n\nYes! Use `grpc_web_virtual_hosts`:\n\n```json\n{\n  \"grpc_web_virtual_hosts\": [\n    {\n      \"from\": \"grpcweb.example.com\",\n      \"scheme\": \"http\",\n      \"host_name\": \"grpc-backend\",\n      \"port\": 9090\n    }\n  ],\n  \"default_host\": \"grpcweb.example.com\",\n  \"reverse_proxy_port\": \":443\"\n}\n```\n\nThen use gRPC-Web client in JavaScript/TypeScript.\n\n---\n\n## Installation Options\n\n### Docker (Recommended)\n\n**Quick start with ENV vars:**\n```bash\ndocker run -d \\\n  --name reverseproxy \\\n  -p 80:80 -p 443:443 -p 8081:8081 \\\n  -e DOMAIN=example.com \\\n  -e BACKEND_HOST=host.docker.internal \\\n  -e BACKEND_PORT=3000 \\\n  -v certs:/app/certs \\\n  janmbaco/go-reverseproxy-ssl:v3\n```\n\n**With custom config file:**\n```bash\ndocker run -d \\\n  --name reverseproxy \\\n  -p 80:80 -p 443:443 \\\n  -v ./configs/config.json:/app/config/config.json:ro \\\n  -v certs:/app/certs \\\n  janmbaco/go-reverseproxy-ssl:v3\n```\n\n---\n\n### Docker Compose\n\nSee `docker-compose.quickstart.yml` for complete example.\n\n---\n\n### Build from Source\n\n**Requirements**: Go 1.25+\n\n```bash\ngit clone https://github.com/janmbaco/go-reverseproxy-ssl.git\ncd go-reverseproxy-ssl\ngo build -o bin/reverseproxy ./cmd/reverseproxy\n./bin/reverseproxy --config configs/config.json\n```\n\n---\n\n### Go Module\n\n```bash\ngo get github.com/janmbaco/go-reverseproxy-ssl/v3\n```\n\n```go\npackage main\n\nimport \"github.com/janmbaco/go-reverseproxy-ssl/v3/src/infrastructure\"\n\nfunc main() {\n    bootstrapper := infrastructure.NewServerBootstrapper(\"config.json\")\n    bootstrapper.Start()\n}\n```\n\n---\n\n## Features\n\n- ✓ **Automatic HTTPS** with Let's Encrypt (HTTP-01 challenge)\n- ✓ **HTTP → HTTPS** automatic redirect\n- ✓ **Multiple protocols**: HTTP/1.1, HTTP/2, gRPC-Web\n- ✓ **Web Configuration UI** for managing virtual hosts\n- ✓ **Custom certificates** support (BYO cert)\n- ✓ **Client certificate authentication** (mTLS)\n- ✓ **Custom response headers**\n- ✓ **Lightweight**: ~20MB Docker image (Alpine-based)\n- ✓ **Zero-downtime** certificate renewal\n- ✓ **Structured logging** with configurable levels\n\n---\n\n## Documentation\n\n- **[CONFIGURATION.md](docs/CONFIGURATION.md)** - Complete configuration reference\n- **[TROUBLESHOOTING.md](docs/TROUBLESHOOTING.md)** - Common issues and solutions\n- **[ARCHITECTURE.md](docs/ARCHITECTURE.md)** - Internal architecture details\n- **[UPGRADE-3.0.md](UPGRADE-3.0.md)** - Migration guide from v2.x\n\n---\n\n## What's New in v3.0\n\n- **Clean Architecture**: Complete restructure for better maintainability\n- **Web Configuration UI**: Manage virtual hosts without editing JSON\n- **Improved DI**: Powered by go-infrastructure v2.0\n- **Breaking Changes**: See [UPGRADE-3.0.md](UPGRADE-3.0.md) for migration guide\n\n---\n\n## License\n\nGNU General Public License v3.0 - see [LICENSE](LICENSE) file.\n\n**Key points**:\n- ✓ Free to use, modify, and distribute\n- ✓ Source code must be provided with distributions\n- ✓ Derivative works must also be GPL v3.0\n- ✓ No warranty\n\n\n---\n\n## Testing\n\n### Unit Tests\n\nRun all unit tests (no external dependencies required):\n\n```bash\ngo test ./internal/... -v\n```\n\nUnit tests cover:\n- Virtual host routing logic\n- Configuration parsing\n- Certificate management\n- HTTP redirector functionality\n- Server state management\n\n### Integration Tests\n\nIntegration tests require Docker and test end-to-end functionality with real HTTPS certificates and backend services.\n\n**Automated execution (recommended):**\n\n```bash\n# Windows (PowerShell)\n.\\scripts\\run-integration-tests.ps1\n\n# Linux/macOS (Bash)\n./scripts/run-integration-tests.sh\n```\n\n**Config UI specific tests:**\n\n```bash\n# Windows (PowerShell) - Config UI only\n.\\scripts\\run-config-ui-integration-tests.ps1\n\n# Linux/macOS (Bash) - Config UI only\n./scripts/run-config-ui-integration-tests.sh\n```\n\nThe script automatically:\n1. Generates temporary self-signed certificates using `alpine/openssl`\n2. Builds the Docker image from `build/package/Dockerfile`\n3. Starts all services with Docker Compose (`docker-compose.test.yml`)\n4. Waits for reverse proxy to be ready (health check on port 443)\n5. Waits for config UI to be ready (health check on port 8081)\n6. Runs integration tests with verbose output\n7. Cleans up all containers and temporary certificates\n\n**What gets tested:**\n- ✅ HTTP-to-HTTPS reverse proxy with multiple backends\n- ✅ gRPC-Web proxy with streaming support\n- ✅ TLS certificate loading and permissions\n- ✅ Docker networking between services\n- ✅ Virtual host routing by domain\n- ✅ **Config UI HTML pages and API endpoints**\n- ✅ **Virtual host CRUD operations via API**\n- ✅ **Certificate upload functionality**\n\n**Test services (Docker Compose):**\n- `reverse-proxy` - Main application under test (ports 443 + 8081 for config UI)\n- `backend-8080` through `backend-8083` - Nginx backends for web virtual hosts\n- `grpc-backend` - gRPC server for gRPC-Web tests\n\n**Manual execution:**\n```bash\n# Build test image\ndocker build -f build/package/Dockerfile -t go-reverseproxy-ssl:test .\n\n# Generate certificates\ndocker run --rm -v ${PWD}/integration/testdata:/certs alpine/openssl req -x509 \\\n  -newkey rsa:2048 -nodes -days 365 \\\n  -keyout /certs/localhost-key.pem -out /certs/localhost-cert.pem \\\n  -subj \"/CN=localhost\"\n\n# Start all services\ndocker-compose -f docker-compose.test.yml up -d\n\n# Run tests\nINTEGRATION_TEST=true go test -v ./integration/...\n\n# Cleanup\ndocker-compose -f docker-compose.test.yml down\n```\n\n**CI/CD:**\n- Unit tests run automatically on every push/PR\n- Integration tests can be triggered manually or on schedule\n- Use `INTEGRATION_TEST=true` environment variable to enable integration tests\n\n### Test Coverage\n\n- **Unit Tests**: 60+ tests covering all business logic\n- **Integration Tests**: End-to-end HTTP/HTTPS/gRPC-Web proxy testing\n- **Config UI Tests**: Complete web interface and API testing\n- **Certificate Tests**: TLS certificate loading, permissions, and validation\n- **CI Coverage**: Automated testing with GitHub Actions\n\n---\n\n## Contributing\n\nContributions welcome! See [CONTRIBUTING.md](CONTRIBUTING.md).\n\n---\n\n## Support\n\n- **Issues**: [GitHub Issues](https://github.com/janmbaco/go-reverseproxy-ssl/issues)\n- **Discussions**: [GitHub Discussions](https://github.com/janmbaco/go-reverseproxy-ssl/discussions)\n\n---\n\n**Version**: v3.0.0  \n**Go Version**: 1.25+  \n**License**: GPL v3.0\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjanmbaco%2Fgo-reverseproxy-ssl","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjanmbaco%2Fgo-reverseproxy-ssl","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjanmbaco%2Fgo-reverseproxy-ssl/lists"}