Forward proxy acts on behalf of clients, masking their identity from servers.
Reverse proxy acts on behalf of servers, hiding backend topology from clients.
Client knows and configures forward proxy; reverse proxy is invisible to clients.
Misconfigured forward proxy can become an SSRF vector into private networks.
Reverse proxies enable load balancing, SSL termination, and DDoS absorption.
Rule: ask "whose side is this proxy on?" to tell them apart instantly.
Plain-English First
Imagine you work at a big company. When you want to visit a website, your request goes through a security guard at the front door — that guard is a forward proxy. It acts on YOUR behalf, hiding who you are from the outside world. Now flip it around: when millions of people visit Netflix, they don't talk directly to Netflix's servers. Instead, a receptionist intercepts all those calls and routes them smartly — that receptionist is a reverse proxy. It acts on behalf of the SERVER, hiding the server's identity from the outside world. Same idea, completely opposite direction.
Every major system you rely on daily — Google, Netflix, your company's internal tools — quietly runs traffic through a proxy of some kind. Yet most developers can't explain the difference between a forward proxy and a reverse proxy without getting tangled up. That confusion costs real money: misconfigured proxies cause security holes, poor load distribution, and debugging nightmares that take days to unravel.
The problem is that both tools have the word 'proxy' in the name, which implies they're variations of the same thing. They're not. They solve fundamentally different problems and sit on opposite sides of the network boundary. A forward proxy controls and masks outbound traffic from clients. A reverse proxy controls and masks inbound traffic to servers. Getting this wrong means you deploy the wrong tool, patch the wrong layer, or — worse — expose infrastructure you thought was hidden.
By the end of this article you'll be able to explain both proxy types clearly in a system design interview, sketch out a real architecture diagram showing where each one lives, configure a minimal working example of each using Nginx, and confidently decide which one a given system needs — and why.
What a Forward Proxy Actually Does (and Why Companies Love It)
A forward proxy sits between a group of clients — say, every laptop in a corporate office — and the open internet. When an employee's browser makes a request, it goes to the proxy first. The proxy then makes that request on the employee's behalf, receives the response, and hands it back.
The key word is 'behalf of the client.' The destination server never sees the real client IP. It only ever sees the proxy's IP address. This is the foundation of tools like VPNs, Tor exit nodes, and corporate content filters.
Why does this matter? Three big reasons. First, anonymity — you can mask the origin of requests, which matters for privacy, web scraping, or bypassing geo-restrictions. Second, access control — companies use forward proxies to block social media or gambling sites during work hours without touching individual machines. Third, caching — a forward proxy can cache responses for frequently visited sites, so if 200 employees all load the same news article, only one actual request hits the internet. The other 199 get a cached copy in milliseconds.
The critical insight: the CLIENT knows about the forward proxy and is configured to use it. That's what distinguishes it architecturally from a reverse proxy.
forward_proxy_nginx.confNGINX
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# ForwardProxyConfiguration using Nginx + ngx_http_proxy_connect_module
# This turns Nginx into a forward proxy that corporate clients route through.
# NOTE: StandardNginx doesn't support CONNECT (HTTPS tunneling) out of the box.
# You need the ngx_http_proxy_connect_module patch for full HTTPS support.
# This example shows HTTP forward proxying which works with vanilla Nginx.
server {
# The port corporate clients point their browser proxy settings at
listen 8888;
# resolver is required so Nginx can resolve the destination hostname dynamically
# 8.8.8.8 is Google's publicDNS — replace with your internal DNS in production
resolver 8.8.8.8;
location / {
# $http_host captures the destination host the client requested
# $request_uri captures the full path and query string
# Together they reconstruct the original target URL
proxy_pass http://$http_host$request_uri;
# Forward the real client IP in a custom header so destination servers
# can log who actually made the request (optional, reduces anonymity)
proxy_set_header X-Forwarded-For $remote_addr;
# Pass along the original Host header so the destination server
# knows which virtual host it's being asked for
proxy_set_header Host $http_host;
}
# Block access to internal/privateIP ranges — critical security rule.
# Withoutthis, an attacker could use YOUR proxy to attack your own internal network
# (known as Server-SideRequestForgery, SSRF)
location ~* ^http://(10\.|192\.168\.|172\.(1[6-9]|2[0-9]|3[01])\.) {
# Return403Forbiddenfor any request targeting privateIP space
return403"Access to private network ranges is blocked";
}
}
Output
# Test from a client machine configured to use this proxy:
# ^ real client IP ^ full URL shows this is a forward proxy request
# From example.com's perspective, the request came from the PROXY IP, not 192.168.1.45
# That's the anonymization happening in real time.
Watch Out: SSRF Risk
An open forward proxy with no IP filtering is an SSRF (Server-Side Request Forgery) disaster waiting to happen. An attacker can route requests through your proxy to hit internal services on your private network — your Redis instance, your admin panel, your cloud metadata endpoint (169.254.169.254). Always blocklist RFC 1918 private ranges as shown above.
Production Insight
Forward proxies are a favorite vector for SSRF because they blindly forward requests to any IP.
In production, always layer IP allowlisting and domain whitelisting.
Rule: if your forward proxy can reach internal subnets, an attacker can too.
Key Takeaway
Forward proxy = client side.
Client must configure it.
Security: block private IPs first.
What a Reverse Proxy Does — and Why It's the Backbone of Modern Web Architecture
A reverse proxy sits in front of your servers, not your clients. When a user types 'api.yourapp.com' into their browser, they're hitting the reverse proxy — and they have absolutely no idea. The proxy then decides which backend server should handle that request, forwards it, gets the response, and returns it to the user.
The client never knows the backend servers exist. They might think they're talking to one server. In reality, you could have 50 servers behind that proxy, dynamically scaling up and down.
This is why reverse proxies are the single most important infrastructure component in scalable web systems. They enable four things that are otherwise very hard to achieve: load balancing across multiple server instances, SSL termination (so your app servers never deal with encryption overhead), caching of static assets close to the edge, and centralized authentication and rate limiting.
Nginx, Caddy, HAProxy, AWS ALB, Cloudflare — these are all reverse proxies at their core. When engineers say 'put it behind Nginx', they mean 'add a reverse proxy in front of your application.' This is the default architecture for virtually every production web service today.
The critical insight: the CLIENT does not know about the reverse proxy. It's invisible. The client thinks it's talking directly to 'api.yourapp.com'.
reverse_proxy_nginx.confNGINX
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
# Production-style ReverseProxyConfiguration
# ThisNginx config sits in front of three application server instances
# and handles load balancing, SSL termination, and health checking.
# Define the pool of backend application servers
# Nginx will distribute requests across these using round-robin by default
upstream application_servers {
# Each'server' directive points to a running application instance
# These could be Node.js, Python/Gunicorn, Java/Spring — anything
server 10.0.1.10:3000 weight=3; # This server handles 3x the traffic (more powerful machine)
server 10.0.1.11:3000 weight=1; # Standard traffic share
server 10.0.1.12:3000 weight=1; # Standard traffic share
# Health check: if a server fails to respond twice in 30 seconds, stop sending it traffic
# Nginx will automatically recheck it every 30 seconds and re-add it when healthy
server 10.0.1.13:3000 backup; # Only used ifALL primary servers are down
# Keepalive maintains persistent connections to backends — reduces TCP handshake overhead
keepalive 32;
}
# Redirect all HTTP traffic to HTTPS — never serve your app unencrypted
server {
listen 80;
server_name api.yourapp.com;
return301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
server_name api.yourapp.com;
# SSL termination happens HERE at the proxy — backend servers talk plain HTTP internally
# This offloads crypto work from your app servers (significant CPU saving at scale)
ssl_certificate /etc/ssl/certs/yourapp.crt;
ssl_certificate_key /etc/ssl/private/yourapp.key;
ssl_protocols TLSv1.2TLSv1.3; # Never allow TLS1.0 or 1.1 — they're broken
# Security headers added here once, forALL backends — not in each app separately
add_header Strict-Transport-Security"max-age=31536000; includeSubDomains" always;
add_header X-Frame-OptionsDENY always;
add_header X-Content-Type-Options nosniff always;
# Rate limiting — block clients making more than 100 requests/second
# This protects ALL backend servers centrally without touching app code
limit_req zone=api_rate_limit burst=200 nodelay;
location /api/ {
# Hand the request off to our upstream pool
proxy_pass http://application_servers;
# Tell the backend the REAL client IP, not the proxy's IP
# Your app needs thisfor logging, analytics, and fraud detection
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme; # Tell backend this came in over HTTPS
# Pass the original hostname so the backend knows which virtual host was requested
proxy_set_header Host $host;
# Timeouts: don't let a slow backend server hang the proxy indefinitely
proxy_connect_timeout 5s; # Max time to establish connection to backend
proxy_read_timeout 30s; # Max time to wait for backend to send a response
proxy_send_timeout 10s; # Max time to send the request to backend
}
# Cachestatic assets at the proxy level — backends never even see these requests
location /static/ {
proxy_pass http://application_servers;
proxy_cache_valid 200 7d; # Cache successful responses for7 days
add_header X-Cache-Status $upstream_cache_status; # ShowsHIT or MISS in response
}
}
Output
# Test the reverse proxy from outside:
# curl -I https://api.yourapp.com/api/users
HTTP/2 200
server: nginx # Client sees Nginx, not your Node.js/Python app
x-real-ip: (stripped — not visible to client)
x-cache-status: HIT # Static asset served from cache, backend never touched
# Nginx access log shows which backend handled the request:
# [proxy] 203.0.113.42 -> 10.0.1.10:3000 GET /api/users 200 142ms
# [proxy] 203.0.113.99 -> 10.0.1.11:3000 GET /api/users 200 138ms <- round-robin in action
# [proxy] 203.0.113.77 -> 10.0.1.12:3000 GET /api/users 200 145ms
Pro Tip: X-Forwarded-For Is Not Optional
If you don't set X-Real-IP and X-Forwarded-For in your reverse proxy config, every request your application sees will appear to come from the proxy's internal IP (e.g. 127.0.0.1 or 10.0.0.1). Your analytics are meaningless, your rate limiting by IP breaks, and your fraud detection goes blind. Always pass the real client IP through.
Production Insight
Reverse proxy missing X-Forwarded-For is the #1 cause of broken rate limiting in production.
Without it, every request appears to come from the proxy's IP, making all clients look identical.
Rule: always configure header forwarding before deploying behind a reverse proxy.
Key Takeaway
Reverse proxy = server side.
Client is oblivious.
Always forward client IP — analytics depend on it.
How They Fit Into Real System Design — Side by Side
Here's where it clicks: in a sophisticated real-world architecture, you'll often have BOTH types of proxy at work simultaneously — serving completely different purposes.
Consider a company's internal developer tooling setup. Developers sit behind a corporate forward proxy that logs outbound traffic, blocks social media, and caches npm packages locally (so 200 developers all downloading React doesn't hammer npm's servers). They're using a forward proxy without thinking about it.
At the same time, the product those developers are building runs behind a reverse proxy (probably an AWS Application Load Balancer or Cloudflare). The reverse proxy handles SSL termination, routes '/api/' requests to their API servers and '/app/' requests to their frontend servers, and absorbs DDoS traffic before it touches any real infrastructure.
The mental model that makes this permanent: ask 'whose side is this proxy on?' A forward proxy is on the CLIENT's side — it represents and protects the clients. A reverse proxy is on the SERVER's side — it represents and protects the servers. Once that distinction is locked in, everything else (configuration, security implications, caching strategy) flows naturally from it.
This is also why CDNs like Cloudflare are technically reverse proxies. You configure your DNS to point at Cloudflare, and Cloudflare proxies all requests to your origin server. Your origin is hidden. Cloudflare is on your server's side.
docker_compose_full_proxy_architecture.ymlYAML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
# Full local demo: Forward proxy + Reverse proxy in the same DockerCompose setup
# Run: docker compose up
# This lets you see both proxy types operating simultaneously on your machine
version: '3.9'
services:
# ── REVERSEPROXY (public-facing) ──────────────────────────────────────────
# This is what the internet hits. It knows nothing about which backend answers.
reverse_proxy:
image: nginx:alpine
container_name: reverse_proxy
ports:
- "80:80" # Exposed to the outside world (simulates public internet)
volumes:
- ./reverse_proxy.conf:/etc/nginx/conf.d/default.conf:ro
networks:
- public_network # Faces the outside
- private_network # Can reach backend servers
depends_on:
- api_server_1
- api_server_2
# ── BACKENDAPISERVERS (hidden from public) ────────────────────────────────
# These servers are ONLY on the private network.
# There is no way to reach them except through the reverse proxy.
api_server_1:
image: node:18-alpine
container_name: api_server_1
working_dir: /app
volumes:
- ./api_server.js:/app/server.js
command: node server.js
environment:
SERVER_ID: "api-server-1" # So we can see which server handled the request
PORT: "3000"
networks:
- private_network # NOT on public_network — completely hidden
api_server_2:
image: node:18-alpine
container_name: api_server_2
working_dir: /app
volumes:
- ./api_server.js:/app/server.js
command: node server.js
environment:
SERVER_ID: "api-server-2"PORT: "3000"
networks:
- private_network # NOT on public_network — completely hidden
# ── FORWARDPROXY (internal clients use this to reach outside) ─────────────
# Internal services that need to make outbound HTTP calls route through this.
# It logs all outbound requests centrally and can block/allow by domain.
forward_proxy:
image: ubuntu/squid # Squid is the industry standard forward proxy daemon
container_name: forward_proxy
ports:
- "3128:3128" # StandardSquid port — clients set HTTP_PROXY=http://forward_proxy:3128
volumes:
- ./squid.conf:/etc/squid/squid.conf:ro
networks:
- private_network # Internal clients use this to reach the internet
- public_network # It needs internet access to fulfill those requests
# ── INTERNALCLIENT (simulates a service making outbound calls via forward proxy) ──
# This could be a data pipeline, a webhook sender, a third-party API caller etc.
internal_service:
image: curlimages/curl:latest
container_name: internal_service
# RouteALL outbound HTTP traffic through the forward proxy
environment:
HTTP_PROXY: "http://forward_proxy:3128"
HTTPS_PROXY: "http://forward_proxy:3128"
NO_PROXY: "localhost,127.0.0.1,reverse_proxy" # Don't proxy internal service-to-service calls
networks:
- private_network # Only on private network — can't reach internet directly
command: sh -c "sleep 5 && curl -s http://httpbin.org/ip" # Test: what IP does the internet see?
networks:
public_network: # Represents the internet-facing network
driver: bridge
private_network: # Represents your internal datacenter network
driver: bridge
internal: false # Set to true in production to truly block direct internet egress
Output
# docker compose up --abort-on-container-exit
reverse_proxy | Starting nginx
api_server_1 | Server api-server-1 listening on port 3000
api_server_2 | Server api-server-2 listening on port 3000
forward_proxy | Squid listening on port 3128
# From your host machine — hitting the REVERSE PROXY:
# internal_service container output — showing what IP the internet sees:
# (It sees the FORWARD PROXY's IP, not the internal_service container's IP)
{
"origin": "203.0.113.10" <- This is the forward proxy's public IP, not the internal service IP
}
# Try to reach api_server_1 directly from your host (should FAIL — it's hidden):
# curl http://localhost:3000/api/ping
curl: (7) Failed to connect to localhost port 3000: Connection refused
# Perfect. The backend is unreachable without going through the reverse proxy.
Interview Gold: The Network Boundary Question
Interviewers love asking 'where does each proxy sit relative to the trust boundary?' The forward proxy sits at the edge of the CLIENT network, managing outbound trust. The reverse proxy sits at the edge of the SERVER network, managing inbound trust. Being able to draw this boundary clearly on a whiteboard immediately signals senior-level system design thinking.
Production Insight
Running both proxies in the same network without careful routing can cause traffic loops.
For example, if internal services have HTTP_PROXY set to the forward proxy, make sure they don't route traffic intended for the reverse proxy through it.
Use NO_PROXY for internal domains like the reverse proxy's hostname.
Key Takeaway
Both proxies can coexist.
Forward proxy = outbound control.
Reverse proxy = inbound control.
Use NO_PROXY to avoid loops.
Key Configuration Differences That Trip Up Engineers
The most common configuration mistakes happen because engineers assume a reverse proxy config can be adapted for a forward proxy with minor changes. That's not how it works.
Where the proxy listens: - Forward proxy: listens on a port that clients explicitly connect to (e.g., 3128). Clients must set browser or OS proxy settings. - Reverse proxy: listens on standard ports (80/443) and appears as the destination server. Clients don't configure anything — DNS points to it.
How routing works: - Forward proxy: receives a full URL (e.g., GET http://example.com/path) and proxies to that URL. This is why forward proxy access logs show the full URL. - Reverse proxy: receives a relative path (e.g., GET /api/users) and forwards to a backend. It never sees the original request URL beyond the path.
SSL handling: - Forward proxy: can participate in SSL interception (MITM) for inspection, which requires installing a CA certificate on clients. This is optional and controversial. - Reverse proxy: typically terminates SSL, decrypts traffic, and forwards plain HTTP to internal backends. Backend servers never handle certificates.
Caching: - Forward proxy: caches responses for multiple clients accessing the same external resource. Cache key is the full URL. - Reverse proxy: caches responses from backend servers. Cache key is the request path and headers. Controls cache expiration for each backend route.
config_compare.confNGINX
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# Forward proxy config (simplified)
# Client sends: GET http://example.com/path HTTP/1.1
server {
listen 8888;
resolver 8.8.8.8;
location / {
proxy_pass http://$http_host$request_uri; # FullURL
proxy_set_header Host $http_host;
}
}
# Reverse proxy config (simplified)
# Client sends: GET /path HTTP/1.1 (Host: api.example.com)
upstream backend {
server 10.0.1.10:3000;
}
server {
listen 443 ssl;
server_name api.example.com;
location / {
proxy_pass http://backend; # Just the upstream
proxy_set_header Host $host; # Original host header
}
}
The Reverse Proxy Mental Model
The main number (proxy IP) is the only number you know.
You never get the direct extension (backend server IP).
The receptionist can handle many calls at once (load balancing).
If the person is busy, the receptionist takes a message (caching).
The receptionist checks your ID before connecting (authentication/rate limiting).
Production Insight
Mixing up the proxy_pass directive is a classic rookie mistake.
Misconfiguring these causes 404s or requests to wrong destinations.
Key Takeaway
Routing differs entirely.
Forward proxy: full URL → dynamic destination.
Reverse proxy: relative path → static backend pool.
Security Implications: What Breaks When You Get the Proxy Type Wrong
Choosing the wrong proxy type for a use case isn't just an architectural mistake — it's often a security incident waiting to happen.
Forward proxy security pitfalls: - SSRF: as covered, an open forward proxy can be used to attack internal networks. Always restrict by source IP and destination. - SSL inspection: if you're using a forward proxy for HTTPS interception, you must manage CA certificates on all clients. A leaked certificate compromises all traffic. - Logging: forward proxies log all outbound traffic. If logs are not secured, sensitive URLs and data can leak.
Reverse proxy security benefits: - DDoS absorption: the reverse proxy absorbs malicious traffic before it reaches origin servers. Cloudflare and AWS Shield do this at scale. - WAF integration: a reverse proxy is the natural place to run a Web Application Firewall (ModSecurity, AWS WAF). - IP hiding: attackers cannot directly target origin servers because they only see the proxy. - Centralized SSL: you manage one certificate on the proxy instead of dozens on backend servers.
Common mistake: using a forward proxy as a reverse proxy (or vice versa). For example, putting a forward proxy like Squid in front of a web server to 'protect' it. Squid doesn't do load balancing or health checks natively — it will fail under traffic spikes. Conversely, using Nginx as a forward proxy for HTTPS traffic requires compile-time modules and careful configuration; it's not designed for it.
Production Insight
A forward proxy deployed to 'speed up' backend responses (caching) will fail to cache dynamic content and may introduce stale data.
Reverse proxies are built for server-side caching with invalidation controls.
Rule: if you're caching server responses, you need a reverse proxy, not a forward one.
Key Takeaway
Security posture differs:
Forward proxy protects clients.
Reverse proxy protects servers.
Mismatch creates gaps attackers exploit.
● Production incidentPOST-MORTEMseverity: high
Open Forward Proxy Used to Exfiltrate AWS Metadata
Symptom
A security team noticed unusual outbound traffic from a staging server to 169.254.169.254, the AWS metadata endpoint. The traffic originated from an IP external to the VPC.
Assumption
The forward proxy was intended only for internal developers to access external APIs. No one considered that an attacker outside the network could route requests through it.
Root cause
The forward proxy (Squid) was deployed without IP allowlisting or blocking of private RFC 1918 ranges. It listened on a public IP and allowed any source to send CONNECT requests. An attacker scanned the internet, found the open proxy, and issued an HTTP request targeting the AWS metadata endpoint. The proxy dutifully forwarded it to the internal network, and the metadata service returned the instance's IAM credentials.
Fix
1. Immediately block all requests to 169.254.169.254 and private IP ranges in the proxy config using ACL rules. 2. Restrict proxy access to known IP ranges using acl allowed_clients src in Squid. 3. Rotate all IAM credentials exposed during the incident. 4. Add monitoring for any proxy access to metadata endpoints.
Key lesson
Never expose a forward proxy to the public internet without strict IP allowlisting.
Always block private IP ranges (RFC 1918) and the AWS metadata endpoint (169.254.169.254).
An open proxy is not just a performance risk — it's a direct path to internal infrastructure.
Production debug guideCommon symptoms and actions for both forward and reverse proxy problems.4 entries
Symptom · 01
Client cannot reach external websites through forward proxy.
→
Fix
Check proxy configuration in browser or env vars (HTTP_PROXY). Verify proxy server is running and port is reachable (telnet proxy_ip 3128). Look for DNS resolution errors in proxy logs.
Symptom · 02
All backend application logs show the same IP (load balancer internal IP).
→
Fix
Reverse proxy is not forwarding X-Forwarded-For. Add proxy_set_header X-Real-IP $remote_addr; and proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; to nginx location block.
Symptom · 03
Reverse proxy returns 502 Bad Gateway.
→
Fix
Backend servers are unreachable. Check proxy_pass URL, backend service health, firewall rules between proxy and backends, and timeouts.
Symptom · 04
HTTPS requests through forward proxy fail with SSL errors.
→
Fix
Forward proxy may not support CONNECT method for SSL tunneling. Ensure you are using a proxy (like Squid) that supports HTTPS proxying, or configure the client to use HTTP CONNECT.
★ Quick Proxy Debugging Cheat SheetFive-minute diagnostics for the most common production proxy failures.
Clients can't reach internet through forward proxy−
Immediate action
Check if proxy port is open: nc -zv proxy_ip 3128
Commands
curl -x http://proxy:3128 http://example.com -v
tail -f /var/log/squid/access.log
Fix now
If access log shows TCP_DENIED, add client IP to allowed ACL in squid.conf.
Backend logs show load balancer IP only+
Immediate action
Check if X-Forwarded-For is present in request headers
Hides server IPs, centralizes DDoS protection and WAF
SSL handling
Intercepts and inspects HTTPS (SSL inspection/MITM)
Terminates SSL — backend servers use plain HTTP internally
Typical config location
Set in browser/OS network settings or via env vars
Set in DNS — your domain points to the proxy, not origin
Real-world analogy
A company's security guard checking employees leaving
A hotel receptionist routing guest calls to the right room
Key takeaways
1
Direction defines everything
forward proxy = outbound (client→internet), reverse proxy = inbound (internet→server). When in doubt, ask 'whose side is this proxy on?'
2
Client awareness is the architectural tell
if the client is configured to use it (browser proxy settings, HTTP_PROXY env var), it's a forward proxy. If the client thinks it's talking directly to the destination, it's a reverse proxy.
3
Reverse proxies are the foundation of scalable web architecture
SSL termination, load balancing, DDoS absorption, and centralized auth all happen here before requests ever touch your app servers.
4
An open, unauthenticated forward proxy is an immediate security incident waiting to happen
always restrict by IP allowlist and always block RFC 1918 private ranges to prevent SSRF attacks against your own infrastructure.
5
Never mix up the routing
forward proxies proxy the full URL, reverse proxies proxy a relative path to a fixed backend pool.
Common mistakes to avoid
4 patterns
×
Confusing a VPN with a Forward Proxy
Symptom
Developers say 'just use a VPN' when a forward proxy is needed, then wonder why they can't do per-domain filtering or caching.
Fix
Understand that a VPN encrypts ALL traffic at the OS level and tunnels it to a remote network (changing your effective location), while a forward proxy operates at the HTTP/HTTPS application layer and can selectively proxy, filter, cache, and inspect individual requests. For corporate content filtering and request caching, use a forward proxy like Squid, not a VPN.
×
Not passing X-Forwarded-For in the reverse proxy config
Symptom
Every request in your application logs shows the same IP address (127.0.0.1 or your load balancer's internal IP). Rate limiting by IP fails. Geo-location is broken. Analytics are meaningless.
Fix
Add 'proxy_set_header X-Real-IP $remote_addr;' and 'proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;' to your Nginx location block. Then configure your application to read the client IP from the X-Real-IP or X-Forwarded-For header. In Express.js: app.set('trust proxy', 1). In Django: use REMOTE_ADDR after configuring TRUSTED_PROXIES.
×
Running an open forward proxy with no access controls
Symptom
Your proxy server appears in public proxy lists within hours of launch. Your IP gets blacklisted by major services. Bandwidth costs explode. Attackers use it to hit your own internal services (SSRF).
Fix
Never expose a forward proxy to the public internet without authentication. Use IP allowlisting to restrict which clients can use it, add HTTP Basic Auth, and always blocklist private IP ranges (10.x.x.x, 192.168.x.x, 172.16-31.x.x, 127.x.x.x, 169.254.x.x) so the proxy cannot be weaponized to target your own internal network.
×
Using a forward proxy to handle inbound traffic (i.e., as a reverse proxy)
Symptom
Traffic spikes cause the proxy to crash or become slow because forward proxies (Squid, etc.) lack load balancing and health checks. Requests are not distributed across backend servers.
Fix
Use a dedicated reverse proxy (Nginx, HAProxy, AWS ALB) for inbound traffic. Forward proxies are designed for outbound client requests — they don't handle server-side routing, session persistence, or health monitoring.
INTERVIEW PREP · PRACTICE MODE
Interview Questions on This Topic
Q01SENIOR
A client's browser makes a request to api.stripe.com. Describe exactly w...
Q02SENIOR
Cloudflare sits in front of millions of websites and users configure it ...
Q03SENIOR
Your application logs show every single incoming request has the IP addr...
Q01 of 03SENIOR
A client's browser makes a request to api.stripe.com. Describe exactly where a forward proxy and a reverse proxy would each sit in that network path, and what each one would do to the request.
ANSWER
A forward proxy sits on the client side: the browser's outbound request to api.stripe.com first goes to the forward proxy (configured in OS/browser proxy settings). The proxy then makes the request to Stripe, Stripe sees the proxy's IP, and the proxy returns the response to the client. A reverse proxy sits on Stripe's side: the DNS for api.stripe.com resolves to the reverse proxy's IP (e.g., Cloudflare or AWS ALB). The client's request hits the proxy, which inspects headers, applies rate limits, terminates SSL, then forwards to an internal backend server. The client has no idea it talked to a proxy — it thinks it's talking directly to Stripe.
Q02 of 03SENIOR
Cloudflare sits in front of millions of websites and users configure it by changing their DNS records. Is Cloudflare a forward proxy or a reverse proxy? Explain your reasoning — and then describe a situation where an organization might run BOTH types of proxy simultaneously.
ANSWER
Cloudflare is a reverse proxy. Users configure their DNS to point to Cloudflare's IPs, and Cloudflare receives inbound traffic on behalf of their origin servers. The client is not configured to use Cloudflare — it just resolves the domain to Cloudflare's IP. Cloudflare acts on the server's side. Both types simultaneously: a corporate office uses a forward proxy (e.g., Squid) to control and log outbound internet access for employees. At the same time, the company's public-facing website is protected by a reverse proxy (Cloudflare). The forward proxy hides employee IPs from the outside, the reverse proxy hides the origin server IPs from the public.
Q03 of 03SENIOR
Your application logs show every single incoming request has the IP address 10.0.0.1 — your load balancer's private IP. Users are complaining that rate limiting is broken because everyone shares the same 'identity'. Walk me through exactly what configuration changes are needed at the proxy layer and the application layer to fix this.
ANSWER
At the proxy layer (assuming Nginx): add proxy_set_header X-Real-IP $remote_addr; and proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; to the location block. The X-Real-IP header will contain the actual client IP. At the application layer: configure your web framework to trust these headers. For example, in Express.js: app.set('trust proxy', 1). This tells Express to read the client IP from X-Forwarded-For instead of the socket's remote address. In Django: set USE_X_FORWARDED_HOST = True and configure REMOTE_ADDR from the header. Then update your rate limiting logic to extract the IP from the header or use middleware that automatically handles proxy headers.
01
A client's browser makes a request to api.stripe.com. Describe exactly where a forward proxy and a reverse proxy would each sit in that network path, and what each one would do to the request.
SENIOR
02
Cloudflare sits in front of millions of websites and users configure it by changing their DNS records. Is Cloudflare a forward proxy or a reverse proxy? Explain your reasoning — and then describe a situation where an organization might run BOTH types of proxy simultaneously.
SENIOR
03
Your application logs show every single incoming request has the IP address 10.0.0.1 — your load balancer's private IP. Users are complaining that rate limiting is broken because everyone shares the same 'identity'. Walk me through exactly what configuration changes are needed at the proxy layer and the application layer to fix this.
SENIOR
FAQ · 4 QUESTIONS
Frequently Asked Questions
01
Is Nginx a forward proxy or a reverse proxy?
Nginx is primarily designed and used as a reverse proxy — it sits in front of your backend servers and routes inbound traffic to them. It can be configured as a basic forward proxy for HTTP traffic, but it requires a third-party module (ngx_http_proxy_connect_module) to handle HTTPS CONNECT tunneling properly. For production forward proxy setups, Squid is a more purpose-built choice.
Was this helpful?
02
Does a VPN count as a forward proxy?
They overlap in purpose — both can mask your IP and route traffic through an intermediary — but they operate at different layers. A VPN works at the network (OS) level, encrypting all traffic and routing it through a remote server. A forward proxy works at the HTTP application level, handling requests selectively by domain or URL. A VPN gives you a new apparent location; a forward proxy gives you control over individual HTTP requests including filtering, caching, and inspection.
Was this helpful?
03
Can a server tell if a request came through a reverse proxy?
Not without deliberate cooperation from the proxy. A reverse proxy completely hides the server topology — the client has no way to discover the real backend server IPs unless the proxy leaks them. However, the reverse proxy can also hide the real CLIENT IP from the backend server, unless it's configured to pass it through X-Forwarded-For or X-Real-IP headers. This is why configuring those headers correctly is non-negotiable in any production reverse proxy setup.
Was this helpful?
04
What is the difference between a transparent proxy and a forward proxy?
A transparent proxy intercepts traffic without client configuration — typically deployed at the network gateway level (e.g., router). The client doesn't know it exists. A forward proxy requires client configuration (browser settings or HTTP_PROXY env var). The distinction is important: transparent proxies are often used for content filtering in schools or public Wi-Fi, while forward proxies are used within corporate networks where explicit client configuration is acceptable.