skillby lawless-m
Reverse Proxy Deployment
Deploy web applications behind Apache reverse proxy on rivsprod01 with relative URLs and proper path configuration
Installs: 0
Used in: 3 repos
Updated: 19h ago
$
npx ai-builder add skill lawless-m/reverse-proxy-deploymentInstalls to .claude/skills/reverse-proxy-deployment/
# Reverse Proxy Deployment
Guide for deploying web applications behind the Apache reverse proxy on rivsprod01 (dw.ramsden-international.com). Covers frontend relative URL patterns and Apache ProxyPass configuration.
## Instructions
When deploying a web application that will be accessed through the reverse proxy:
1. **Use Relative URLs**: Frontend code must use relative URLs (not absolute localhost URLs) to work behind the proxy
2. **Configure ProxyPass on rivsprod01**: Create Apache config in `/etc/apache2/proxy-conf.d/` with proper path stripping
3. **Order Matters**: More specific paths must come before general paths in Apache config
4. **Test Locally First**: Verify services are accessible from rivsprod01 before configuring proxy
5. **Use Trailing Slashes**: Both source and target URLs need trailing slashes for path stripping to work
## Examples
### Example 1: Deploy Web Application with API Backend
```
User: I have a web app with frontend on port 9273 and API on port 8765. Deploy it behind the proxy at /myapp/
Claude: I'll set up your application for reverse proxy deployment:
1. **Update Frontend URLs**: Change API calls to use relative URLs
- Before: `fetch('http://localhost:8765/api/status')`
- After: `fetch('api/status')` // Relative to current path
2. **Create Apache Config**: SSH to rivsprod01 and create `/etc/apache2/proxy-conf.d/myapp.conf`
- API path: `/myapp/api/` → backend on port 8765
- Frontend path: `/myapp/` → frontend on port 9273
- API location must come FIRST (more specific)
3. **Reload Apache**: `sudo systemctl reload apache2`
[Provides implementation using the ProxyPass Configuration pattern below]
```
### Example 2: Frontend Uses Absolute URLs
```
User: My frontend has `fetch('http://localhost:3000/api/data')` hardcoded. How do I make it work behind a proxy?
Claude: Absolute URLs won't work behind a reverse proxy. You need to update the frontend to use relative URLs:
**Options:**
1. **Best: Use relative URLs** - `fetch('api/data')` - works anywhere
2. **Alternative: Use base path** - Store base path in config, prepend to all API calls
3. **Hidden input method** - Store proxy path in HTML, read in JavaScript
For option 1 (recommended):
- Replace all `http://localhost:3000/api/` with `api/`
- The browser resolves relative to current page path
- If page is at `/myapp/`, API call goes to `/myapp/api/`
[Shows the Relative URL Pattern implementation below]
```
### Example 3: Multiple Services on Same Machine
```
User: I have 3 services running on ports 8001, 8002, 8003. Deploy all at /service1/, /service2/, /service3/
Claude: I'll configure all three services in a single Apache config file:
1. **Create single config file**: `/etc/apache2/proxy-conf.d/services.conf`
2. **Add ProxyPass for each**: More specific paths first (doesn't matter here since no overlap)
3. **Each service gets clean URLs**: Paths are stripped before forwarding
4. **Reload once**: All services go live together
[Provides implementation using the Multi-Service Configuration pattern below]
```
### Example 4: API Calls Return 404
```
User: My frontend loads but API calls return 404. Frontend is at /rabbit/, API should be at /rabbit/api/
Claude: The issue is likely ProxyPass configuration. Let me check:
1. **Verify services are reachable**: From rivsprod01, can you curl the backend directly?
- `curl http://10.99.0.3:8765/status` should work
2. **Check ProxyPass order**: API location must come BEFORE frontend location
- Wrong order: frontend `/rabbit/` catches `/rabbit/api/` requests
- Right order: API `/rabbit/api/` catches first, then frontend `/rabbit/`
3. **Verify trailing slashes**: Both paths need trailing slashes for stripping
- `ProxyPass /rabbit/api/ http://10.99.0.3:8765/`
- Without trailing slash, path isn't stripped properly
[Shows debugging steps from Troubleshooting section below]
```
---
# Reference Implementation Details
The sections below contain proven working configurations from production deployments.
**Reference Files in This Folder**:
- `rabbit.conf` - Example Apache config from Invoice OCR deployment
## Relative URL Pattern
**Purpose**: Make frontend work behind any reverse proxy path without hardcoding URLs
### HTML Method (Recommended for Simple Cases)
```html
<!-- Hidden input stores the API base path -->
<input type="hidden" id="serverUrl" value="api">
<script>
function getServerUrl() {
return document.getElementById('serverUrl').value.trim();
}
// Use in fetch calls
async function checkStatus() {
const res = await fetch(`${getServerUrl()}/status`);
const data = await res.json();
// ...
}
</script>
```
**Key Points**:
- Value is `"api"` (relative) not `"http://localhost:8765"` (absolute)
- Browser resolves `api/status` relative to current page
- If page is at `https://example.com/rabbit/`, request goes to `https://example.com/rabbit/api/status`
- Proxy strips `/rabbit/` and forwards to backend
### JavaScript Config Method (For Complex Applications)
```javascript
// config.js
const CONFIG = {
API_BASE: 'api', // Relative to current path
WS_BASE: 'ws' // WebSocket endpoint if needed
};
// Use throughout app
fetch(`${CONFIG.API_BASE}/endpoint`)
```
**When to use**:
- Multiple API endpoints
- Different base paths for dev/staging/prod
- Need to change paths without editing HTML
## ProxyPass Configuration Pattern
**Purpose**: Configure Apache to forward requests from public path to internal service
### Basic Single-Service Configuration
**File**: `/etc/apache2/proxy-conf.d/myservice.conf`
```apache
# Single service on custom path
ProxyPass /myservice/ http://10.99.0.3:8080/
ProxyPassReverse /myservice/ http://10.99.0.3:8080/
```
**Key Points**:
- Simple ProxyPass directives (no `<Location>` blocks needed)
- Trailing slashes on both source and target strip the path prefix
- Request to `/myservice/page` becomes `/page` at backend
- `ProxyPassReverse` rewrites response headers (redirects, etc.)
### API + Frontend Configuration
**File**: `/etc/apache2/proxy-conf.d/service-with-api.conf`
```apache
# Backend API - Must come FIRST (more specific path)
ProxyPass /rabbit/api/ http://10.99.0.3:8765/
ProxyPassReverse /rabbit/api/ http://10.99.0.3:8765/
# Frontend - Comes SECOND (less specific path)
ProxyPass /rabbit/ http://10.99.0.3:9273/
ProxyPassReverse /rabbit/ http://10.99.0.3:9273/
```
**Key Points**:
- API location MUST be listed first (more specific)
- If frontend is listed first, it catches API requests → 404
- Both locations strip their prefix before forwarding
- Request flow:
- `GET /rabbit/api/status` → matches first rule → `GET /status` to port 8765
- `GET /rabbit/index.html` → matches second rule → `GET /index.html` to port 9273
### Large File Upload Configuration
```apache
# Service that handles file uploads (PDFs, images, etc.)
ProxyPass /uploads/ http://10.99.0.3:7000/
ProxyPassReverse /uploads/ http://10.99.0.3:7000/
# Important for large PDF uploads
<Location /uploads/>
ProxyPass http://10.99.0.3:7000/
ProxyPassReverse http://10.99.0.3:7000/
# Allow 50MB uploads
LimitRequestBody 52428800
</Location>
```
**When to use**:
- File upload services
- PDF/image processing
- Any endpoint that receives large request bodies
## Multi-Service Configuration Pattern
**Purpose**: Deploy multiple independent services in one config file
**File**: `/etc/apache2/proxy-conf.d/all-services.conf`
```apache
# Service 1: API Gateway
ProxyPass /api/ http://10.99.0.3:8001/
ProxyPassReverse /api/ http://10.99.0.3:8001/
# Service 2: Admin Dashboard
ProxyPass /admin/ http://10.99.0.3:8002/
ProxyPassReverse /admin/ http://10.99.0.3:8002/
# Service 3: Public Website
ProxyPass /site/ http://10.99.0.3:8003/
ProxyPassReverse /site/ http://10.99.0.3:8003/
```
**Key Points**:
- Each service is completely independent
- Order doesn't matter if paths don't overlap
- All go live together when Apache reloads
- Can comment out individual services to disable temporarily
## Deployment Workflow
**Standard deployment process for new service:**
### 1. Verify Local Service Accessibility
```bash
# From local machine where service runs
curl http://10.99.0.3:8765/status # Test your port
# From rivsprod01 (SSH in)
ssh rivsprod01 "curl -s http://10.99.0.3:8765/status"
```
**Expected**: JSON response or HTML, not connection refused
### 2. Create Apache Configuration
```bash
# SSH to rivsprod01
ssh rivsprod01
# Create config file (as root or with sudo)
sudo nano /etc/apache2/proxy-conf.d/myservice.conf
# Add ProxyPass configuration (see patterns above)
# Verify syntax (optional, but recommended)
# apache2ctl configtest # May not be available
# Just proceed to reload if command not found
```
### 3. Reload Apache
```bash
sudo systemctl reload apache2
# Verify Apache is still running
sudo systemctl status apache2
```
### 4. Test Public Access
```bash
# From any machine
curl https://dw.ramsden-international.com/myservice/
# Or open in browser
```
## Troubleshooting
### 404 Not Found - Service Works Locally
**Cause**: ProxyPass configuration issue
**Diagnostic Steps**:
1. Verify service is accessible from rivsprod01:
```bash
ssh rivsprod01 "curl -s http://10.99.0.3:PORT/endpoint"
```
2. Check Apache config order:
```bash
ssh rivsprod01 "cat /etc/apache2/proxy-conf.d/myservice.conf"
```
- API paths must come before frontend paths
- Verify trailing slashes on both source and target
3. Check Apache error logs:
```bash
ssh rivsprod01 "sudo tail -50 /var/log/apache2/error.log | grep myservice"
```
**Solution**:
- Fix ProxyPass order (more specific first)
- Add missing trailing slashes
- Reload Apache: `sudo systemctl reload apache2`
### 404 on API Calls, Frontend Works
**Cause**: Frontend path is catching API requests
**Example of Wrong Configuration**:
```apache
# WRONG - Frontend catches everything
ProxyPass /app/ http://10.99.0.3:9000/
ProxyPass /app/api/ http://10.99.0.3:8000/
```
**Solution - Put API First**:
```apache
# CORRECT - API catches specific path first
ProxyPass /app/api/ http://10.99.0.3:8000/
ProxyPass /app/ http://10.99.0.3:9000/
```
### Connection Refused from rivsprod01
**Cause**: Service not listening on accessible IP
**Diagnostic**:
```bash
# On machine running service
netstat -tlnp | grep PORT
# or
ss -tlnp | grep PORT
```
**Look for**:
- `127.0.0.1:PORT` - Only listening on localhost (wrong)
- `10.99.0.3:PORT` - Listening on network IP (correct)
- `0.0.0.0:PORT` - Listening on all interfaces (also correct)
**Solution**: Configure service to bind to `0.0.0.0` or specific IP `10.99.0.3`
### Service Stops Working After Apache Reload
**Cause**: Configuration syntax error
**Diagnostic**:
```bash
ssh rivsprod01 "sudo systemctl status apache2"
```
**Solution**:
1. Check for typos in ProxyPass URLs
2. Verify no missing quotes or slashes
3. Look at error log: `sudo tail /var/log/apache2/error.log`
4. Fix config and reload again
### CORS Errors in Browser Console
**Cause**: Backend not configured for CORS, or wrong origin
**Solution (Backend)**: Enable CORS in your service
```python
# FastAPI example
from fastapi.middleware.cors import CORSMiddleware
app.add_middleware(
CORSMiddleware,
allow_origins=["*"], # Or specific domain
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
```
**Note**: Proxy handles forwarding; backend sees requests from proxy IP, not client
## IP Addresses Reference
| Hostname | IP Address | Purpose |
|----------|-----------|---------|
| rivsprod01 | 10.99.0.2 | Reverse proxy server (Apache) |
| pogs (local) | 10.99.0.3 | Development machine running services |
**Service Configuration**:
- Services should bind to `10.99.0.3` or `0.0.0.0` to be accessible from rivsprod01
- Use `http://10.99.0.3:PORT` in ProxyPass target URLs
- Never use `localhost` or `127.0.0.1` in ProxyPass targets
## File Locations on rivsprod01
| Path | Purpose |
|------|---------|
| `/etc/apache2/proxy-conf.d/*.conf` | Proxy configurations (add yours here) |
| `/etc/apache2/sites-available/default-ssl.conf` | Main SSL site config (includes proxy-conf.d) |
| `/var/log/apache2/error.log` | Apache error log |
| `/var/log/apache2/access.log` | Access log (if needed) |
**Important**: Configs in `/etc/apache2/proxy-conf.d/` are automatically included by the SSL site configuration.
## Best Practices Summary
1. **Always use relative URLs** in frontend code for proxy compatibility
2. **Test service accessibility** from rivsprod01 before configuring proxy
3. **Put specific paths first** in Apache config (API before frontend)
4. **Use trailing slashes** on both ProxyPass source and target for path stripping
5. **Reload Apache** after config changes: `sudo systemctl reload apache2`
6. **Keep configs organized** - one file per application or related service group
7. **Document your paths** - add comments explaining what each ProxyPass does
8. **Test both frontend and API** after deployment to verify routingQuick Install
$
npx ai-builder add skill lawless-m/reverse-proxy-deploymentDetails
- Type
- skill
- Author
- lawless-m
- Slug
- lawless-m/reverse-proxy-deployment
- Created
- 1w ago