Reverse Proxy Deployment
Deploy web applications behind Apache reverse proxy on rivsprod01 with relative URLs and proper path configuration
$ 安裝
git clone https://github.com/lawless-m/claude-skills /tmp/claude-skills && cp -r /tmp/claude-skills/.claude/skills/Reverse%20Proxy%20Deployment ~/.claude/skills/claude-skills// tip: Run this command in your terminal to install the skill
name: Reverse Proxy Deployment description: Deploy web applications behind Apache reverse proxy on rivsprod01 with relative URLs and proper path configuration
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:
- Use Relative URLs: Frontend code must use relative URLs (not absolute localhost URLs) to work behind the proxy
- Configure ProxyPass on rivsprod01: Create Apache config in
/etc/apache2/proxy-conf.d/with proper path stripping - Order Matters: More specific paths must come before general paths in Apache config
- Test Locally First: Verify services are accessible from rivsprod01 before configuring proxy
- 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)
<!-- 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/statusrelative to current page - If page is at
https://example.com/rabbit/, request goes tohttps://example.com/rabbit/api/status - Proxy strips
/rabbit/and forwards to backend
JavaScript Config Method (For Complex Applications)
// 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
# 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/pagebecomes/pageat backend ProxyPassReverserewrites response headers (redirects, etc.)
API + Frontend Configuration
File: /etc/apache2/proxy-conf.d/service-with-api.conf
# 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 /statusto port 8765GET /rabbit/index.html→ matches second rule →GET /index.htmlto port 9273
Large File Upload Configuration
# 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
# 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
# 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
# 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
sudo systemctl reload apache2
# Verify Apache is still running
sudo systemctl status apache2
4. Test Public Access
# 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:
-
Verify service is accessible from rivsprod01:
ssh rivsprod01 "curl -s http://10.99.0.3:PORT/endpoint" -
Check Apache config order:
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
-
Check Apache error logs:
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:
# 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:
# 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:
# 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:
ssh rivsprod01 "sudo systemctl status apache2"
Solution:
- Check for typos in ProxyPass URLs
- Verify no missing quotes or slashes
- Look at error log:
sudo tail /var/log/apache2/error.log - 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
# 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.3or0.0.0.0to be accessible from rivsprod01 - Use
http://10.99.0.3:PORTin ProxyPass target URLs - Never use
localhostor127.0.0.1in 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
- Always use relative URLs in frontend code for proxy compatibility
- Test service accessibility from rivsprod01 before configuring proxy
- Put specific paths first in Apache config (API before frontend)
- Use trailing slashes on both ProxyPass source and target for path stripping
- Reload Apache after config changes:
sudo systemctl reload apache2 - Keep configs organized - one file per application or related service group
- Document your paths - add comments explaining what each ProxyPass does
- Test both frontend and API after deployment to verify routing
Repository
