Profile    Mohammed Shiroz Status   Loading  
Logo
Share This
Back to blog
Filter by:
Tags
//Article title

What Is a Reverse Proxy? The Server in Front of Your Server

About Post

You deploy behind a load balancer and two strange things happen. Every visitor in your logs now has the same IP address, something like 10.0.0.12. And Laravel starts generating http:// links on a site that is definitely served over HTTPS, so the browser complains about mixed content and the login redirect goes in circles.

Nothing is wrong with your code. Your app just doesn't know that there's now another server standing between it and the world. That server is a reverse proxy, and once you understand what it does, both bugs take one line to fix.

What's the difference between a proxy and a reverse proxy?

Both sit in the middle of a conversation. The difference is whose side they're on.

  • A forward proxy works for the client. A company network might send all employee traffic through one, to filter sites or cache downloads. The websites see the proxy, not the individual laptops.
  • A reverse proxy works for the server. Visitors think they're talking to your site, but they're talking to the proxy, which passes requests to your app servers behind it.

Think of a hotel reception desk. Guests never walk into the kitchen or the laundry room. They talk to reception, and reception gets the right department to deal with it. The guests don't know (or need to know) how many people work in the back.

Nginx, HAProxy, Caddy and Traefik are common reverse proxies. So are AWS Application Load Balancers and CDNs like Cloudflare. You may have several in a row.

What does a reverse proxy actually do?

TLS termination

The proxy holds the certificate and handles HTTPS. Behind it, traffic to your app may be plain HTTP on a private network. One place to manage certificates, and your app servers don't spend effort on encryption.

Load balancing

With three app servers, the proxy spreads requests between them and stops sending traffic to a server that fails its health check. Deploys can take servers out one at a time without downtime.

Caching and compression

The proxy can serve static files and cached responses itself and compress responses, so many requests never reach PHP at all.

Routing

One domain, several services: /api goes to a Laravel app, /realtime to a Node.js WebSocket server, everything else to a frontend. Users see a single site.

Protection

Rate limiting, request size limits, blocking bad bots and hiding your real servers from direct access all happen at the front door, before a request costs you a database query.

What does it look like?

A minimal Nginx reverse proxy in front of an app listening on port 8000 (simplified, certificate lines left out):

server {
    listen 443 ssl;
    server_name app.example.com;

    location / {
        proxy_pass http://127.0.0.1:8000;
        proxy_set_header Host $host;
        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;
    }
}

Those proxy_set_header lines are the key to the two bugs from the start.

Why does Laravel see the wrong IP and the wrong scheme?

From your app's point of view, every request now comes from the proxy. The TCP connection really does start at 10.0.0.12, and it really is plain HTTP. So $request->ip() returns the proxy's address, and url() builds http:// links.

The proxy passes the original details along in headers:

  • X-Forwarded-For: the original client IP, plus any proxies along the way.
  • X-Forwarded-Proto: whether the visitor used https.
  • X-Forwarded-Host and X-Forwarded-Port: the host and port the visitor used.

But Laravel won't believe those headers by default, and that's a good thing. Anyone can send a request with X-Forwarded-For: 1.2.3.4. If your app trusted it blindly, an attacker could fake their IP to dodge rate limits or poison your audit logs.

How do I make Laravel trust the proxy safely?

You tell Laravel which proxies are allowed to set those headers. In Laravel 12 that lives in bootstrap/app.php:

->withMiddleware(function (Middleware $middleware): void {
    $middleware->trustProxies(at: [
        '10.0.0.0/8',   // your load balancer's private network
    ]);
})

Now, when a request arrives from an address in that range, Laravel reads the forwarded headers. $request->ip() returns the real visitor, $request->secure() is true, and generated URLs use https.

You'll often see trustProxies(at: '*'). It's common with cloud load balancers whose IP addresses change, and it's acceptable only if your app servers can't be reached directly from the internet, for example because a security group only allows traffic from the load balancer. If someone can bypass the proxy and hit your server, '*' lets them write their own IP address.

The rule: trust forwarded headers only from proxies you control, and make sure nothing else can reach your app servers. Trusting '*' on a server that's publicly reachable is trusting every stranger's word about who they are.

Anything else that bites in production?

  • Several layers. CDN in front of a load balancer in front of Nginx means a chain of IPs in X-Forwarded-For. Each layer needs to be trusted for the real client IP to come through.
  • CDN-specific headers. Some CDNs send their own header with the visitor's IP (Cloudflare uses CF-Connecting-IP). Know which one your setup relies on.
  • Timeouts. The proxy has its own timeout. A long report that PHP is happy to run for two minutes may be cut off by the proxy much earlier, returning a 504 that never appears in your Laravel logs. Long work belongs in a queue anyway.
  • Upload limits. Nginx's client_max_body_size can reject a large upload before PHP ever sees it, with a 413 error. Raising upload_max_filesize in PHP won't help on its own.
  • Health checks. Give the load balancer a lightweight endpoint (Laravel 12 ships with /up) so it knows when a server is really ready.

The short version

  • A reverse proxy is the front door to your servers: TLS, load balancing, caching, routing and protection.
  • Behind it, your app sees the proxy's IP and plain HTTP unless it trusts the forwarded headers.
  • Trust only proxies you control, and keep app servers unreachable from anywhere else.
  • When something fails with no trace in your app logs, check the proxy's logs and limits.

How many proxies sit in front of your main app right now? It's often one more than people think.

Comments (0)
Leave your review

Thanks for your valuable comments. Your comments has been updated and appreciate your getting in touch...

01. About Shiroz

Mohammed Shiroz

Hi, I'm Mohammed Shiroz, a software engineer and AI enthusiast from Sri Lanka who turns ideas into intelligent, real-world solutions. With over 9 years of hands-on experience, I currently lead real estate ERP development at Kate Group, a...

03.My Projects

04. Categories

Ready To order Your Project ?

Get in Touch
Close